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