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 -
maybe_octet(
 
32 -
    unsigned char const* p) noexcept
 
33 -
{
 
34 -
    unsigned short word =
 
35 -
        static_cast<unsigned short>(
 
36 -
            p[0]) * 256 +
 
37 -
        static_cast<unsigned short>(
 
38 -
            p[1]);
 
39 -
    if(word > 0x255)
 
40 -
        return false;
 
41 -
    if(((word >>  4) & 0xf) > 9)
 
42 -
        return false;
 
43 -
    if((word & 0xf) > 9)
 
44 -
        return false;
 
45 -
    return true;
 
46 -
}
 
47 -

 
48 -
} // detail
 
49 -

 
50 -
auto
 
51 -
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 -
    int n = 8;      // words needed
 
59 -
    int b = -1;     // value of n
 
60 -
                    // when '::' seen
 
61 -
    bool c = false; // need colon
 
62 -
    auto prev = it;
 
63 -
    ipv6_address::bytes_type bytes;
 
64 -
    system::result<detail::h16_rule_t::value_type> rv;
 
65 -
    for(;;)
 
66 -
    {
 
67 -
        if(it == end)
 
68 -
        {
 
69 -
            if(b != -1)
 
70 -
            {
 
71 -
                // end in "::"
 
72 -
                break;
 
73 -
            }
 
74 -
            BOOST_ASSERT(n > 0);
 
75 -
            // not enough words
 
76 -
            BOOST_URL_RETURN_EC(
 
77 -
                grammar::error::invalid);
 
78 -
        }
 
79 -
        if(*it == ':')
 
80 -
        {
 
81 -
            ++it;
 
82 -
            if(it == end)
 
83 -
            {
 
84 -
                // expected ':'
 
85 -
                BOOST_URL_RETURN_EC(
 
86 -
                    grammar::error::invalid);
 
87 -
            }
 
88 -
            if(*it == ':')
 
89 -
            {
 
90 -
                if(b == -1)
 
91 -
                {
 
92 -
                    // first "::"
 
93 -
                    ++it;
 
94 -
                    --n;
 
95 -
                    b = n;
 
96 -
                    if(n == 0)
 
97 -
                        break;
 
98 -
                    c = false;
 
99 -
                    continue;
 
100 -
                }
 
101 -
                // extra "::" found
 
102 -
                BOOST_URL_RETURN_EC(
 
103 -
                    grammar::error::invalid);
 
104 -
            }
 
105 -
            if(c)
 
106 -
            {
 
107 -
                prev = it;
 
108 -
                rv = grammar::parse(
 
109 -
                    it, end,
 
110 -
                    detail::h16_rule);
 
111 -
                if(! rv)
 
112 -
                    return rv.error();
 
113 -
                bytes[2*(8-n)+0] = rv->hi;
 
114 -
                bytes[2*(8-n)+1] = rv->lo;
 
115 -
                --n;
 
116 -
                if(n == 0)
 
117 -
                    break;
 
118 -
                continue;
 
119 -
            }
 
120 -
            // expected h16
 
121 -
            BOOST_URL_RETURN_EC(
 
122 -
                grammar::error::invalid);
 
123 -
        }
 
124 -
        if(*it == '.')
 
125 -
        {
 
126 -
            if(b == -1 && n > 1)
 
127 -
            {
 
128 -
                // not enough h16
 
129 -
                BOOST_URL_RETURN_EC(
 
130 -
                    grammar::error::invalid);
 
131 -
            }
 
132 -
            if(! detail::maybe_octet(
 
133 -
                &bytes[2*(7-n)]))
 
134 -
            {
 
135 -
                // invalid octet
 
136 -
                BOOST_URL_RETURN_EC(
 
137 -
                    grammar::error::invalid);
 
138 -
            }
 
139 -
            // rewind the h16 and
 
140 -
            // parse it as ipv4
 
141 -
            it = prev;
 
142 -
            auto rv1 = grammar::parse(
 
143 -
                it, end, ipv4_address_rule);
 
144 -
            if(! rv1)
 
145 -
                return rv1.error();
 
146 -
            auto v4 = *rv1;
 
147 -
            auto const b4 =
 
148 -
                v4.to_bytes();
 
149 -
            bytes[2*(7-n)+0] = b4[0];
 
150 -
            bytes[2*(7-n)+1] = b4[1];
 
151 -
            bytes[2*(7-n)+2] = b4[2];
 
152 -
            bytes[2*(7-n)+3] = b4[3];
 
153 -
            --n;
 
154 -
            break;
 
155 -
        }
 
156 -
        auto d =
 
157 -
            grammar::hexdig_value(*it);
 
158 -
        if( b != -1 &&
 
159 -
            d < 0)
 
160 -
        {
 
161 -
            // ends in "::"
 
162 -
            break;
 
163 -
        }
 
164 -
        if(! c)
 
165 -
        {
 
166 -
            prev = it;
 
167 -
            rv = grammar::parse(
 
168 -
                it, end,
 
169 -
                detail::h16_rule);
 
170 -
            if(! rv)
 
171 -
                return rv.error();
 
172 -
            bytes[2*(8-n)+0] = rv->hi;
 
173 -
            bytes[2*(8-n)+1] = rv->lo;
 
174 -
            --n;
 
175 -
            if(n == 0)
 
176 -
                break;
 
177 -
            c = true;
 
178 -
            continue;
 
179 -
        }
 
180 -
        // ':' divides a word
 
181 -
        BOOST_URL_RETURN_EC(
 
182 -
            grammar::error::invalid);
 
183 -
    }
 
184 -
    if(b == -1)
 
185 -
        return ipv6_address{bytes};
 
186 -
    if(b == n)
 
187 -
    {
 
188 -
        // "::" last
 
189 -
        auto const i =
 
190 -
            2 * (7 - n);
 
191 -
        std::memset(
 
192 -
            &bytes[i],
 
193 -
            0, 16 - i);
 
194 -
    }
 
195 -
    else if(b == 7)
 
196 -
    {
 
197 -
        // "::" first
 
198 -
        auto const i =
 
199 -
            2 * (b - n);
 
200 -
        std::memmove(
 
201 -
            &bytes[16 - i],
 
202 -
            &bytes[2],
 
203 -
            i);
 
204 -
        std::memset(
 
205 -
            &bytes[0],
 
206 -
            0, 16 - i);
 
207 -
    }
 
208 -
    else
 
209 -
    {
 
210 -
        // "::" in middle
 
211 -
        auto const i0 =
 
212 -
            2 * (7 - b);
 
213 -
        auto const i1 =
 
214 -
            2 * (b - n);
 
215 -
        std::memmove(
 
216 -
            &bytes[16 - i1],
 
217 -
            &bytes[i0 + 2],
 
218 -
            i1);
 
219 -
        std::memset(
 
220 -
            &bytes[i0],
 
221 -
            0, 16 - (i0 + i1));
 
222 -
    }
 
223 -
    return ipv6_address{bytes};
 
224 -
}
 
225 -

 
226 -
} // urls
 
227 -
} // boost
 
228 -