// // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/url // #ifndef BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP #define BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP #include #include #include #include #include #include #include namespace boost { namespace urls { namespace detail { // return `true` if the hex // word could be 0..255 if // interpreted as decimal static bool maybe_octet( unsigned char const* p) noexcept { unsigned short word = static_cast( p[0]) * 256 + static_cast( p[1]); if(word > 0x255) return false; if(((word >> 4) & 0xf) > 9) return false; if((word & 0xf) > 9) return false; return true; } } // detail auto ipv6_address_rule_t:: parse( char const*& it, char const* const end ) const noexcept -> result { int n = 8; // words needed int b = -1; // value of n // when '::' seen bool c = false; // need colon auto prev = it; ipv6_address::bytes_type bytes; result rv; for(;;) { if(it == end) { if(b != -1) { // end in "::" break; } BOOST_ASSERT(n > 0); // not enough words BOOST_URL_RETURN_EC( grammar::error::invalid); } if(*it == ':') { ++it; if(it == end) { // expected ':' BOOST_URL_RETURN_EC( grammar::error::invalid); } if(*it == ':') { if(b == -1) { // first "::" ++it; --n; b = n; if(n == 0) break; c = false; continue; } // extra "::" found BOOST_URL_RETURN_EC( grammar::error::invalid); } if(c) { prev = it; rv = grammar::parse( it, end, detail::h16_rule); if(! rv) return rv.error(); bytes[2*(8-n)+0] = rv->hi; bytes[2*(8-n)+1] = rv->lo; --n; if(n == 0) break; continue; } // expected h16 BOOST_URL_RETURN_EC( grammar::error::invalid); } if(*it == '.') { if(b == -1 && n > 1) { // not enough h16 BOOST_URL_RETURN_EC( grammar::error::invalid); } if(! detail::maybe_octet( &bytes[2*(7-n)])) { // invalid octet BOOST_URL_RETURN_EC( grammar::error::invalid); } // rewind the h16 and // parse it as ipv4 it = prev; auto rv1 = grammar::parse( it, end, ipv4_address_rule); if(! rv1) return rv1.error(); auto v4 = *rv1; auto const b4 = v4.to_bytes(); bytes[2*(7-n)+0] = b4[0]; bytes[2*(7-n)+1] = b4[1]; bytes[2*(7-n)+2] = b4[2]; bytes[2*(7-n)+3] = b4[3]; --n; break; } auto d = grammar::hexdig_value(*it); if( b != -1 && d < 0) { // ends in "::" break; } if(! c) { prev = it; rv = grammar::parse( it, end, detail::h16_rule); if(! rv) return rv.error(); bytes[2*(8-n)+0] = rv->hi; bytes[2*(8-n)+1] = rv->lo; --n; if(n == 0) break; c = true; continue; } // ':' divides a word BOOST_URL_RETURN_EC( grammar::error::invalid); } if(b == -1) return ipv6_address{bytes}; if(b == n) { // "::" last auto const i = 2 * (7 - n); std::memset( &bytes[i], 0, 16 - i); } else if(b == 7) { // "::" first auto const i = 2 * (b - n); std::memmove( &bytes[16 - i], &bytes[2], i); std::memset( &bytes[0], 0, 16 - i); } else { // "::" in middle auto const i0 = 2 * (7 - b); auto const i1 = 2 * (b - n); std::memmove( &bytes[16 - i1], &bytes[i0 + 2], i1); std::memset( &bytes[i0], 0, 16 - (i0 + i1)); } return ipv6_address{bytes}; } } // urls } // boost #endif