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 : 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 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 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 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 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 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 335 : rv = grammar::parse(
168 : it, end,
169 : 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 :
|