LCOV - code coverage report
Current view: top level - libs/url/src/rfc - ipv6_address_rule.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 111 111 100.0 %
Date: 2024-03-05 20:06:56 Functions: 2 2 100.0 %

          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 : 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             :                 rv = grammar::parse(
     109             :                     it, end,
     110         597 :                     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             :             auto rv1 = grammar::parse(
     143          55 :                 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             :             rv = grammar::parse(
     168             :                 it, end,
     169         335 :                 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             : 

Generated by: LCOV version 1.15