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 292 : implementation_defined::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 292 : int n = 8; // words needed
59 292 : int b = -1; // value of n
60 : // when '::' seen
61 292 : bool c = false; // need colon
62 292 : auto prev = it;
63 : ipv6_address::bytes_type bytes;
64 292 : system::result<detail::h16_rule_t::value_type> rv;
65 : for(;;)
66 : {
67 1358 : if(it == end)
68 : {
69 91 : if(b != -1)
70 : {
71 : // end in "::"
72 85 : break;
73 : }
74 6 : BOOST_ASSERT(n > 0);
75 : // not enough words
76 6 : BOOST_URL_RETURN_EC(
77 : grammar::error::invalid);
78 : }
79 1267 : if(*it == ':')
80 : {
81 804 : ++it;
82 804 : if(it == end)
83 : {
84 : // expected ':'
85 5 : BOOST_URL_RETURN_EC(
86 : grammar::error::invalid);
87 : }
88 799 : if(*it == ':')
89 : {
90 196 : if(b == -1)
91 : {
92 : // first "::"
93 193 : ++it;
94 193 : --n;
95 193 : b = n;
96 193 : if(n == 0)
97 2 : break;
98 191 : c = false;
99 191 : 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 597 : rv = grammar::parse(
109 : it, end,
110 : 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 463 : 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 55 : auto rv1 = grammar::parse(
143 : 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 388 : grammar::hexdig_value(*it);
158 388 : if( b != -1 &&
159 : d < 0)
160 : {
161 : // ends in "::"
162 33 : break;
163 : }
164 355 : if(! c)
165 : {
166 351 : prev = it;
167 351 : rv = grammar::parse(
168 : it, end,
169 : detail::h16_rule);
170 351 : if(! rv)
171 16 : return rv.error();
172 335 : bytes[2*(8-n)+0] = rv->hi;
173 335 : bytes[2*(8-n)+1] = rv->lo;
174 335 : --n;
175 335 : if(n == 0)
176 1 : break;
177 334 : c = true;
178 334 : continue;
179 : }
180 : // ':' divides a word
181 4 : BOOST_URL_RETURN_EC(
182 : grammar::error::invalid);
183 1066 : }
184 205 : if(b == -1)
185 50 : return ipv6_address{bytes};
186 155 : 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 121 : 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 76 : auto const i0 =
212 76 : 2 * (7 - b);
213 76 : auto const i1 =
214 76 : 2 * (b - n);
215 152 : std::memmove(
216 76 : &bytes[16 - i1],
217 76 : &bytes[i0 + 2],
218 : i1);
219 76 : std::memset(
220 76 : &bytes[i0],
221 76 : 0, 16 - (i0 + i1));
222 : }
223 155 : return ipv6_address{bytes};
224 : }
225 :
226 : } // urls
227 : } // boost
228 :
|