@@ -385,119 +385,161 @@ ip_representation::ip_representation(const struct sockaddr* ip) {
385385 mask = constants::DEFAULT_MASK_VALUE ;
386386}
387387
388- ip_representation::ip_representation (const std::string& ip) {
389- std::vector<std::string> parts;
390- mask = constants::DEFAULT_MASK_VALUE ;
391- std::fill (pieces, pieces + 16 , 0 );
392- if (ip.find (' :' ) != std::string::npos) { // IPV6
393- ip_version = http_utils::IPV6 ;
394- parts = string_utilities::string_split (ip, ' :' , false );
395- if (parts.size () > 8 ) {
396- throw std::invalid_argument (" IP is badly formatted. Max 8 parts in IPV6." );
397- }
398-
399- unsigned int omitted = 8 - (parts.size () - 1 );
400- if (omitted != 0 ) {
401- int empty_count = 0 ;
402- for (unsigned int i = 0 ; i < parts.size (); i++) {
403- if (parts[i].size () == 0 ) empty_count++;
404- }
388+ namespace {
389+
390+ // "::ffff:1.2.3.4" / "::1.2.3.4" prefix invariants: bytes 10-11 must
391+ // be either 0x00 (pure ::-mapped) or 0xFF (v4-mapped). Lifted out of
392+ // parse_nested_ipv4 so the surrounding function stays below the CCN
393+ // bar.
394+ bool ipv4_mapped_prefix_invalid (uint16_t a, uint16_t b) {
395+ return (a != 0 && a != 255 ) || (b != 0 && b != 255 );
396+ }
405397
406- if (empty_count > 1 ) {
407- if (parts[parts.size () - 1 ].find (" ." ) != std::string::npos) omitted -= 1 ;
398+ } // namespace
408399
409- if (empty_count == 2 && parts[0 ] == " " && parts[1 ] == " " ) {
410- omitted += 1 ;
411- parts = std::vector<std::string>(parts.begin () + 1 , parts.end ());
412- } else {
413- throw std::invalid_argument (" IP is badly formatted. Cannot have more than one omitted segment in IPV6." );
414- }
415- }
400+ void ip_representation::parse_ipv4 (const std::string& ip) {
401+ ip_version = http_utils::IPV4 ;
402+ auto parts = string_utilities::string_split (ip, ' .' );
403+ if (parts.size () != 4 ) {
404+ throw std::invalid_argument (" IP is badly formatted. Max 4 parts in IPV4." );
405+ }
406+ for (unsigned int i = 0 ; i < parts.size (); i++) {
407+ if (parts[i] == " *" ) {
408+ CLEAR_BIT (mask, 12 +i);
409+ continue ;
416410 }
411+ pieces[12 +i] = strtol (parts[i].c_str (), nullptr , 10 );
412+ if (pieces[12 +i] > 255 ) {
413+ throw std::invalid_argument (" IP is badly formatted. 255 is max value for ip part." );
414+ }
415+ }
416+ }
417417
418- int y = 0 ;
419- for (unsigned int i = 0 ; i < parts.size (); i++) {
420- if (parts[i] != " *" ) {
421- if (parts[i].size () == 0 ) {
422- for (unsigned int omitted_idx = 0 ; omitted_idx < omitted; omitted_idx++) {
423- pieces[y] = 0 ;
424- pieces[y+1 ] = 0 ;
425- y += 2 ;
426- }
427-
428- continue ;
429- }
418+ unsigned int ip_representation::compute_ipv6_omitted_segments (std::vector<std::string>& parts) {
419+ unsigned int omitted = 8 - (parts.size () - 1 );
420+ if (omitted == 0 ) return 0 ;
430421
431- if (parts[i].size () < 4 ) {
432- std::stringstream ss;
433- ss << std::setfill (' 0' ) << std::setw (4 ) << parts[i];
434- parts[i] = ss.str ();
435- }
422+ int empty_count = 0 ;
423+ for (unsigned int i = 0 ; i < parts.size (); i++) {
424+ if (parts[i].size () == 0 ) empty_count++;
425+ }
426+ if (empty_count <= 1 ) return omitted;
427+
428+ // > 1 empty segments: the only legal shape is a leading "::" (which
429+ // string_split produces as two consecutive empties) on an IPv6 that
430+ // also has a nested IPv4 trailing dotted-quad — the "::" produces
431+ // one extra empty segment beyond the canonical single placeholder.
432+ if (parts.back ().find (' .' ) != std::string::npos) omitted -= 1 ;
433+
434+ const bool leading_double_colon =
435+ empty_count == 2 && parts[0 ].empty () && parts[1 ].empty ();
436+ if (!leading_double_colon) {
437+ throw std::invalid_argument (
438+ " IP is badly formatted. Cannot have more than one omitted segment in IPV6." );
439+ }
440+ omitted += 1 ;
441+ parts.erase (parts.begin ());
442+ return omitted;
443+ }
436444
437- if (parts[i].size () == 4 ) {
438- pieces[y] = strtol ((parts[i].substr (0 , 2 )).c_str (), nullptr , 16 );
439- pieces[y+1 ] = strtol ((parts[i].substr (2 , 2 )).c_str (), nullptr , 16 );
440-
441- y += 2 ;
442- } else {
443- if (parts[i].find (' .' ) != std::string::npos) {
444- if (y != 12 ) {
445- throw std::invalid_argument (" IP is badly formatted. Missing parts before nested IPV4." );
446- }
447-
448- if (i != parts.size () - 1 ) {
449- throw std::invalid_argument (" IP is badly formatted. Nested IPV4 should be at the end" );
450- }
451-
452- std::vector<std::string> subparts = string_utilities::string_split (parts[i], ' .' );
453- if (subparts.size () == 4 ) {
454- for (unsigned int k = 0 ; k < 10 ; k++) {
455- if (pieces[k] != 0 ) throw std::invalid_argument (" IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)" );
456- }
457-
458- if ((pieces[10 ] != 0 && pieces[10 ] != 255 ) || (pieces[11 ] != 0 && pieces[11 ] != 255 )) {
459- throw std::invalid_argument (" IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)" );
460- }
461-
462- for (unsigned int ii = 0 ; ii < subparts.size (); ii++) {
463- if (subparts[ii] != " *" ) {
464- pieces[y+ii] = strtol (subparts[ii].c_str (), nullptr , 10 );
465- if (pieces[y+ii] > 255 ) throw std::invalid_argument (" IP is badly formatted. 255 is max value for ip part." );
466- } else {
467- CLEAR_BIT (mask, y+ii);
468- }
469- }
470- } else {
471- throw std::invalid_argument (" IP is badly formatted. Nested IPV4 can have max 4 parts." );
472- }
473- } else {
474- throw std::invalid_argument (" IP is badly formatted. IPV6 parts can have max 4 characters (or nest an IPV4)" );
475- }
476- }
477- } else {
478- CLEAR_BIT (mask, y);
479- CLEAR_BIT (mask, y+1 );
480- y+=2 ;
481- }
445+ void ip_representation::parse_nested_ipv4 (const std::vector<std::string>& parts,
446+ unsigned int i, int y) {
447+ if (y != 12 ) {
448+ throw std::invalid_argument (" IP is badly formatted. Missing parts before nested IPV4." );
449+ }
450+ if (i != parts.size () - 1 ) {
451+ throw std::invalid_argument (" IP is badly formatted. Nested IPV4 should be at the end" );
452+ }
453+ auto subparts = string_utilities::string_split (parts[i], ' .' );
454+ if (subparts.size () != 4 ) {
455+ throw std::invalid_argument (" IP is badly formatted. Nested IPV4 can have max 4 parts." );
456+ }
457+ // Bytes 0-9 must be zero; bytes 10-11 must be 0x00 or 0xFF.
458+ for (unsigned int k = 0 ; k < 10 ; k++) {
459+ if (pieces[k] != 0 ) {
460+ throw std::invalid_argument (
461+ " IP is badly formatted. Nested IPV4 can be preceded only by 0 "
462+ " (and, optionally, two 255 octects)" );
482463 }
483- } else { // IPV4
484- ip_version = http_utils:: IPV4 ;
485- parts = string_utilities::string_split (ip, ' . ' );
486- if (parts. size () == 4 ) {
487- for ( unsigned int i = 0 ; i < parts. size (); i++) {
488- if (parts[i] != " * " ) {
489- pieces[ 12 +i] = strtol (parts[i]. c_str (), nullptr , 10 );
490- if (pieces[ 12 +i] > 255 ) throw std::invalid_argument ( " IP is badly formatted. 255 is max value for ip part. " );
491- } else {
492- CLEAR_BIT (mask, 12 +i) ;
493- }
494- }
495- } else {
496- throw std::invalid_argument (" IP is badly formatted. Max 4 parts in IPV4 ." );
464+ }
465+ if ( ipv4_mapped_prefix_invalid (pieces[ 10 ], pieces[ 11 ])) {
466+ throw std::invalid_argument (
467+ " IP is badly formatted. Nested IPV4 can be preceded only by 0 "
468+ " (and, optionally, two 255 octects) " );
469+ }
470+ for ( unsigned int ii = 0 ; ii < subparts. size (); ii++) {
471+ if (subparts[ii] == " * " ) {
472+ CLEAR_BIT (mask, y+ii);
473+ continue ;
474+ }
475+ pieces[y+ii] = strtol (subparts[ii]. c_str (), nullptr , 10 );
476+ if (pieces[y+ii] > 255 ) {
477+ throw std::invalid_argument (" IP is badly formatted. 255 is max value for ip part ." );
497478 }
498479 }
499480}
500481
482+ void ip_representation::apply_ipv6_part (std::vector<std::string>& parts, unsigned int i,
483+ int & y, unsigned int omitted) {
484+ auto & part = parts[i];
485+ if (part == " *" ) {
486+ CLEAR_BIT (mask, y);
487+ CLEAR_BIT (mask, y+1 );
488+ y += 2 ;
489+ return ;
490+ }
491+ if (part.empty ()) {
492+ // Placeholder for one or more omitted segments. Zero-fill the
493+ // implied slots; the bump is `omitted` segments x 2 bytes each.
494+ for (unsigned int o = 0 ; o < omitted; o++) {
495+ pieces[y] = 0 ;
496+ pieces[y+1 ] = 0 ;
497+ y += 2 ;
498+ }
499+ return ;
500+ }
501+ if (part.size () < 4 ) {
502+ // Pad short hex group to 4 chars (e.g. "f" -> "000f").
503+ std::stringstream ss;
504+ ss << std::setfill (' 0' ) << std::setw (4 ) << part;
505+ part = ss.str ();
506+ }
507+ if (part.size () == 4 ) {
508+ pieces[y] = strtol (part.substr (0 , 2 ).c_str (), nullptr , 16 );
509+ pieces[y+1 ] = strtol (part.substr (2 , 2 ).c_str (), nullptr , 16 );
510+ y += 2 ;
511+ return ;
512+ }
513+ if (part.find (' .' ) == std::string::npos) {
514+ throw std::invalid_argument (
515+ " IP is badly formatted. IPV6 parts can have max 4 characters (or nest an IPV4)" );
516+ }
517+ parse_nested_ipv4 (parts, i, y);
518+ }
519+
520+ void ip_representation::parse_ipv6 (const std::string& ip) {
521+ ip_version = http_utils::IPV6 ;
522+ auto parts = string_utilities::string_split (ip, ' :' , false );
523+ if (parts.size () > 8 ) {
524+ throw std::invalid_argument (" IP is badly formatted. Max 8 parts in IPV6." );
525+ }
526+ unsigned int omitted = compute_ipv6_omitted_segments (parts);
527+ int y = 0 ;
528+ for (unsigned int i = 0 ; i < parts.size (); i++) {
529+ apply_ipv6_part (parts, i, y, omitted);
530+ }
531+ }
532+
533+ ip_representation::ip_representation (const std::string& ip) {
534+ mask = constants::DEFAULT_MASK_VALUE ;
535+ std::fill (pieces, pieces + 16 , 0 );
536+ if (ip.find (' :' ) != std::string::npos) {
537+ parse_ipv6 (ip);
538+ } else {
539+ parse_ipv4 (ip);
540+ }
541+ }
542+
501543bool ip_representation::operator <(const ip_representation& b) const {
502544 int64_t this_score = 0 ;
503545 int64_t b_score = 0 ;
0 commit comments