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 61 : maybe_octet(
35 : unsigned char const* p) noexcept
36 : {
37 61 : unsigned short word =
38 : static_cast<unsigned short>(
39 61 : p[0]) * 256 +
40 : static_cast<unsigned short>(
41 61 : p[1]);
42 61 : if(word > 0x255)
43 3 : 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 352 : 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 352 : int n = 8; // words needed
63 352 : int b = -1; // value of n
64 : // when '::' seen
65 352 : bool c = false; // need colon
66 352 : auto prev = it;
67 : ipv6_address::bytes_type bytes;
68 352 : system::result<detail::h16_rule_t::value_type> rv;
69 : for(;;)
70 : {
71 1760 : if(it == end)
72 : {
73 92 : if(b != -1)
74 : {
75 : // end in "::"
76 86 : 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 1668 : if(*it == ':')
84 : {
85 1135 : ++it;
86 1135 : if(it == end)
87 : {
88 : // expected ':'
89 5 : BOOST_URL_CONSTEXPR_RETURN_EC(
90 : grammar::error::invalid);
91 : }
92 1130 : if(*it == ':')
93 : {
94 210 : if(b == -1)
95 : {
96 : // first "::"
97 207 : ++it;
98 207 : --n;
99 207 : b = n;
100 207 : if(n == 0)
101 2 : break;
102 205 : c = false;
103 205 : continue;
104 : }
105 : // extra "::" found
106 3 : BOOST_URL_CONSTEXPR_RETURN_EC(
107 : grammar::error::invalid);
108 : }
109 920 : if(c)
110 : {
111 914 : prev = it;
112 914 : rv = grammar::parse(
113 : it, end,
114 : detail::h16_rule);
115 914 : if(! rv)
116 5 : return rv.error();
117 909 : bytes[2*(8-n)+0] = rv->hi;
118 909 : bytes[2*(8-n)+1] = rv->lo;
119 909 : --n;
120 909 : if(n == 0)
121 96 : break;
122 813 : continue;
123 : }
124 : // expected h16
125 6 : BOOST_URL_CONSTEXPR_RETURN_EC(
126 : grammar::error::invalid);
127 : }
128 533 : if(*it == '.')
129 : {
130 81 : if(b == -1 && n > 1)
131 : {
132 : // not enough h16
133 11 : BOOST_URL_CONSTEXPR_RETURN_EC(
134 : grammar::error::invalid);
135 : }
136 70 : if(! c)
137 : {
138 : // missing h16 before "."
139 9 : BOOST_URL_CONSTEXPR_RETURN_EC(
140 : grammar::error::invalid);
141 : }
142 61 : if(! detail::maybe_octet(
143 61 : &bytes[2*(7-n)]))
144 : {
145 : // invalid octet
146 6 : BOOST_URL_CONSTEXPR_RETURN_EC(
147 : grammar::error::invalid);
148 : }
149 : // rewind the h16 and
150 : // parse it as ipv4
151 55 : it = prev;
152 55 : auto rv1 = grammar::parse(
153 : it, end, ipv4_address_rule);
154 55 : if(! rv1)
155 22 : return rv1.error();
156 33 : auto v4 = *rv1;
157 : auto const b4 =
158 33 : v4.to_bytes();
159 33 : bytes[2*(7-n)+0] = b4[0];
160 33 : bytes[2*(7-n)+1] = b4[1];
161 33 : bytes[2*(7-n)+2] = b4[2];
162 33 : bytes[2*(7-n)+3] = b4[3];
163 33 : --n;
164 33 : break;
165 : }
166 : auto d =
167 452 : grammar::hexdig_value(*it);
168 452 : if( b != -1 &&
169 : d < 0)
170 : {
171 : // ends in "::"
172 41 : break;
173 : }
174 411 : if(! c)
175 : {
176 407 : prev = it;
177 407 : rv = grammar::parse(
178 : it, end,
179 : detail::h16_rule);
180 407 : if(! rv)
181 16 : return rv.error();
182 391 : bytes[2*(8-n)+0] = rv->hi;
183 391 : bytes[2*(8-n)+1] = rv->lo;
184 391 : --n;
185 391 : if(n == 0)
186 1 : break;
187 390 : c = true;
188 390 : continue;
189 : }
190 : // ':' divides a word
191 4 : BOOST_URL_CONSTEXPR_RETURN_EC(
192 : grammar::error::invalid);
193 1408 : }
194 259 : if(b == -1)
195 95 : return ipv6_address{bytes};
196 164 : if(b == n)
197 : {
198 : // "::" last
199 35 : auto const i =
200 35 : 2 * (7 - n);
201 35 : std::memset(
202 35 : &bytes[i],
203 35 : 0, 16 - i);
204 : }
205 129 : else if(b == 7)
206 : {
207 : // "::" first
208 52 : auto const i =
209 52 : 2 * (b - n);
210 104 : std::memmove(
211 52 : &bytes[16 - i],
212 52 : &bytes[2],
213 : i);
214 52 : std::memset(
215 52 : &bytes[0],
216 52 : 0, 16 - i);
217 : }
218 : else
219 : {
220 : // "::" in middle
221 77 : auto const i0 =
222 77 : 2 * (7 - b);
223 77 : auto const i1 =
224 77 : 2 * (b - n);
225 154 : std::memmove(
226 77 : &bytes[16 - i1],
227 77 : &bytes[i0 + 2],
228 : i1);
229 77 : std::memset(
230 77 : &bytes[i0],
231 77 : 0, 16 - (i0 + i1));
232 : }
233 164 : return ipv6_address{bytes};
234 : }
235 :
236 : } // urls
237 : } // boost
238 :
239 :
240 : #endif
|