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