LCOV - code coverage report
Current view: top level - src - addr_parser.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 313 313 100.0 %
Date: 2017-01-21 19:00:32 Functions: 27 27 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Network Address -- classes functions to ease handling IP addresses
       2             : // Copyright (C) 2012-2017  Made to Order Software Corp.
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : // self
      19             : //
      20             : #include "libaddr/addr.h"
      21             : 
      22             : // C++ lib
      23             : //
      24             : #include <algorithm>
      25             : #include <sstream>
      26             : #include <iostream>
      27             : 
      28             : // C lib
      29             : //
      30             : #include <ifaddrs.h>
      31             : #include <netdb.h>
      32             : 
      33             : 
      34             : 
      35             : namespace addr
      36             : {
      37             : 
      38             : 
      39             : namespace
      40             : {
      41             : 
      42             : 
      43             : /** \brief Delete an addrinfo structure.
      44             :  *
      45             :  * This deleter is used to make sure all the addinfo get released when
      46             :  * an exception occurs or the function using such exists.
      47             :  *
      48             :  * \param[in] ai  The addrinfo structure to free.
      49             :  */
      50      131847 : void addrinfo_deleter(struct addrinfo * ai)
      51             : {
      52      131847 :     freeaddrinfo(ai);
      53      131847 : }
      54             : 
      55             : 
      56             : }
      57             : 
      58             : 
      59             : 
      60             : 
      61             : 
      62         206 : void addr_parser::set_default_address(std::string const & addr)
      63             : {
      64         206 :     f_default_address = addr;
      65         206 : }
      66             : 
      67             : 
      68         104 : std::string const & addr_parser::get_default_address() const
      69             : {
      70         104 :     return f_default_address;
      71             : }
      72             : 
      73             : 
      74             : /** \brief Define the default port.
      75             :  *
      76             :  * This function is used to define the default port to use in the address
      77             :  * parser object. By default this is set to -1 meaning: no default port.
      78             :  *
      79             :  * This function accepts any port number from 0 to 65535. It also accepts
      80             :  * -1 to reset the port back to "no default".
      81             :  *
      82             :  * \param[in] port  The new default port.
      83             :  */
      84          76 : void addr_parser::set_default_port(int const port)
      85             : {
      86          76 :     if(port < -1
      87          63 :     || port > 65535)
      88             :     {
      89          25 :         throw addr_invalid_argument_exception("addr_parser::set_default_port(): port must be in range [-1..65535].");
      90             :     }
      91             : 
      92          51 :     f_default_port = port;
      93          51 : }
      94             : 
      95             : 
      96          76 : int addr_parser::get_default_port() const
      97             : {
      98          76 :     return f_default_port;
      99             : }
     100             : 
     101             : 
     102         100 : void addr_parser::set_default_mask(std::string const & mask)
     103             : {
     104         100 :     f_default_mask = mask;
     105         100 : }
     106             : 
     107             : 
     108           1 : std::string const & addr_parser::get_default_mask() const
     109             : {
     110           1 :     return f_default_mask;
     111             : }
     112             : 
     113             : 
     114         109 : void addr_parser::set_protocol(std::string const & protocol)
     115             : {
     116         109 :     if(protocol == "ip")
     117             :     {
     118           2 :         f_protocol = IPPROTO_IP;
     119             :     }
     120         107 :     else if(protocol == "tcp")
     121             :     {
     122         103 :         f_protocol = IPPROTO_TCP;
     123             :     }
     124           4 :     else if(protocol == "udp")
     125             :     {
     126           2 :         f_protocol = IPPROTO_UDP;
     127             :     }
     128             :     else
     129             :     {
     130             :         // not a protocol we support
     131             :         //
     132             :         throw addr_invalid_argument_exception(
     133             :                   std::string("unknown protocol \"")
     134           4 :                 + protocol
     135           6 :                 + "\", expected \"tcp\" or \"udp\".");
     136             :     }
     137         107 : }
     138             : 
     139             : 
     140      131899 : void addr_parser::set_protocol(int const protocol)
     141             : {
     142             :     // make sure that's a protocol we support
     143             :     //
     144      131899 :     switch(protocol)
     145             :     {
     146             :     case IPPROTO_IP:
     147             :     case IPPROTO_TCP:
     148             :     case IPPROTO_UDP:
     149      131699 :         break;
     150             : 
     151             :     default:
     152             :         throw addr_invalid_argument_exception(
     153             :                   std::string("unknown protocol \"")
     154         400 :                 + std::to_string(protocol)
     155         600 :                 + "\", expected \"tcp\" or \"udp\".");
     156             : 
     157             :     }
     158             : 
     159      131699 :     f_protocol = protocol;
     160      131699 : }
     161             : 
     162             : 
     163             : /** \brief Use this function to reset the protocol back to "no default."
     164             :  *
     165             :  * This function sets the protocol to -1 (which is something you cannot
     166             :  * do by callingt he set_protocol() functions above.)
     167             :  *
     168             :  * The -1 special value means that the protocol is not defined, that
     169             :  * there is no default. In most cases this means all the addresses
     170             :  * that match, ignoring the protocol, will be returned by the parse()
     171             :  * function.
     172             :  */
     173           3 : void addr_parser::clear_protocol()
     174             : {
     175           3 :     f_protocol = -1;
     176           3 : }
     177             : 
     178             : 
     179         216 : int addr_parser::get_protocol() const
     180             : {
     181         216 :     return f_protocol;
     182             : }
     183             : 
     184             : 
     185             : /** \brief Set or clear allow flags in the parser.
     186             :  *
     187             :  * This parser has a set of flags it uses to know whether the input
     188             :  * string can include certain things such as a port or a mask.
     189             :  *
     190             :  * This function is used to allow or require certain parameters and
     191             :  * to disallow others.
     192             :  *
     193             :  * By default, the ADDRESS and PORT flags are set, meaning that an
     194             :  * address and a port can appear, but either or both are optinal.
     195             :  * If unspecified, then the default will be used. If not default
     196             :  * is defined, then the parser may fail in this situation.
     197             :  *
     198             :  * One problem is that we include contradictory syntatical features.
     199             :  * The parser supports lists of addresses separated by commas and
     200             :  * lists of ports separated by commas. Both are not supported
     201             :  * simultaneously. This means you want to allow multiple addresses
     202             :  * separated by commas, the function makes sure that the multiple
     203             :  * port separated by commas support is turned of.
     204             :  *
     205             :  * \li ADDRESS -- the IP address is allowed, but optional
     206             :  * \li REQUIRED_ADDRESS -- the IP address is mandatory
     207             :  * \li PORT -- the port is allowed, but optional
     208             :  * \li REQUIRED_PORT -- the port is mandatory
     209             :  * \li MASK -- the mask is allowed, but optional
     210             :  * \li MULTI_ADDRESSES_COMMAS -- the input can have multiple addresses
     211             :  * separated by commas, spaces are not allowed (prevents MULTI_PORTS_COMMAS)
     212             :  * \li MULTI_ADDRESSES_SPACES -- the input can have multiple addresses
     213             :  * separated by spaces
     214             :  * \li MULTI_ADDRESSES_COMMAS_AND_SPACES -- the input can have multiple
     215             :  * addresses separated by spaces and commas (prevents MULTI_PORTS_COMMAS)
     216             :  * \li MULTI_PORTS_SEMICOLONS -- the input can  have multiple ports
     217             :  * separated by semicolons NOT IMPLEMENTED YET
     218             :  * \li MULTI_PORTS_COMMAS -- the input can have multiple ports separated
     219             :  * by commas (prevents MULTI_ADDRESSES_COMMAS and
     220             :  * MULTI_ADDRESSES_COMMAS_AND_SPACES) NOT IMPLEMENTED YET
     221             :  * \li PORT_RANGE -- the input supports port ranges (p1-p2) NOT
     222             :  * IMPLEMENTED YET
     223             :  * \li ADDRESS_RANGE -- the input supports address ranges (addr-addr) NOT
     224             :  * IMPLEMENTED YET
     225             :  *
     226             :  * \param[in] flag  The flag to set or clear.
     227             :  * \param[in] allow  Whether to allow (true) or disallow (false).
     228             :  *
     229             :  * \sa get_allow()
     230             :  */
     231         417 : void addr_parser::set_allow(flag_t const flag, bool const allow)
     232             : {
     233         417 :     if(flag < static_cast<flag_t>(0)
     234         397 :     || flag >= flag_t::FLAG_max)
     235             :     {
     236          40 :         throw addr_invalid_argument_exception("addr_parser::set_allow(): flag has to be one of the valid flags.");
     237             :     }
     238             : 
     239         377 :     f_flags[static_cast<int>(flag)] = allow;
     240             : 
     241             :     // if we just set a certain flag, others may need to go to false
     242             :     //
     243         377 :     if(allow)
     244             :     {
     245             :         // we can only support one type of commas
     246             :         //
     247         369 :         switch(flag)
     248             :         {
     249             :         case flag_t::MULTI_ADDRESSES_COMMAS:
     250             :         case flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES:
     251           7 :             f_flags[static_cast<int>(flag_t::MULTI_PORTS_COMMAS)] = false;
     252           7 :             break;
     253             : 
     254             :         case flag_t::MULTI_PORTS_COMMAS:
     255           2 :             f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)] = false;
     256           2 :             f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)] = false;
     257           2 :             break;
     258             : 
     259             :         default:
     260         360 :             break;
     261             : 
     262             :         }
     263             :     }
     264         377 : }
     265             : 
     266             : 
     267          56 : bool addr_parser::get_allow(flag_t const flag) const
     268             : {
     269          56 :     if(flag < static_cast<flag_t>(0)
     270          46 :     || flag >= flag_t::FLAG_max)
     271             :     {
     272          20 :         throw addr_invalid_argument_exception("addr_parser::get_allow(): flag has to be one of the valid flags.");
     273             :     }
     274             : 
     275          36 :     return f_flags[static_cast<int>(flag)];
     276             : }
     277             : 
     278             : 
     279      131710 : bool addr_parser::has_errors() const
     280             : {
     281      131710 :     return !f_error.empty();
     282             : }
     283             : 
     284             : 
     285          46 : void addr_parser::emit_error(std::string const & msg)
     286             : {
     287          46 :     f_error += msg;
     288          46 :     f_error += "\n";
     289          46 :     ++f_error_count;
     290          46 : }
     291             : 
     292             : 
     293          46 : std::string const & addr_parser::error_messages() const
     294             : {
     295          46 :     return f_error;
     296             : }
     297             : 
     298             : 
     299          46 : int addr_parser::error_count() const
     300             : {
     301          46 :     return f_error_count;
     302             : }
     303             : 
     304             : 
     305           5 : void addr_parser::clear_errors()
     306             : {
     307           5 :     f_error.clear();
     308           5 :     f_error_count = 0;
     309           5 : }
     310             : 
     311             : 
     312      131700 : addr_range::vector_t addr_parser::parse(std::string const & in)
     313             : {
     314      131700 :     addr_range::vector_t result;
     315             : 
     316      131700 :     if(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)]
     317      131699 :     || f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_SPACES)])
     318             :     {
     319           2 :         char sep(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)] ? ',' : ' ');
     320           2 :         std::string::size_type s(0);
     321          24 :         while(s < in.length())
     322             :         {
     323          11 :             std::string::size_type e(in.find(sep, s));
     324          11 :             if(e == std::string::npos)
     325             :             {
     326           2 :                 e = in.length();
     327             :             }
     328          11 :             if(e > s)
     329             :             {
     330           6 :                 parse_cidr(in.substr(s, e - s), result);
     331             :             }
     332          11 :             s = e + 1;
     333           2 :         }
     334             :     }
     335      131698 :     else if(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)])
     336             :     {
     337           2 :         std::string comma_space(", ");
     338           1 :         std::string::size_type s(0);
     339          17 :         while(s < in.length())
     340             :         {
     341             :             // since C++11 we have a way to search for a set of character
     342             :             // in a string with an algorithm!
     343             :             //
     344           8 :             auto const it(std::find_first_of(in.begin() + s, in.end(), comma_space.begin(), comma_space.end()));
     345           8 :             std::string::size_type const e(it == in.end() ? in.length() : it - in.begin());
     346           8 :             if(e > s)
     347             :             {
     348           3 :                 parse_cidr(in.substr(s, e - s), result);
     349             :             }
     350           8 :             s = e + 1;
     351             :         }
     352             :     }
     353             :     else
     354             :     {
     355      131697 :         parse_cidr(in, result);
     356             :     }
     357             : 
     358      131700 :     return result;
     359             : }
     360             : 
     361             : 
     362             : /** \brief Check one address.
     363             :  *
     364             :  * This function checks one address, although if it is a name, it could
     365             :  * represent multiple IP addresses.
     366             :  *
     367             :  * This function separate the address:port from the mask if the mask is
     368             :  * allowed. Then it parses the address:port part and the mask separately.
     369             :  *
     370             :  * \param[in] in  The address to parse.
     371             :  * \param[in] result  The resulting list of addresses.
     372             :  */
     373      131706 : void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
     374             : {
     375      131706 :     if(f_flags[static_cast<int>(flag_t::MASK)])
     376             :     {
     377             :         // check whether there is a mask
     378             :         //
     379         710 :         std::string mask(f_default_mask);
     380             : 
     381         710 :         std::string address;
     382         355 :         std::string::size_type const p(in.find('/'));
     383         355 :         if(p != std::string::npos)
     384             :         {
     385         302 :             address = in.substr(0, p);
     386         302 :             mask = in.substr(p + 1);
     387             :         }
     388             :         else
     389             :         {
     390          53 :             address = in;
     391             :         }
     392         355 :         if(mask.empty())
     393             :         {
     394             :             // mask not found, do as if none defined
     395             :             //
     396           5 :             parse_address(address, result);
     397             :         }
     398             :         else
     399             :         {
     400         350 :             int const errcnt(f_error_count);
     401             : 
     402             :             // handle the address first
     403             :             //
     404         700 :             addr_range::vector_t addr_mask;
     405         350 :             parse_address(address, addr_mask);
     406             : 
     407             :             // now check for the mask
     408             :             //
     409         700 :             for(auto & am : addr_mask)
     410             :             {
     411         350 :                 parse_mask(mask, am.get_from());
     412             :             }
     413             : 
     414             :             // now append the list to the result if no errors occurred
     415             :             //
     416         350 :             if(errcnt == f_error_count)
     417             :             {
     418         315 :                 result.insert(result.end(), addr_mask.begin(), addr_mask.end());
     419             :             }
     420             :         }
     421             :     }
     422             :     else
     423             :     {
     424             :         // no mask allowed, if there is one, this call will fail
     425             :         //
     426      131351 :         parse_address(in, result);
     427             :     }
     428      131706 : }
     429             : 
     430             : 
     431      131706 : void addr_parser::parse_address(std::string const & in, addr_range::vector_t & result)
     432             : {
     433             :     // With our only supportted format, ipv6 addresses must be between square
     434             :     // brackets. The address may just be a mask in which case the '[' may
     435             :     // not be at the very start (i.e. "/[ffff:ffff::]")
     436             :     //
     437      131706 :     if(in.find('[') != std::string::npos)
     438             :     {
     439             :         // IPv6 parsing
     440             :         //
     441       65769 :         parse_address6(in, result);
     442             :     }
     443             :     else
     444             :     {
     445       65937 :         parse_address4(in, result);
     446             :     }
     447      131706 : }
     448             : 
     449             : 
     450       65937 : void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
     451             : {
     452      131873 :     std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
     453      131873 :     std::string address(f_default_address);
     454             : 
     455       65937 :     std::string::size_type const p(in.find(':'));
     456             : 
     457       65937 :     if(f_flags[static_cast<int>(flag_t::PORT)]
     458           5 :     || f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
     459             :     {
     460             :         // the address can include a port
     461             :         //
     462      131866 :         if(p != std::string::npos)
     463             :         {
     464             :             // get the address only if not empty (otherwise we want to
     465             :             // keep the default)
     466             :             //
     467       65897 :             if(p > 0)
     468             :             {
     469       65697 :                 address = in.substr(0, p);
     470             :             }
     471             : 
     472             :             // get the port only if not empty (otherwise we want to
     473             :             // keep the default)
     474             :             //
     475       65897 :             if(p + 1 < in.length())
     476             :             {
     477       65872 :                 port_str = in.substr(p + 1);
     478             :             }
     479             :         }
     480          36 :         else if(!in.empty())
     481             :         {
     482          33 :             address = in;
     483             :         }
     484             :     }
     485             :     else
     486             :     {
     487           4 :         if(p != std::string::npos
     488           1 :         && !f_flags[static_cast<int>(flag_t::PORT)]
     489           1 :         && !f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
     490             :         {
     491           1 :             emit_error("Port not allowed (" + in + ").");
     492           1 :             return;
     493             :         }
     494             : 
     495           3 :         if(!in.empty())
     496             :         {
     497           1 :             address = in;
     498             :         }
     499             :     }
     500             : 
     501       65936 :     parse_address_port(address, port_str, result);
     502             : }
     503             : 
     504             : 
     505       65769 : void addr_parser::parse_address6(std::string const & in, addr_range::vector_t & result)
     506             : {
     507       65769 :     std::string::size_type p(0);
     508             : 
     509      131536 :     std::string address(f_default_address);
     510      131536 :     std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
     511             : 
     512             :     // if there is an address extract it otherwise put the default
     513             :     //
     514      131538 :     if(!in.empty()
     515       65769 :     && in[0] == '[')
     516             :     {
     517       65769 :         p = in.find(']');
     518             : 
     519       65769 :         if(p == std::string::npos)
     520             :         {
     521           1 :             emit_error("IPv6 is missing the ']' (" + in + ").");
     522           1 :             return;
     523             :         }
     524             : 
     525       65768 :         if(p != 1)
     526             :         {
     527             :             // get the address only if not empty (otherwise we want to
     528             :             // keep the default) -- so we actually support "[]" to
     529             :             // represent "use the default address if defined".
     530             :             //
     531       65767 :             address = in.substr(1, p - 1);
     532             :         }
     533             :     }
     534             : 
     535             :     // on entry 'p' is either 0 or the position of the ']' character
     536             :     //
     537       65768 :     p = in.find(':', p);
     538             : 
     539       65768 :     if(p != std::string::npos)
     540             :     {
     541       65762 :         if(f_flags[static_cast<int>(flag_t::PORT)]
     542           1 :         || f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
     543             :         {
     544             :             // there is also a port, extract it
     545             :             //
     546       65761 :             port_str = in.substr(p + 1);
     547             :         }
     548           1 :         else if(!f_flags[static_cast<int>(flag_t::PORT)]
     549           1 :              && !f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
     550             :         {
     551           1 :             emit_error("Port not allowed (" + in + ").");
     552           1 :             return;
     553             :         }
     554             :     }
     555             : 
     556       65767 :     parse_address_port(address, port_str, result);
     557             : }
     558             : 
     559             : 
     560      131703 : void addr_parser::parse_address_port(std::string const & address, std::string const & port_str, addr_range::vector_t & result)
     561             : {
     562             :     // make sure the port is good
     563             :     //
     564      263406 :     if(port_str.empty()
     565      131703 :     && f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
     566             :     {
     567           4 :         emit_error("Required port is missing.");
     568          12 :         return;
     569             :     }
     570             : 
     571             :     // make sure the address is good
     572             :     //
     573      263398 :     if(address.empty()
     574      131699 :     && f_flags[static_cast<int>(flag_t::REQUIRED_ADDRESS)])
     575             :     {
     576           2 :         emit_error("Required address is missing.");
     577           2 :         return;
     578             :     }
     579             : 
     580             :     // prepare hints for the the getaddrinfo() function
     581             :     //
     582             :     struct addrinfo hints;
     583      131697 :     memset(&hints, 0, sizeof(hints));
     584      131697 :     hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
     585      131697 :     hints.ai_family = AF_UNSPEC;
     586             : 
     587      131697 :     switch(f_protocol)
     588             :     {
     589             :     case IPPROTO_TCP:
     590       65722 :         hints.ai_socktype = SOCK_STREAM;
     591       65722 :         hints.ai_protocol = IPPROTO_TCP;
     592       65722 :         break;
     593             : 
     594             :     case IPPROTO_UDP:
     595       65972 :         hints.ai_socktype = SOCK_DGRAM;
     596       65972 :         hints.ai_protocol = IPPROTO_UDP;
     597       65972 :         break;
     598             : 
     599             :     }
     600             : 
     601             :     // convert address to binary
     602             :     //
     603      131697 :     struct addrinfo * addrlist(nullptr);
     604             :     {
     605      131697 :         errno = 0;
     606      131697 :         int const r(getaddrinfo(address.c_str(), port_str.c_str(), &hints, &addrlist));
     607      131697 :         if(r != 0)
     608             :         {
     609             :             // break on invalid addresses
     610             :             //
     611           2 :             int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
     612             :             emit_error("Invalid address in \""
     613           4 :                      + address
     614           6 :                      + (port_str.empty() ? "" : ":")
     615           4 :                      + port_str
     616           4 :                      + "\" error "
     617           8 :                      + std::to_string(r)
     618           4 :                      + " -- "
     619           6 :                      + gai_strerror(r)
     620           4 :                      + " (errno: "
     621           8 :                      + std::to_string(e)
     622           4 :                      + " -- "
     623           6 :                      + strerror(e)
     624           2 :                      + ").");
     625           2 :             return;
     626             :         }
     627             :     }
     628      263390 :     std::shared_ptr<struct addrinfo> ai(addrlist, addrinfo_deleter);
     629             : 
     630      131695 :     bool first(true);
     631      395089 :     while(addrlist != nullptr)
     632             :     {
     633             :         // go through the addresses and create ranges and save that in the result
     634             :         //
     635      131697 :         if(addrlist->ai_family == AF_INET)
     636             :         {
     637       65832 :             if(addrlist->ai_addrlen != sizeof(struct sockaddr_in))
     638             :             {
     639             :                 emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
     640             :                          + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
     641             :                          + ", expected"                                  // LCOV_EXCL_LINE
     642             :                          + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
     643             :                          + ").");                                        // LCOV_EXCL_LINE
     644             :             }
     645             :             else
     646             :             {
     647      131664 :                 addr a(*reinterpret_cast<struct sockaddr_in *>(addrlist->ai_addr));
     648             :                 // in most cases we do not get a protocol from
     649             :                 // the getaddrinfo() function...
     650       65832 :                 if(addrlist->ai_protocol != 0)
     651             :                 {
     652       65831 :                     a.set_protocol(addrlist->ai_protocol);
     653             :                 }
     654      131664 :                 addr_range r;
     655       65832 :                 r.set_from(a);
     656       65832 :                 result.push_back(r);
     657             :             }
     658             :         }
     659       65865 :         else if(addrlist->ai_family == AF_INET6)
     660             :         {
     661       65865 :             if(addrlist->ai_addrlen != sizeof(struct sockaddr_in6))
     662             :             {
     663             :                 emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
     664             :                          + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
     665             :                          + ", expected "                                 // LCOV_EXCL_LINE
     666             :                          + std::to_string(sizeof(struct sockaddr_in6))   // LCOV_EXCL_LINE
     667             :                          + ").");                                        // LCOV_EXCL_LINE
     668             :             }
     669             :             else
     670             :             {
     671      131730 :                 addr a(*reinterpret_cast<struct sockaddr_in6 *>(addrlist->ai_addr));
     672       65865 :                 a.set_protocol(addrlist->ai_protocol);
     673      131730 :                 addr_range r;
     674       65865 :                 r.set_from(a);
     675       65865 :                 result.push_back(r);
     676             :             }
     677             :         }
     678             :         else if(first)                                                  // LCOV_EXCL_LINE
     679             :         {
     680             :             // ignore errors from further addresses
     681             :             //
     682             :             emit_error("Unsupported address family "                     // LCOV_EXCL_LINE
     683             :                      + std::to_string(addrlist->ai_family)               // LCOV_EXCL_LINE
     684             :                      + ".");                                             // LCOV_EXCL_LINE
     685             :         }
     686             : 
     687      131697 :         first = false;
     688             : 
     689      131697 :         addrlist = addrlist->ai_next;
     690             :     }
     691             : }
     692             : 
     693             : 
     694             : /** \brief Parse a mask.
     695             :  *
     696             :  * If the input string is a decimal number, then use that as the
     697             :  * number of bits to clear.
     698             :  *
     699             :  * If the mask is not just one decimal number, try to convert it
     700             :  * as an address.
     701             :  *
     702             :  * If the string is neither a decimal number nor a valid IP address
     703             :  * then the parser adds an error string to the f_error variable.
     704             :  *
     705             :  * \param[in] mask  The mask to transform to binary.
     706             :  * \param[in,out] cidr  The address to which the mask will be added.
     707             :  */
     708         350 : void addr_parser::parse_mask(std::string const & mask, addr & cidr)
     709             : {
     710             :     // no mask?
     711             :     //
     712         350 :     if(mask.empty())
     713             :     {
     714             :         // in the current implementation this cannot happen since we
     715             :         // do not call this function when mask is empty
     716             :         //
     717             :         // hwoever, the algorithm below expect that 'mask' is not
     718             :         // empty (otherwise we get the case of 0 even though it
     719             :         // may not be correct.)
     720             :         //
     721             :         return;  // LCOV_EXCL_LINE
     722             :     }
     723             : 
     724             :     // the mask may be a decimal number or an address, if just one number
     725             :     // then it's not an address, so test that first
     726             :     //
     727         350 :     uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
     728             : 
     729             :     // convert the mask to an integer, if possible
     730             :     //
     731         350 :     int mask_count(0);
     732             :     {
     733         942 :         for(char const * s(mask.c_str()); *s != '\0'; ++s)
     734             :         {
     735         768 :             if(*s >= '0' && *s <= '9')
     736             :             {
     737         602 :                 mask_count = mask_count * 10 + *s - '0';
     738        1194 :                 if(mask_count > 1000)
     739             :                 {
     740             :                     emit_error("Mask number too large ("
     741          20 :                              + mask
     742          10 :                              + ", expected a maximum of 128).");
     743          10 :                     return;
     744             :                 }
     745             :             }
     746             :             else
     747             :             {
     748         166 :                 mask_count = -1;
     749         166 :                 break;
     750             :             }
     751             :         }
     752             :     }
     753             : 
     754             :     // the conversion to an integer worked if mask_count != -1
     755             :     //
     756         340 :     if(mask_count != -1)
     757             :     {
     758         174 :         if(cidr.is_ipv4())
     759             :         {
     760          40 :             if(mask_count > 32)
     761             :             {
     762             :                 emit_error("Unsupported mask size ("
     763          10 :                          + std::to_string(mask_count)
     764           5 :                          + ", expected 32 at the most for an IPv4).");
     765           5 :                 return;
     766             :             }
     767          35 :             mask_count = 32 - mask_count;
     768             :         }
     769             :         else
     770             :         {
     771         134 :             if(mask_count > 128)
     772             :             {
     773             :                 emit_error("Unsupported mask size ("
     774          10 :                          + std::to_string(mask_count)
     775           5 :                          + ", expected 128 at the most for an IPv6).");
     776           5 :                 return;
     777             :             }
     778         129 :             mask_count = 128 - mask_count;
     779             :         }
     780             : 
     781             :         // clear a few bits at the bottom of mask_bits
     782             :         //
     783         164 :         int idx(15);
     784        2184 :         for(; mask_count > 8; mask_count -= 8, --idx)
     785             :         {
     786        1010 :             mask_bits[idx] = 0;
     787             :         }
     788         164 :         mask_bits[idx] = 255 << mask_count;
     789             :     }
     790             :     else //if(mask_count < 0)
     791             :     {
     792             :         // prepare hints for the the getaddrinfo() function
     793             :         //
     794             :         struct addrinfo hints;
     795         166 :         memset(&hints, 0, sizeof(hints));
     796         166 :         hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
     797         166 :         hints.ai_family = AF_UNSPEC;
     798             : 
     799         166 :         switch(cidr.get_protocol())
     800             :         {
     801             :         case IPPROTO_TCP:
     802          80 :             hints.ai_socktype = SOCK_STREAM;
     803          80 :             hints.ai_protocol = IPPROTO_TCP;
     804          80 :             break;
     805             : 
     806             :         case IPPROTO_UDP:
     807          86 :             hints.ai_socktype = SOCK_DGRAM;
     808          86 :             hints.ai_protocol = IPPROTO_UDP;
     809          86 :             break;
     810             : 
     811             :         }
     812             : 
     813         316 :         std::string const port_str(std::to_string(cidr.get_port()));
     814             : 
     815             :         // if the mask is an IPv6, then it has to have the '[...]'
     816         316 :         std::string m(mask);
     817         166 :         if(cidr.is_ipv4())
     818             :         {
     819          82 :             if(mask[0] == '[')
     820             :             {
     821           1 :                 emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
     822           1 :                 return;
     823             :             }
     824             :         }
     825             :         else //if(!cidr.is_ipv4())
     826             :         {
     827          84 :             if(mask[0] != '[')
     828             :             {
     829           1 :                 emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
     830           1 :                 return;
     831             :             }
     832          83 :             if(mask.back() != ']')
     833             :             {
     834           1 :                 emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
     835           1 :                 return;
     836             :             }
     837             : 
     838             :             // note that we know that mask.length() >= 2 here since
     839             :             // we at least have a '[' and ']'
     840             :             //
     841          82 :             m = mask.substr(1, mask.length() - 2);
     842          82 :             if(m.empty())
     843             :             {
     844             :                 // an empty mask is valid, it just means keep the default
     845             :                 // (getaddrinfo() fails on an empty string)
     846             :                 //
     847           1 :                 return;
     848             :             }
     849             :         }
     850             : 
     851             :         // if negative, we may have a full address here, so call the
     852             :         // getaddrinfo() on this other string
     853             :         //
     854         162 :         struct addrinfo * masklist(nullptr);
     855         162 :         errno = 0;
     856         162 :         int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
     857         162 :         if(r != 0)
     858             :         {
     859             :             // break on invalid addresses
     860             :             //
     861          10 :             int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
     862             :             emit_error("Invalid mask in \"/"
     863          20 :                      + mask
     864          20 :                      + "\", error "
     865          40 :                      + std::to_string(r)
     866          20 :                      + " -- "
     867          30 :                      + gai_strerror(r)
     868          20 :                      + " (errno: "
     869          40 :                      + std::to_string(e)
     870          20 :                      + " -- "
     871          30 :                      + strerror(e)
     872          10 :                      + ").");
     873          10 :             return;
     874             :         }
     875         302 :         std::shared_ptr<struct addrinfo> mask_ai(masklist, addrinfo_deleter);
     876             : 
     877         152 :         if(cidr.is_ipv4())
     878             :         {
     879          76 :             if(masklist->ai_family != AF_INET)
     880             :             {
     881             :                 // this one happens when the user does not put the '[...]'
     882             :                 // around an IPv6 address
     883             :                 //
     884           2 :                 emit_error("Incompatible address between the address and"
     885           1 :                           " mask address (first was an IPv4 second an IPv6).");
     886           1 :                 return;
     887             :             }
     888          75 :             if(masklist->ai_addrlen != sizeof(struct sockaddr_in))
     889             :             {
     890             :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
     891             :                         + std::to_string(masklist->ai_addrlen)          // LCOV_EXCL_LINE
     892             :                         + ", expected"                                  // LCOV_EXCL_LINE
     893             :                         + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
     894             :                         + ").");                                        // LCOV_EXCL_LINE
     895             :                 return;                                                 // LCOV_EXCL_LINE
     896             :             }
     897          75 :             memcpy(mask_bits + 12, &reinterpret_cast<struct sockaddr_in *>(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
     898             :         }
     899             :         else //if(!cidr.is_ipv4())
     900             :         {
     901          76 :             if(masklist->ai_family != AF_INET6)
     902             :             {
     903             :                 // this one happens if the user puts the '[...]'
     904             :                 // around an IPv4 address
     905             :                 //
     906           2 :                 emit_error("Incompatible address between the address"
     907           1 :                           " and mask address (first was an IPv6 second an IPv4).");
     908           1 :                 return;
     909             :             }
     910          75 :             if(masklist->ai_addrlen != sizeof(struct sockaddr_in6))
     911             :             {
     912             :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
     913             :                          + std::to_string(masklist->ai_addrlen)         // LCOV_EXCL_LINE
     914             :                          + ", expected "                                // LCOV_EXCL_LINE
     915             :                          + std::to_string(sizeof(struct sockaddr_in6))  // LCOV_EXCL_LINE
     916             :                          + ").");                                       // LCOV_EXCL_LINE
     917             :                 return;                                                 // LCOV_EXCL_LINE
     918             :             }
     919          75 :             memcpy(mask_bits, &reinterpret_cast<struct sockaddr_in6 *>(masklist->ai_addr)->sin6_addr.s6_addr, 16);
     920             :         }
     921             :     }
     922             : 
     923         314 :     cidr.set_mask(mask_bits);
     924             : }
     925             : 
     926             : 
     927             : 
     928             : 
     929             : 
     930             : 
     931             : 
     932             : 
     933           6 : }
     934             : // snap_addr namespace
     935             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12