Line data Source code
1 : // 2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 3 : // 4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 : // 7 : // Official repository: https://github.com/boostorg/url 8 : // 9 : 10 : #ifndef BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP 11 : #define BOOST_URL_RFC_IMPL_IPV6_ADDRESS_RULE_IPP 12 : 13 : #include <boost/url/detail/config.hpp> 14 : #include <boost/url/rfc/ipv6_address_rule.hpp> 15 : #include <boost/url/rfc/ipv4_address_rule.hpp> 16 : #include "detail/h16_rule.hpp" 17 : #include <boost/url/grammar/charset.hpp> 18 : #include <boost/url/grammar/hexdig_chars.hpp> 19 : #include <boost/url/grammar/parse.hpp> 20 : #include <boost/assert.hpp> 21 : #include <cstring> 22 : 23 : namespace boost { 24 : namespace urls { 25 : 26 : namespace detail { 27 : 28 : // return `true` if the hex 29 : // word could be 0..255 if 30 : // interpreted as decimal 31 : static 32 : bool 33 65 : maybe_octet( 34 : unsigned char const* p) noexcept 35 : { 36 65 : unsigned short word = 37 : static_cast<unsigned short>( 38 65 : p[0]) * 256 + 39 : static_cast<unsigned short>( 40 65 : p[1]); 41 65 : if(word > 0x255) 42 7 : return false; 43 58 : if(((word >> 4) & 0xf) > 9) 44 1 : return false; 45 57 : if((word & 0xf) > 9) 46 2 : return false; 47 55 : return true; 48 : } 49 : 50 : } // detail 51 : 52 : auto 53 288 : ipv6_address_rule_t:: 54 : parse( 55 : char const*& it, 56 : char const* const end 57 : ) const noexcept -> 58 : system::result<ipv6_address> 59 : { 60 288 : int n = 8; // words needed 61 288 : int b = -1; // value of n 62 : // when '::' seen 63 288 : bool c = false; // need colon 64 288 : auto prev = it; 65 : ipv6_address::bytes_type bytes; 66 288 : system::result<detail::h16_rule_t::value_type> rv; 67 : for(;;) 68 : { 69 1324 : if(it == end) 70 : { 71 91 : if(b != -1) 72 : { 73 : // end in "::" 74 83 : break; 75 : } 76 8 : BOOST_ASSERT(n > 0); 77 : // not enough words 78 8 : BOOST_URL_RETURN_EC( 79 : grammar::error::invalid); 80 : } 81 1233 : if(*it == ':') 82 : { 83 794 : ++it; 84 794 : if(it == end) 85 : { 86 : // expected ':' 87 5 : BOOST_URL_RETURN_EC( 88 : grammar::error::invalid); 89 : } 90 789 : if(*it == ':') 91 : { 92 186 : if(b == -1) 93 : { 94 : // first "::" 95 183 : ++it; 96 183 : --n; 97 183 : b = n; 98 183 : if(n == 0) 99 2 : break; 100 181 : c = false; 101 181 : continue; 102 : } 103 : // extra "::" found 104 3 : BOOST_URL_RETURN_EC( 105 : grammar::error::invalid); 106 : } 107 603 : if(c) 108 : { 109 597 : prev = it; 110 : rv = grammar::parse( 111 : it, end, 112 597 : detail::h16_rule); 113 597 : if(! rv) 114 5 : return rv.error(); 115 592 : bytes[2*(8-n)+0] = rv->hi; 116 592 : bytes[2*(8-n)+1] = rv->lo; 117 592 : --n; 118 592 : if(n == 0) 119 51 : break; 120 541 : continue; 121 : } 122 : // expected h16 123 6 : BOOST_URL_RETURN_EC( 124 : grammar::error::invalid); 125 : } 126 439 : if(*it == '.') 127 : { 128 75 : if(b == -1 && n > 1) 129 : { 130 : // not enough h16 131 10 : BOOST_URL_RETURN_EC( 132 : grammar::error::invalid); 133 : } 134 65 : if(! detail::maybe_octet( 135 65 : &bytes[2*(7-n)])) 136 : { 137 : // invalid octet 138 10 : BOOST_URL_RETURN_EC( 139 : grammar::error::invalid); 140 : } 141 : // rewind the h16 and 142 : // parse it as ipv4 143 55 : it = prev; 144 : auto rv1 = grammar::parse( 145 55 : it, end, ipv4_address_rule); 146 55 : if(! rv1) 147 22 : return rv1.error(); 148 33 : auto v4 = *rv1; 149 : auto const b4 = 150 33 : v4.to_bytes(); 151 33 : bytes[2*(7-n)+0] = b4[0]; 152 33 : bytes[2*(7-n)+1] = b4[1]; 153 33 : bytes[2*(7-n)+2] = b4[2]; 154 33 : bytes[2*(7-n)+3] = b4[3]; 155 33 : --n; 156 33 : break; 157 : } 158 : auto d = 159 364 : grammar::hexdig_value(*it); 160 364 : if( b != -1 && 161 : d < 0) 162 : { 163 : // ends in "::" 164 25 : break; 165 : } 166 339 : if(! c) 167 : { 168 335 : prev = it; 169 : rv = grammar::parse( 170 : it, end, 171 335 : detail::h16_rule); 172 335 : if(! rv) 173 20 : return rv.error(); 174 315 : bytes[2*(8-n)+0] = rv->hi; 175 315 : bytes[2*(8-n)+1] = rv->lo; 176 315 : --n; 177 315 : if(n == 0) 178 1 : break; 179 314 : c = true; 180 314 : continue; 181 : } 182 : // ':' divides a word 183 4 : BOOST_URL_RETURN_EC( 184 : grammar::error::invalid); 185 1036 : } 186 195 : if(b == -1) 187 50 : return ipv6_address{bytes}; 188 145 : if(b == n) 189 : { 190 : // "::" last 191 34 : auto const i = 192 34 : 2 * (7 - n); 193 34 : std::memset( 194 34 : &bytes[i], 195 34 : 0, 16 - i); 196 : } 197 111 : else if(b == 7) 198 : { 199 : // "::" first 200 45 : auto const i = 201 45 : 2 * (b - n); 202 90 : std::memmove( 203 45 : &bytes[16 - i], 204 45 : &bytes[2], 205 : i); 206 45 : std::memset( 207 45 : &bytes[0], 208 45 : 0, 16 - i); 209 : } 210 : else 211 : { 212 : // "::" in middle 213 66 : auto const i0 = 214 66 : 2 * (7 - b); 215 66 : auto const i1 = 216 66 : 2 * (b - n); 217 132 : std::memmove( 218 66 : &bytes[16 - i1], 219 66 : &bytes[i0 + 2], 220 : i1); 221 66 : std::memset( 222 66 : &bytes[i0], 223 66 : 0, 16 - (i0 + i1)); 224 : } 225 145 : return ipv6_address{bytes}; 226 : } 227 : 228 : } // urls 229 : } // boost 230 : 231 : #endif