LCOV - code coverage report
Current view: top level - libaddr - addr_parser.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 475 508 93.5 %
Date: 2022-03-01 21:05:13 Functions: 32 37 86.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/libaddr
       4             : //
       5             : // Permission is hereby granted, free of charge, to any person obtaining a
       6             : // copy of this software and associated documentation files (the
       7             : // "Software"), to deal in the Software without restriction, including
       8             : // without limitation the rights to use, copy, modify, merge, publish,
       9             : // distribute, sublicense, and/or sell copies of the Software, and to
      10             : // permit persons to whom the Software is furnished to do so, subject to
      11             : // the following conditions:
      12             : //
      13             : // The above copyright notice and this permission notice shall be included
      14             : // in all copies or substantial portions of the Software.
      15             : //
      16             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      17             : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      18             : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      19             : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      20             : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      21             : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      22             : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      23             : 
      24             : 
      25             : /** \file
      26             :  * \brief The implementation of the IP address parser.
      27             :  *
      28             :  * This function is used to parse IP addresses from a string to a
      29             :  * vector of ranges.
      30             :  */
      31             : 
      32             : // self
      33             : //
      34             : #include    "libaddr/addr_parser.h"
      35             : #include    "libaddr/addr_exception.h"
      36             : 
      37             : 
      38             : // advgetopt library
      39             : //
      40             : #include    <advgetopt/validator_integer.h>
      41             : 
      42             : 
      43             : // C++ library
      44             : //
      45             : #include    <algorithm>
      46             : #include    <iostream>
      47             : 
      48             : 
      49             : // C library
      50             : //
      51             : #include    <ifaddrs.h>
      52             : #include    <netdb.h>
      53             : 
      54             : 
      55             : // last include
      56             : //
      57             : #include    <snapdev/poison.h>
      58             : 
      59             : 
      60             : 
      61             : namespace addr
      62             : {
      63             : 
      64             : 
      65             : namespace
      66             : {
      67             : 
      68             : 
      69             : /** \brief Delete an addrinfo structure.
      70             :  *
      71             :  * This deleter is used to make sure all the addinfo get released when
      72             :  * an exception occurs or the function using such exists.
      73             :  *
      74             :  * \param[in] ai  The addrinfo structure to free.
      75             :  */
      76      131992 : void addrinfo_deleter(addrinfo * ai)
      77             : {
      78      131992 :     freeaddrinfo(ai);
      79      131992 : }
      80             : 
      81             : 
      82             : }
      83             : 
      84             : 
      85             : 
      86             : 
      87             : 
      88             : /** \brief Initialize an addr_parser object.
      89             :  *
      90             :  * This function initializes the addr_parser object.
      91             :  *
      92             :  * Especially, it calls the set_allow() functions a few times to set
      93             :  * flags which are expected to be true on initialization.
      94             :  */
      95      131953 : addr_parser::addr_parser()
      96             : {
      97             :     // allow addresses & DNS lookups by default
      98             :     //
      99      131953 :     set_allow(allow_t::ALLOW_ADDRESS, true);
     100      131953 :     set_allow(allow_t::ALLOW_ADDRESS_LOOKUP, true);
     101             : 
     102             :     // allow port after address
     103             :     //
     104      131953 :     set_allow(allow_t::ALLOW_PORT, true);
     105      131953 : }
     106             : 
     107             : 
     108             : /** \brief Set the default IP addresses.
     109             :  *
     110             :  * This function sets the default IP addresses to be used by the parser
     111             :  * when the input string of the parse() function does not include an IP
     112             :  * address.
     113             :  *
     114             :  * The \p address parameter cannot include a port. See
     115             :  * set_default_port() as a way to change the default port.
     116             :  *
     117             :  * The function expects either an IPv4 or an IPv6 address. It can be
     118             :  * called twice if you need to define both types of addresses (which
     119             :  * is often a good idea.)
     120             :  *
     121             :  * For example, the following input is considered valid when a default
     122             :  * address is defined:
     123             :  *
     124             :  * \code
     125             :  *      parser.parse(":123");
     126             :  * \endcode
     127             :  *
     128             :  * It returns the default address and port 123. Note that by default
     129             :  * an address is mandatory unless a default address is defined.
     130             :  *
     131             :  * To prevent the parser from working when no default and no address
     132             :  * are specified, then make sure to set the REQUIRED_ADDRESS allow
     133             :  * flag to true:
     134             :  *
     135             :  * \code
     136             :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, true);
     137             :  *      // now address is mandatory
     138             :  * \endcode
     139             :  *
     140             :  * To completely prevent the use of an address in an input string, set
     141             :  * the `ADDRESS` and `REQUIRED_ADDRESS` values to false:
     142             :  *
     143             :  * \code
     144             :  *      parser.set_allow(parser.allow_t::ALLOW_ADDRESS,          false);
     145             :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, false);
     146             :  * \endcode
     147             :  *
     148             :  * To remove both default IP addresses, call this function with an empty
     149             :  * string:
     150             :  *
     151             :  * \code
     152             :  *      parser.set_default_address(std::string());
     153             :  * \endcode
     154             :  *
     155             :  * \todo
     156             :  * Consider saving the default IPs as addr structures and allow such
     157             :  * as input (keep in mind that the default could also represent multiple
     158             :  * addresses).
     159             :  *
     160             :  * \param[in] addr  The new address.
     161             :  */
     162         227 : void addr_parser::set_default_address(std::string const & address)
     163             : {
     164         227 :     if(address.empty())
     165             :     {
     166           3 :         f_default_address4.clear();
     167           3 :         f_default_address6.clear();
     168             :     }
     169         224 :     else if(address[0] == '[')
     170             :     {
     171             :         // remove the '[' and ']'
     172             :         //
     173           5 :         if(address.back() != ']')
     174             :         {
     175           3 :             throw addr_invalid_argument("an IPv6 address starting with '[' must end with ']'.");
     176             :         }
     177           2 :         f_default_address6 = address.substr(1, address.length() - 2);
     178             :     }
     179         219 :     else if(address.find(':') != std::string::npos)
     180             :     {
     181         103 :         f_default_address6 = address;
     182             :     }
     183             :     else
     184             :     {
     185         116 :         f_default_address4 = address;
     186             :     }
     187         224 : }
     188             : 
     189             : 
     190             : /** \brief Retrieve the default IP address for IPv4 parsing.
     191             :  *
     192             :  * This function returns a copy of the default IP address used by
     193             :  * the parser when the input string does not include an IP address.
     194             :  *
     195             :  * If the function returns an empty string, then no default address
     196             :  * is defined.
     197             :  *
     198             :  * \return The default IPv4 address.
     199             :  *
     200             :  * \sa get_default_address6()
     201             :  * \sa set_default_address()
     202             :  */
     203         115 : std::string const & addr_parser::get_default_address4() const
     204             : {
     205         115 :     return f_default_address4;
     206             : }
     207             : 
     208             : 
     209             : /** \brief Retrieve the default IP address for IPv4 parsing.
     210             :  *
     211             :  * This function returns a copy of the default IP address used by
     212             :  * the parser when the input string does not include an IP address.
     213             :  *
     214             :  * If the function returns an empty string, then no default address
     215             :  * is defined.
     216             :  *
     217             :  * \return The default IPv6 address, without square brackets.
     218             :  *
     219             :  * \sa get_default_address4()
     220             :  * \sa set_default_address()
     221             :  */
     222         115 : std::string const & addr_parser::get_default_address6() const
     223             : {
     224         115 :     return f_default_address6;
     225             : }
     226             : 
     227             : 
     228             : /** \brief Define the default port.
     229             :  *
     230             :  * This function is used to define the default port to use in the address
     231             :  * parser object. By default this is set to -1 meaning: no default port.
     232             :  *
     233             : * This function accepts any port number from 0 to 65535. It also accepts
     234             :  * -1 to reset the port back to "no default".
     235             :  *
     236             :  * To prevent the parser from working when no default and no port
     237             :  * are specified, then make sure to set the REQUIRED_PORT allow
     238             :  * flag to true:
     239             :  *
     240             :  * \code
     241             :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, true);
     242             :  *      // now port is mandatory
     243             :  * \endcode
     244             :  *
     245             :  * To completely prevent the use of a port in an input string, set
     246             :  * the `PORT` and `REQUIRED_PORT` values to false:
     247             :  *
     248             :  * \code
     249             :  *      parser.set_allow(parser.allow_t::ALLOW_PORT,          false);
     250             :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, false);
     251             :  * \endcode
     252             :  *
     253             :  * \exception addr_invalid_argument_exception
     254             :  * If the port number is out of range, then this expcetion is raised.
     255             :  * The allowed range for a port is 0 to 65535. This function also
     256             :  * accepts -1 meaning that no default port is specified.
     257             :  *
     258             :  * \param[in] port  The new default port.
     259             :  */
     260          84 : void addr_parser::set_default_port(int const port)
     261             : {
     262          84 :     if(port < -1
     263          70 :     || port > 65535)
     264             :     {
     265          25 :         throw addr_invalid_argument("addr_parser::set_default_port(): port must be in range [-1..65535].");
     266             :     }
     267             : 
     268          59 :     f_default_port = port;
     269          59 : }
     270             : 
     271             : 
     272             : /** \brief Retrieve the default port.
     273             :  *
     274             :  * This function retrieves the default port as defined by the
     275             :  * set_default_port() function.
     276             :  */
     277          76 : int addr_parser::get_default_port() const
     278             : {
     279          76 :     return f_default_port;
     280             : }
     281             : 
     282             : 
     283             : /** \brief Define the default mask.
     284             :  *
     285             :  * This function is used to define the default mask. Note that the
     286             :  * default mask will not be used at all if the allow_t::ALLOW_MASK allow
     287             :  * flag is not set to true:
     288             :  *
     289             :  * \code
     290             :  *      parser.set_allow(parser.allow_t::ALLOW_MASK, true);
     291             :  *      parser.set_default_mask("255.255.0.0");
     292             :  *      parser.set_default_mask("[ffff:ffff:ffff::]");
     293             :  * \endcode
     294             :  *
     295             :  * The IPv6 mask does not require the square brackets (`'['` and `']'`).
     296             :  *
     297             :  * To remove the default mask, call this function with an empty
     298             :  * string:
     299             :  *
     300             :  * \code
     301             :  *      parser.set_default_mask(std::string());
     302             :  * \endcode
     303             :  *
     304             :  * \note
     305             :  * As you can see, here we expect the mask to be a string. This is because
     306             :  * it gets parsed as if it came from the input string of the parser. This
     307             :  * also means that if the mask is invalid, it will not be detected until
     308             :  * you attempt to parse an input string that does not include a mask and
     309             :  * the default gets used.
     310             :  *
     311             :  * \todo
     312             :  * Add a check of the default mask when it gets set so we can throw on
     313             :  * errors and that way it is much more likely that programmers can fix
     314             :  * their errors early. (Actually by pre-parsing we could save it as
     315             :  * an addr and allow a `set_default_mask(addr ...)`!)
     316             :  *
     317             :  * \param[in] mask  The mask to use by default.
     318             :  */
     319         111 : void addr_parser::set_default_mask(std::string const & mask)
     320             : {
     321         111 :     if(mask.empty())
     322             :     {
     323           3 :         f_default_mask4.clear();
     324           3 :         f_default_mask6.clear();
     325             :     }
     326         108 :     else if(mask[0] == '[')
     327             :     {
     328             :         // remove the '[' and ']'
     329             :         //
     330          30 :         if(mask.back() != ']')
     331             :         {
     332           3 :             throw addr_invalid_argument("an IPv6 mask starting with '[' must end with ']'.");
     333             :         }
     334          27 :         f_default_mask6 = mask.substr(1, mask.length() - 2);
     335             :     }
     336          78 :     else if(mask.find(':') != std::string::npos)
     337             :     {
     338          25 :         f_default_mask6 = mask;
     339             :     }
     340             :     else
     341             :     {
     342          53 :         f_default_mask4 = mask;
     343             :     }
     344         108 : }
     345             : 
     346             : 
     347             : /** \brief Retrieve the default mask.
     348             :  *
     349             :  * This function returns a reference to the mask as set by the
     350             :  * set_default_mask() function. The value is an empty string by
     351             :  * default.
     352             :  *
     353             :  * The default mask will be used if no mask is specified in the
     354             :  * input string to the parse() function. When no default mask
     355             :  * is defined, the mask is set to all 1s.
     356             :  *
     357             :  * \note
     358             :  * The default mask is a string, not a binary mask. It gets
     359             :  * converted by the parser at the time it is required.
     360             :  *
     361             :  * \return The default mask.
     362             :  *
     363             :  * \sa get_default_mask6()
     364             :  * \sa set_default_mask()
     365             :  */
     366          12 : std::string const & addr_parser::get_default_mask4() const
     367             : {
     368          12 :     return f_default_mask4;
     369             : }
     370             : 
     371             : 
     372             : /** \brief Retrieve the default mask.
     373             :  *
     374             :  * This function returns a reference to the mask as set by the
     375             :  * set_default_mask() function. The value is an empty string by
     376             :  * default.
     377             :  *
     378             :  * The default mask will be used if no mask is specified in the
     379             :  * input string to the parse() function. When no default mask
     380             :  * is defined, the mask is set to all 1s.
     381             :  *
     382             :  * \note
     383             :  * The default mask is a string, not a binary mask. It gets
     384             :  * converted by the parser at the time it is required.
     385             :  *
     386             :  * \return The default mask.
     387             :  *
     388             :  * \sa get_default_mask4()
     389             :  * \sa set_default_mask()
     390             :  */
     391          12 : std::string const & addr_parser::get_default_mask6() const
     392             : {
     393          12 :     return f_default_mask6;
     394             : }
     395             : 
     396             : 
     397             : /** \brief Set the protocol to use to filter addresses.
     398             :  *
     399             :  * This function sets the protocol as one of the following:
     400             :  *
     401             :  * \li "ip" -- only return IP address supporting the IP protocol
     402             :  * (this is offered because getaddrinfo() may return such IP addresses.)
     403             :  * \li "tcp" -- only return IP address supporting TCP
     404             :  * \li "udp" -- only return IP address supporting UDP
     405             :  *
     406             :  * Any other value is refused. To reset the protocol to the default,
     407             :  * which is "do not filter by protocol", call the clear_protocol().
     408             :  *
     409             :  * \exception addr_invalid_argument_exception
     410             :  * If the string passed to this function is not one of the acceptable
     411             :  * protocols (ip, tcp, udp), then this exception is raised.
     412             :  *
     413             :  * \param[in] protocol  The default protocol for this parser.
     414             :  *
     415             :  * \sa clear_protocol()
     416             :  * \sa get_protocol()
     417             :  */
     418         118 : void addr_parser::set_protocol(std::string const & protocol)
     419             : {
     420         118 :     if(protocol == "ip")
     421             :     {
     422           2 :         f_protocol = IPPROTO_IP;
     423             :     }
     424         116 :     else if(protocol == "tcp")
     425             :     {
     426         107 :         f_protocol = IPPROTO_TCP;
     427             :     }
     428           9 :     else if(protocol == "udp")
     429             :     {
     430           6 :         f_protocol = IPPROTO_UDP;
     431             :     }
     432             :     else
     433             :     {
     434             :         // not a protocol we support
     435             :         //
     436             :         throw addr_invalid_argument(
     437           6 :                   std::string("unknown protocol \"")
     438           9 :                 + protocol
     439           9 :                 + "\", expected \"tcp\" or \"udp\".");
     440             :     }
     441         115 : }
     442             : 
     443             : 
     444             : /** \brief Set the protocol to use to filter addresses.
     445             :  *
     446             :  * This function sets the protocol as one of the following:
     447             :  *
     448             :  * \li IPPROTO_IP -- only return IP address supporting the IP protocol
     449             :  * (this is offered because getaddrinfo() may return such IP addresses.)
     450             :  * \li IPPROTO_TCP -- only return IP address supporting TCP
     451             :  * \li IPPROTO_UDP -- only return IP address supporting UDP
     452             :  *
     453             :  * Any other value is refused. To reset the protocol to the default,
     454             :  * which is "do not filter by protocol", call the clear_protocol().
     455             :  *
     456             :  * \exception addr_invalid_argument_exception
     457             :  * If the string passed to this function is not one of the acceptable
     458             :  * protocols (ip, tcp, udp), then this exception is raised.
     459             :  *
     460             :  * \param[in] protocol  The default protocol for this parser.
     461             :  *
     462             :  * \sa clear_protocol()
     463             :  * \sa get_protocol()
     464             :  */
     465      132021 : void addr_parser::set_protocol(int const protocol)
     466             : {
     467             :     // make sure that's a protocol we support
     468             :     //
     469      132021 :     switch(protocol)
     470             :     {
     471      131821 :     case IPPROTO_IP:
     472             :     case IPPROTO_TCP:
     473             :     case IPPROTO_UDP:
     474      131821 :         break;
     475             : 
     476         200 :     default:
     477             :         throw addr_invalid_argument(
     478         400 :                   std::string("unknown protocol \"")
     479         800 :                 + std::to_string(protocol)
     480         600 :                 + "\", expected \"tcp\" or \"udp\".");
     481             : 
     482             :     }
     483             : 
     484      131821 :     f_protocol = protocol;
     485      131821 : }
     486             : 
     487             : 
     488             : /** \brief Use this function to reset the protocol back to "no default."
     489             :  *
     490             :  * This function sets the protocol to -1 (which is something you cannot
     491             :  * do by calling the set_protocol() functions above.)
     492             :  *
     493             :  * The -1 special value means that the protocol is not defined, that
     494             :  * there is no default. In most cases this means all the addresses
     495             :  * that match, ignoring the protocol, will be returned by the parse()
     496             :  * function.
     497             :  *
     498             :  * \sa set_protocol()
     499             :  * \sa get_protocol()
     500             :  */
     501           3 : void addr_parser::clear_protocol()
     502             : {
     503           3 :     f_protocol = -1;
     504           3 : }
     505             : 
     506             : 
     507             : /** \brief Retrieve the protocol as defined by the set_protocol().
     508             :  *
     509             :  * This function returns the protocol number as defined by the
     510             :  * set_protocol.
     511             :  *
     512             :  * When defined, the protocol is used whenever we call the
     513             :  * getaddrinfo() function. In general, this means the IP addresses
     514             :  * returned will have  to match that protocol.
     515             :  *
     516             :  * This function may return -1. The value -1 is used as "do not
     517             :  * filter by protocol". The protocol can be set to -1 by calling
     518             :  * the clear_protocol() function.
     519             :  *
     520             :  * \return The parser default protocol.
     521             :  *
     522             :  * \sa set_protocol()
     523             :  * \sa clear_protocol()
     524             :  */
     525         216 : int addr_parser::get_protocol() const
     526             : {
     527         216 :     return f_protocol;
     528             : }
     529             : 
     530             : 
     531             : /** \brief Change the set of flags defining the sorting order.
     532             :  *
     533             :  * The parser, once done parsing all the input, will sort the addresses
     534             :  * according to these flags.
     535             :  *
     536             :  * By default, it does not re-arrange the order in which the addresses
     537             :  * were defined.
     538             :  *
     539             :  * The parser is capable of sorting by IP type (IPv6 or IPv4 first),
     540             :  * and simply by IP addresses. It can also merge adjacent or overlapping
     541             :  * ranges into a single range.
     542             :  *
     543             :  * \exception addr_invalid_argument
     544             :  * This exception is raised you set SORT_IPV6_FIRST and SORT_IPV4_FIRST
     545             :  * at the same time because these flags are mutually exclusive.
     546             :  *
     547             :  * \param[in] sort  The sort parameters.
     548             :  *
     549             :  * \sa get_sort_order()
     550             :  */
     551           0 : void addr_parser::set_sort_order(sort_t const sort)
     552             : {
     553           0 :     if((sort & (SORT_IPV6_FIRST | SORT_IPV4_FIRST)) == (SORT_IPV6_FIRST | SORT_IPV4_FIRST))
     554             :     {
     555           0 :         throw addr_invalid_argument("addr_parser::set_sort_order(): flags SORT_IPV6_FIRST and SORT_IPV4_FIRST are mutually exclusive.");
     556             :     }
     557             : 
     558           0 :     f_sort = sort;
     559           0 : }
     560             : 
     561             : 
     562             : /** \brief Get the flags defining the sort order of the parser.
     563             :  *
     564             :  * This function returns the sort order of the parser as a set of flags.
     565             :  *
     566             :  * By default this value is set to NO_SORT meaning that the input is
     567             :  * kept as is.
     568             :  *
     569             :  * \return The sort order flags.
     570             :  *
     571             :  * \sa set_sort_order()
     572             :  */
     573           0 : sort_t addr_parser::get_sort_order() const
     574             : {
     575           0 :     return f_sort;
     576             : }
     577             : 
     578             : 
     579             : /** \brief Set or clear allow flags in the parser.
     580             :  *
     581             :  * This parser has a set of flags it uses to know whether the input
     582             :  * string can include certain things such as a port or a mask.
     583             :  *
     584             :  * This function is used to allow or require certain parameters and
     585             :  * to disallow others.
     586             :  *
     587             :  * By default, the ADDRESS and PORT flags are set, meaning that an
     588             :  * address and a port can appear, but either or both are optinal.
     589             :  * If unspecified, then the default will be used. If not default
     590             :  * is defined, then the parser may fail in this situation.
     591             :  *
     592             :  * One problem is that we include contradictory syntatical features.
     593             :  * The parser supports lists of addresses separated by commas and
     594             :  * lists of ports separated by commas. Both are not supported
     595             :  * simultaneously. This means you want to allow multiple addresses
     596             :  * separated by commas, the function makes sure that the multiple
     597             :  * port separated by commas support is turned off.
     598             :  *
     599             :  * \li `ADDRESS` -- the IP address is allowed, but optional
     600             :  * \li `REQUIRED_ADDRESS` -- the IP address is mandatory
     601             :  * \li `PORT` -- the port is allowed, but optional
     602             :  * \li `REQUIRED_PORT` -- the port is mandatory
     603             :  * \li `MASK` -- the mask is allowed, but optional
     604             :  * \li `MULTI_ADDRESSES_COMMAS` -- the input can have multiple addresses
     605             :  * separated by commas (prevents MULTI_PORTS_COMMAS)
     606             :  * \li `MULTI_ADDRESSES_SPACES` -- the input can have multiple addresses
     607             :  * separated by spaces
     608             :  * \li `MULTI_PORTS_SEMICOLONS` -- the input can  have multiple ports
     609             :  * separated by semicolons _NOT IMPLEMENTED YET_
     610             :  * \li `MULTI_PORTS_COMMAS` -- the input can have multiple ports separated
     611             :  * by commas (prevents MULTI_ADDRESSES_COMMAS) _NOT IMPLEMENTED YET_
     612             :  * \li `PORT_RANGE` -- the input supports port ranges (p1-p2) _NOT
     613             :  * IMPLEMENTED YET_
     614             :  * \li `ADDRESS_RANGE` -- the input supports address ranges (addr-addr) _NOT
     615             :  * IMPLEMENTED YET_
     616             :  *
     617             :  * The `MULTI_ADDRESSES_COMMAS` and `MULTI_ADDRESSES_SPACES` can be used
     618             :  * together in which case any number of both characters are accepted
     619             :  * between addresses.
     620             :  *
     621             :  * Note that the `MULTI_ADDRESSES_COMMAS` and `MULTI_PORTS_COMMAS` are
     622             :  * mutually exclusive. The last set_allow() counts as the one you are
     623             :  * interested in.
     624             :  *
     625             :  * \param[in] flag  The flag to set or clear.
     626             :  * \param[in] allow  Whether to allow (true) or disallow (false).
     627             :  *
     628             :  * \sa get_allow()
     629             :  */
     630      396412 : void addr_parser::set_allow(allow_t const flag, bool const allow)
     631             : {
     632      396412 :     if(flag < static_cast<allow_t>(0)
     633      396392 :     || flag >= allow_t::ALLOW_max)
     634             :     {
     635          40 :         throw addr_invalid_argument("addr_parser::set_allow(): flag has to be one of the valid flags.");
     636             :     }
     637             : 
     638      396372 :     f_flags[static_cast<int>(flag)] = allow;
     639             : 
     640             :     // if we just set a certain flag, others may need to go to false
     641             :     //
     642      396372 :     if(allow)
     643             :     {
     644             :         // we can only support one type of commas
     645             :         //
     646      396347 :         switch(flag)
     647             :         {
     648           5 :         case allow_t::ALLOW_MULTI_ADDRESSES_COMMAS:
     649           5 :             f_flags[static_cast<int>(allow_t::ALLOW_MULTI_PORTS_COMMAS)] = false;
     650           5 :             break;
     651             : 
     652           2 :         case allow_t::ALLOW_MULTI_PORTS_COMMAS:
     653           2 :             f_flags[static_cast<int>(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS)] = false;
     654           2 :             break;
     655             : 
     656      396340 :         default:
     657      396340 :             break;
     658             : 
     659             :         }
     660             :     }
     661      396372 : }
     662             : 
     663             : 
     664             : /** \brief Retrieve the current statius of an allow flag.
     665             :  *
     666             :  * This function returns the current status of the allow flags.
     667             :  *
     668             :  * By default, the `ADDRESS` and `PORT` flags are set to true.
     669             :  * All the other flags are set to false.
     670             :  *
     671             :  * You may change the value of an allow flag by calling the
     672             :  * set_allow() function.
     673             :  *
     674             :  * \param[in] flag  Which flag is to be checked.
     675             :  *
     676             :  * \return The value of the flag: true or false.
     677             :  *
     678             :  * \sa set_allow()
     679             :  */
     680      857275 : bool addr_parser::get_allow(allow_t const flag) const
     681             : {
     682      857275 :     if(flag < static_cast<allow_t>(0)
     683      857265 :     || flag >= allow_t::ALLOW_max)
     684             :     {
     685          20 :         throw addr_invalid_argument("addr_parser::get_allow(): flag has to be one of the valid flags.");
     686             :     }
     687             : 
     688      857255 :     return f_flags[static_cast<int>(flag)];
     689             : }
     690             : 
     691             : 
     692             : /** \brief Check whether errors were registered so far.
     693             :  *
     694             :  * This function returns true if the system detected errors in one
     695             :  * of the previous calls to parse(). The flag can be cleared using
     696             :  * the clear_errors() function.
     697             :  *
     698             :  * On construction and after a call to clear_error(), this flag is
     699             :  * always false. If you are to call parser() multiple times with
     700             :  * the same addr_parser object, then you want to make sure to call
     701             :  * the clear_errors() function before calling the parse() function.
     702             :  * Otherwise you won't know whether errors occurred in a earlier
     703             :  * or later call.
     704             :  *
     705             :  * \code
     706             :  *      // first time, not required
     707             :  *      parser.parse(...);
     708             :  *      ...
     709             :  *
     710             :  *      // next time, required
     711             :  *      parser.clear_errors();
     712             :  *      parser.parse(...);
     713             :  *      ...
     714             :  * \endcode
     715             :  *
     716             :  * \return true if errors were generated.
     717             :  */
     718      131832 : bool addr_parser::has_errors() const
     719             : {
     720      131832 :     return !f_error.empty();
     721             : }
     722             : 
     723             : 
     724             : /** \brief Emit an error and save it in this class.
     725             :  *
     726             :  * This function adds the message to the error string part of this
     727             :  * object. A newline is also added at the end of the message.
     728             :  *
     729             :  * Next the function increments the error counter.
     730             :  *
     731             :  * \note
     732             :  * You are expected to emit one error at a time. If you want to
     733             :  * emit several messages in a row, that will work and properly
     734             :  * count each message.
     735             :  *
     736             :  * \param[in] msg  The message to add to the parser error messages.
     737             :  *
     738             :  * \sa error_messages()
     739             :  */
     740          65 : void addr_parser::emit_error(std::string const & msg)
     741             : {
     742          65 :     f_error += msg;
     743          65 :     f_error += "\n";
     744          65 :     ++f_error_count;
     745          65 : }
     746             : 
     747             : 
     748             : /** \brief Return the current error messages.
     749             :  *
     750             :  * The error messages are added to the addr_parser using the
     751             :  * emit_error() function.
     752             :  *
     753             :  * This function does not clear the list of error messages.
     754             :  * To do that, call the clear_errors() function.
     755             :  *
     756             :  * The number of messages can be determined by counting the
     757             :  * number of "\n" characters in the string. The error_count()
     758             :  * will return that same number (assuming no message included
     759             :  * a '\n' character when emit_error() was called.)
     760             :  *
     761             :  * \return A string with the list of messages.
     762             :  *
     763             :  * \sa emit_error()
     764             :  * \sa clear_errors()
     765             :  */
     766          65 : std::string const & addr_parser::error_messages() const
     767             : {
     768          65 :     return f_error;
     769             : }
     770             : 
     771             : 
     772             : /** \brief Return the number of error messages that were emitted.
     773             :  *
     774             :  * Each time the emit_error() function is called, the error
     775             :  * counter is incremented by 1. This function returns that
     776             :  * error counter.
     777             :  *
     778             :  * The clear_errors() function can be used to clear the
     779             :  * counter back to zero.
     780             :  *
     781             :  * \return The number of errors that were emitted so far.
     782             :  *
     783             :  * \sa emit_error()
     784             :  */
     785          65 : int addr_parser::error_count() const
     786             : {
     787          65 :     return f_error_count;
     788             : }
     789             : 
     790             : 
     791             : /** \brief Clear the error message and error counter.
     792             :  *
     793             :  * This function clears all the error messages and reset the
     794             :  * counter back to zero. In order words, it will be possible
     795             :  * to tell how many times the emit_error() was called since
     796             :  * the start or the last clear_errors() call.
     797             :  *
     798             :  * To retrieve a copy of the error counter, use the error_count()
     799             :  * function.
     800             :  *
     801             :  * \sa error_count()
     802             :  */
     803           5 : void addr_parser::clear_errors()
     804             : {
     805           5 :     f_error.clear();
     806           5 :     f_error_count = 0;
     807           5 : }
     808             : 
     809             : 
     810             : /** \brief Parse a string of addresses, ports, and masks.
     811             :  *
     812             :  * This function is used to parse the list of addresses defined
     813             :  * in the \p in parameter.
     814             :  *
     815             :  * One address is composed of one to three elements:
     816             :  *
     817             :  * \code
     818             :  *          [ address ] [ ':' port ] [ '/' mask ]
     819             :  * \endcode
     820             :  *
     821             :  * Although all three elements are optional (at least by default),
     822             :  * a valid address is expected to include at least one of the
     823             :  * three elements. (i.e. an empty string is just skipped silently.)
     824             :  *
     825             :  * ### Multiple Addresses
     826             :  *
     827             :  * Multiple addresses can be defined if at least one of the
     828             :  * `MULTI_ADDRESSES_COMMAS` or `MULTI_ADDRESSES_SPACES` allow
     829             :  * flags is set to true.
     830             :  *
     831             :  * Next comes `MULTI_ADDRESSES_COMMAS`: if set to true, addresses
     832             :  * must be separated by commas and spaces are not allowed.
     833             :  *
     834             :  * Finally we have `MULTI_ADDRESSES_SPACES`. If that one is true, then
     835             :  * addresses must be separated by spaces and commas are not allowed.
     836             :  *
     837             :  * ### Make Address Field Required
     838             :  *
     839             :  * To make the address field a required field, set the
     840             :  * `REQUIRED_ADDRESS` flag (see set_allow()) to true and do not define a
     841             :  * default address (see set_default_address()).
     842             :  *
     843             :  * ### Make Port Field Required
     844             :  *
     845             :  * To make the port field a required fiel, set the `REQUIRED_PORT`
     846             :  * flag (see set_allow()) to true and do not define a default port
     847             :  * (see set_default_port()).
     848             :  *
     849             :  * ### Allow Mask
     850             :  *
     851             :  * The mask cannot be made mandatory. However, you have to set
     852             :  * the `MASK` flag to true to allow it. By default it is not
     853             :  * allowed.
     854             :  *
     855             :  * ### Ranges
     856             :  *
     857             :  * Ranges are not yet implemented.
     858             :  *
     859             :  * ### Sort
     860             :  *
     861             :  * After the function parsed all the input, it sorts the results in
     862             :  * the vector of ranges. Ranges are sorted using the addr_range::compare()
     863             :  * function. Sorting can also be used to merge ranges. So if two ranges
     864             :  * have an overlap or are adjacent, the union of those two ranges will
     865             :  * be kept and the two ranges are otherwise removed from the result.
     866             :  *
     867             :  * The sort is particularly useful if you first want to connect with
     868             :  * IPv6 addresses instead of IPv4 (which is the current expected behavior
     869             :  * of your services and tools).
     870             :  *
     871             :  * \param[in] in  The input string to be parsed.
     872             :  *
     873             :  * \return A vector of address ranges, see has_errors() to determine whether
     874             :  * errors occurred while parsing the input.
     875             :  *
     876             :  * \sa has_errors()
     877             :  */
     878      131841 : addr_range::vector_t addr_parser::parse(std::string const & in)
     879             : {
     880      131841 :     addr_range::vector_t result;
     881             : 
     882      263682 :     if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS)
     883      131841 :     && get_allow(allow_t::ALLOW_MULTI_ADDRESSES_SPACES))
     884             :     {
     885           2 :         std::string const comma_space(", ");
     886           1 :         std::string::size_type s(0);
     887          17 :         while(s < in.length())
     888             :         {
     889           8 :             auto const it(std::find_first_of(in.begin() + s, in.end(), comma_space.begin(), comma_space.end()));
     890           8 :             std::string::size_type const e(it == in.end() ? in.length() : it - in.begin());
     891           8 :             if(e > s)
     892             :             {
     893           3 :                 parse_cidr(in.substr(s, e - s), result);
     894             :             }
     895           8 :             s = e + 1;
     896             :         }
     897             :     }
     898      263680 :     else if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS)
     899      131840 :          || get_allow(allow_t::ALLOW_MULTI_ADDRESSES_SPACES))
     900             :     {
     901           2 :         char const sep(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS) ? ',' : ' ');
     902           2 :         std::string::size_type s(0);
     903          24 :         while(s < in.length())
     904             :         {
     905          11 :             std::string::size_type e(in.find(sep, s));
     906          11 :             if(e == std::string::npos)
     907             :             {
     908           2 :                 e = in.length();
     909             :             }
     910          11 :             if(e > s)
     911             :             {
     912           6 :                 parse_cidr(in.substr(s, e - s), result);
     913             :             }
     914          11 :             s = e + 1;
     915             :         }
     916             :     }
     917             :     else
     918             :     {
     919      131838 :         parse_cidr(in, result);
     920             :     }
     921             : 
     922      131841 :     if((f_sort & SORT_FULL) != 0)
     923             :     {
     924           0 :         std::sort(
     925             :               result.begin()
     926             :             , result.end()
     927           0 :             , [](auto const & a, auto const & b)
     928             :             {
     929           0 :                 switch(a.compare(b))
     930             :                 {
     931           0 :                 case compare_t::COMPARE_SMALLER:
     932             :                 case compare_t::COMPARE_OVERLAP_SMALL_VS_LARGE:
     933             :                 case compare_t::COMPARE_INCLUDED:
     934             :                 case compare_t::COMPARE_IPV6_VS_IPV4:
     935             :                 case compare_t::COMPARE_SMALL_VS_LARGE:
     936             :                 case compare_t::COMPARE_FOLLOWS:
     937             :                 case compare_t::COMPARE_FIRST:
     938           0 :                     return true;
     939             : 
     940           0 :                 default:
     941           0 :                     return false;
     942             : 
     943             :                 }
     944             :             });
     945             :     }
     946             : 
     947      131841 :     if((f_sort & SORT_MERGE) != 0)
     948             :     {
     949           0 :         std::size_t max(result.size());
     950           0 :         for(std::size_t idx(0); idx < max - 1; ++idx)
     951             :         {
     952           0 :             addr_range const r(result[idx].union_if_possible(result[idx + 1]));
     953           0 :             if(!r.is_empty())
     954             :             {
     955             :                 // merge worked
     956             :                 //
     957           0 :                 result[idx] = r;
     958           0 :                 result.erase(result.begin() + idx + 1);
     959           0 :                 --max;
     960             :             }
     961             :         }
     962             :     }
     963             : 
     964      131841 :     if((f_sort & SORT_IPV4_FIRST) != 0)
     965             :     {
     966           0 :         std::sort(
     967             :               result.begin()
     968             :             , result.end()
     969           0 :             , [](auto const & a, auto const & b)
     970             :             {
     971           0 :                 return a.compare(b) == compare_t::COMPARE_IPV4_VS_IPV6;
     972           0 :             });
     973             :     }
     974      131841 :     else if((f_sort & SORT_IPV6_FIRST) != 0)
     975             :     {
     976           0 :         std::sort(
     977             :               result.begin()
     978             :             , result.end()
     979           0 :             , [](auto const & a, auto const & b)
     980             :             {
     981           0 :                 return a.compare(b) == compare_t::COMPARE_IPV6_VS_IPV4;
     982           0 :             });
     983             :     }
     984             : 
     985      131841 :     return result;
     986             : }
     987             : 
     988             : 
     989             : /** \brief Check one address.
     990             :  *
     991             :  * This function checks one address, although if it is a name, it could
     992             :  * represent multiple IP addresses.
     993             :  *
     994             :  * This function separate the address:port from the mask if the mask is
     995             :  * allowed. Then it parses the address:port part and the mask separately.
     996             :  *
     997             :  * \param[in] in  The address to parse.
     998             :  * \param[in,out] result  The list of resulting addresses.
     999             :  */
    1000      131847 : void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
    1001             : {
    1002      131847 :     if(get_allow(allow_t::ALLOW_MASK))
    1003             :     {
    1004             :         // check whether there is a mask
    1005             :         //
    1006         948 :         std::string mask;
    1007             : 
    1008         948 :         std::string address;
    1009         474 :         std::string::size_type const p(in.find('/'));
    1010         474 :         if(p != std::string::npos)
    1011             :         {
    1012         420 :             address = in.substr(0, p);
    1013         420 :             mask = in.substr(p + 1);
    1014             :         }
    1015             :         else
    1016             :         {
    1017          54 :             address = in;
    1018             :         }
    1019             : 
    1020         474 :         int const errcnt(f_error_count);
    1021             : 
    1022             :         // handle the address first
    1023             :         //
    1024         948 :         addr_range::vector_t addr_mask;
    1025         474 :         parse_address(address, mask, addr_mask);
    1026             : 
    1027             :         // now check for the mask
    1028             :         //
    1029         948 :         for(auto & am : addr_mask)
    1030             :         {
    1031         948 :             std::string m(mask);
    1032         474 :             if(m.empty())
    1033             :             {
    1034             :                 // the mask was not defined in the input, then adapt it to
    1035             :                 // the type of address we got in 'am'
    1036             :                 //
    1037          56 :                 if(am.get_from().is_ipv4())
    1038             :                 {
    1039          29 :                     m = f_default_mask4;
    1040             :                 }
    1041             :                 else
    1042             :                 {
    1043             :                     // parse_mask() expects '[...]' around IPv6 addresses
    1044             :                     //
    1045          27 :                     m = "[" + f_default_mask6 + "]";
    1046             :                 }
    1047             :             }
    1048             : 
    1049         474 :             parse_mask(m, am.get_from());
    1050             :         }
    1051             : 
    1052             :         // now append the list to the result if no errors occurred
    1053             :         //
    1054         474 :         if(errcnt == f_error_count)
    1055             :         {
    1056             :             // note: no need to test the SORT_NO_EMPTY since that was already
    1057             :             //       done when adding addresses to addr_mask
    1058             :             //
    1059         424 :             result.insert(result.end(), addr_mask.begin(), addr_mask.end());
    1060             :         }
    1061             :     }
    1062             :     else
    1063             :     {
    1064             :         // no mask allowed, if there is one, this call will fail
    1065             :         //
    1066      131373 :         parse_address(in, std::string(), result);
    1067             :     }
    1068      131847 : }
    1069             : 
    1070             : 
    1071             : /** \brief Parse one address.
    1072             :  *
    1073             :  * This function is called with one address. It determines whether we
    1074             :  * are dealing with an IPv4 or an IPv6 address and call the
    1075             :  * corresponding sub-function.
    1076             :  *
    1077             :  * An address is considered an IPv6 address if it starts with a '['
    1078             :  * character.
    1079             :  *
    1080             :  * \note
    1081             :  * The input cannot include a mask. It has to already have been
    1082             :  * removed.
    1083             :  *
    1084             :  * \note
    1085             :  * The mask parameter is only used to determine whether this function
    1086             :  * is being called with an IPv6 or not. It is otherwise ignored.
    1087             :  *
    1088             :  * \param[in] in  The input address eventually including a port.
    1089             :  * \param[in] mask  The mask used to determine whether we are dealing with
    1090             :  *                  an IPv6 or not.
    1091             :  * \param[in,out] result  The list of resulting addresses.
    1092             :  */
    1093      131847 : void addr_parser::parse_address(std::string const & in, std::string const & mask, addr_range::vector_t & result)
    1094             : {
    1095             :     // With our only supported format, ipv6 addresses must be between square
    1096             :     // brackets. The address may just be a mask in which case the '[' may
    1097             :     // not be at the very start (i.e. "/[ffff:ffff::]")
    1098             :     //
    1099      263694 :     if(in.empty()
    1100      131847 :     || in[0] == ':')    // if it start with ':' then there is no address
    1101             :     {
    1102             :         // if the address is empty, then use the mask to determine the
    1103             :         // type of IP address (note: if the address starts with ':'
    1104             :         // it is considered empty since an IPv6 would have a '[' at
    1105             :         // the start)
    1106             :         //
    1107         318 :         if(!mask.empty())
    1108             :         {
    1109         111 :             if(mask[0] == '[')
    1110             :             {
    1111             :                 // IPv6 parsing
    1112             :                 //
    1113           5 :                 parse_address6(in, result);
    1114             :             }
    1115             :             else
    1116             :             {
    1117             :                 // if the number is 33 or more, it has to be IPv6, otherwise
    1118             :                 // we cannot know...
    1119             :                 //
    1120         106 :                 int mask_count(0);
    1121         356 :                 for(char const * s(mask.c_str()); *s != '\0'; ++s)
    1122             :                 {
    1123         260 :                     if(*s >= '0' && *s <= '9')
    1124             :                     {
    1125         255 :                         mask_count = mask_count * 10 + *s - '0';
    1126         505 :                         if(mask_count > 1000)
    1127             :                         {
    1128             :                             // not valid
    1129             :                             //
    1130           5 :                             mask_count = -1;
    1131           5 :                             break;;
    1132             :                         }
    1133             :                     }
    1134             :                     else
    1135             :                     {
    1136             :                         // not a valid decimal number
    1137             :                         //
    1138           5 :                         mask_count = -1;
    1139           5 :                         break;
    1140             :                     }
    1141             :                 }
    1142         106 :                 if(mask_count > 32)
    1143             :                 {
    1144          96 :                     parse_address6(in, result);
    1145             :                 }
    1146             :                 else
    1147             :                 {
    1148          10 :                     parse_address4(in, result);
    1149             :                 }
    1150             :             }
    1151             :         }
    1152             :         else
    1153             :         {
    1154         414 :             if(f_default_address4.empty()
    1155         207 :             && !f_default_address6.empty())
    1156             :             {
    1157         102 :                 parse_address6(in, result);
    1158             :             }
    1159             :             else
    1160             :             {
    1161         105 :                 parse_address4(in, result);
    1162             :             }
    1163             :         }
    1164             :     }
    1165             :     else
    1166             :     {
    1167             :         // if an address has a ']' then it is IPv6 even if the '['
    1168             :         // is missing, that being said, it is still considered
    1169             :         // invalid as per our processes
    1170             :         //
    1171      263058 :         if(in[0] == '['
    1172      131529 :         || in.find(']') != std::string::npos)
    1173             :         {
    1174       65778 :             parse_address6(in, result);
    1175             :         }
    1176             :         else
    1177             :         {
    1178             :             // if there is no port, then a ':' can be viewed as an IPv6
    1179             :             // address because there is no other ':', but if there are
    1180             :             // '.' before the ':' then we assume that it is IPv4 still
    1181             :             //
    1182      131502 :             if(!get_allow(allow_t::ALLOW_PORT)
    1183       65751 :             && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1184             :             {
    1185           3 :                 std::string::size_type const p(in.find(':'));
    1186           3 :                 if(p != std::string::npos
    1187           3 :                 && in.find('.') > p)
    1188             :                 {
    1189           1 :                     parse_address6(in, result);
    1190             :                 }
    1191             :                 else
    1192             :                 {
    1193           2 :                     parse_address4(in, result);
    1194             :                 }
    1195             :             }
    1196             :             else
    1197             :             {
    1198       65748 :                 parse_address4(in, result);
    1199             :             }
    1200             :         }
    1201             :     }
    1202      131847 : }
    1203             : 
    1204             : 
    1205             : /** \brief Parse one IPv4 address.
    1206             :  *
    1207             :  * This function checks the input parameter \p in and extracts the
    1208             :  * address and port. There is a port if the input strings includes
    1209             :  * a `':'` character.
    1210             :  *
    1211             :  * If this function detects that a port is not allowed and yet
    1212             :  * a `':'` character is found, then it generates an error and
    1213             :  * returns without adding anything to `result`.
    1214             :  *
    1215             :  * \param[in] in  The input string with the address and optional port.
    1216             :  * \param[in,out] result  The list of resulting addresses.
    1217             :  */
    1218       65865 : void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
    1219             : {
    1220      131729 :     std::string address;
    1221      131729 :     std::string port_str;
    1222             : 
    1223       65865 :     std::string::size_type const p(in.find(':'));
    1224             : 
    1225      131730 :     if(get_allow(allow_t::ALLOW_PORT)
    1226       65865 :     || get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1227             :     {
    1228             :         // the address can include a port
    1229             :         //
    1230       65862 :         if(p != std::string::npos)
    1231             :         {
    1232             :             // get the address only if not empty (otherwise we want to
    1233             :             // keep the default)
    1234             :             //
    1235       65820 :             if(p > 0)
    1236             :             {
    1237       65710 :                 address = in.substr(0, p);
    1238             :             }
    1239             : 
    1240             :             // get the port only if not empty (otherwise we want to
    1241             :             // keep the default)
    1242             :             //
    1243       65820 :             if(p + 1 < in.length())
    1244             :             {
    1245       65795 :                 port_str = in.substr(p + 1);
    1246             :             }
    1247             :         }
    1248          42 :         else if(!in.empty())
    1249             :         {
    1250          38 :             address = in;
    1251             :         }
    1252             :     }
    1253             :     else
    1254             :     {
    1255           3 :         if(p != std::string::npos
    1256           1 :         && !get_allow(allow_t::ALLOW_PORT)
    1257           4 :         && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1258             :         {
    1259           1 :             emit_error("Port not allowed (" + in + ").");
    1260           1 :             return;
    1261             :         }
    1262             : 
    1263           2 :         if(!in.empty())
    1264             :         {
    1265           1 :             address = in;
    1266             :         }
    1267             :     }
    1268             : 
    1269       65864 :     parse_address_port(address, port_str, result, false);
    1270             : }
    1271             : 
    1272             : 
    1273             : /** \brief Parse one IPv6 address.
    1274             :  *
    1275             :  * This function checks the input parameter \p in and extracts the
    1276             :  * address and port. There is a port if the input strings includes
    1277             :  * a `':'` character after the closing square bracket (`']'`).
    1278             :  *
    1279             :  * If this function detects that a port is not allowed and yet
    1280             :  * a `':'` character is found, then it generates an error and
    1281             :  * returns without adding anything to `result`.
    1282             :  *
    1283             :  * \note
    1284             :  * This function can be called with an IPv6
    1285             :  *
    1286             :  * \param[in] in  The input string with the address and optional port.
    1287             :  * \param[in,out] result  The list of resulting addresses.
    1288             :  */
    1289       65982 : void addr_parser::parse_address6(std::string const & in, addr_range::vector_t & result)
    1290             : {
    1291       65982 :     std::string::size_type p(0);
    1292             : 
    1293      131961 :     std::string address;
    1294      131961 :     std::string port_str;
    1295             : 
    1296             :     // remove the square brackets if present
    1297             :     //
    1298      131964 :     if(!in.empty()
    1299       65982 :     && in[0] == '[')
    1300             :     {
    1301       65778 :         p = in.find(']');
    1302             : 
    1303       65778 :         if(p == std::string::npos)
    1304             :         {
    1305           1 :             emit_error("IPv6 is missing the ']' (" + in + ").");
    1306           1 :             return;
    1307             :         }
    1308             : 
    1309       65777 :         if(p != 1)
    1310             :         {
    1311             :             // get the address only if not empty (otherwise we want to
    1312             :             // keep the default) -- so we actually support "[]" to
    1313             :             // represent "use the default address if defined".
    1314             :             //
    1315       65776 :             address = in.substr(1, p - 1);
    1316             :         }
    1317             :     }
    1318             : 
    1319             :     // on entry 'p' is either 0 or the position of the ']' character
    1320             :     //
    1321       65981 :     p = in.find(':', p);
    1322             : 
    1323       65981 :     if(p != std::string::npos)
    1324             :     {
    1325      131946 :         if(get_allow(allow_t::ALLOW_PORT)
    1326       65973 :         || get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1327             :         {
    1328             :             // there is also a port, extract it
    1329             :             //
    1330       65971 :             port_str = in.substr(p + 1);
    1331             :         }
    1332           4 :         else if(!get_allow(allow_t::ALLOW_PORT)
    1333           2 :              && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1334             :         {
    1335           2 :             emit_error("Port not allowed (" + in + ").");
    1336           2 :             return;
    1337             :         }
    1338             :     }
    1339             : 
    1340       65979 :     parse_address_port(address, port_str, result, true);
    1341             : }
    1342             : 
    1343             : 
    1344             : /** \brief Parse the address and port.
    1345             :  *
    1346             :  * This function receives an address and a port string and
    1347             :  * convert them in an addr object which gets saved in
    1348             :  * the specified result range vector.
    1349             :  *
    1350             :  * The address can be an IPv4 or an IPv6 address.
    1351             :  *
    1352             :  * The port may be numeric or a name such as `"http"`.
    1353             :  *
    1354             :  * \note
    1355             :  * This function is not responsible for handling the default address
    1356             :  * and default port. This is expected to be dealt with by the caller
    1357             :  * if required.
    1358             :  *
    1359             :  * \param[in] address  The address to convert to binary.
    1360             :  * \param[in] port_str  The port as a string.
    1361             :  * \param[out] result  The range where we save the results.
    1362             :  * \param[in] ipv6  Use the default IPv6 address if the address is empty.
    1363             :  */
    1364      131843 : void addr_parser::parse_address_port(std::string address, std::string port_str, addr_range::vector_t & result, bool ipv6)
    1365             : {
    1366             :     // make sure the port is good
    1367             :     //
    1368      131843 :     if(port_str.empty())
    1369             :     {
    1370          77 :         if(get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1371             :         {
    1372           4 :             emit_error("Required port is missing.");
    1373           4 :             return;
    1374             :         }
    1375          73 :         if(f_default_port != -1)
    1376             :         {
    1377          52 :             port_str = std::to_string(f_default_port);
    1378             :         }
    1379             :     }
    1380             : 
    1381             :     // make sure the address is good
    1382             :     //
    1383      131839 :     if(address.empty())
    1384             :     {
    1385         319 :         if(get_allow(allow_t::ALLOW_REQUIRED_ADDRESS))
    1386             :         {
    1387           2 :             emit_error("Required address is missing.");
    1388           2 :             return;
    1389             :         }
    1390             :         // internal default if no address was defined
    1391             :         // (TBD: should it be an IPv6 instead?)
    1392             :         //
    1393         317 :         if(ipv6)
    1394             :         {
    1395         203 :             if(f_default_address6.empty())
    1396             :             {
    1397         101 :                 address = "::";
    1398             :             }
    1399             :             else
    1400             :             {
    1401         102 :                 address = f_default_address6;
    1402             :             }
    1403             :         }
    1404             :         else
    1405             :         {
    1406         114 :             if(f_default_address4.empty())
    1407             :             {
    1408          11 :                 address = "0.0.0.0";
    1409             :             }
    1410             :             else
    1411             :             {
    1412         103 :                 address = f_default_address4;
    1413             :             }
    1414             :         }
    1415             :     }
    1416             : 
    1417             :     // prepare hints for the the getaddrinfo() function
    1418             :     //
    1419      131837 :     addrinfo hints;
    1420      131837 :     memset(&hints, 0, sizeof(hints));
    1421      131837 :     hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
    1422      131837 :     hints.ai_family = AF_UNSPEC;
    1423             : 
    1424      131837 :     switch(f_protocol)
    1425             :     {
    1426       66371 :     case IPPROTO_TCP:
    1427       66371 :         hints.ai_socktype = SOCK_STREAM;
    1428       66371 :         hints.ai_protocol = IPPROTO_TCP;
    1429       66371 :         break;
    1430             : 
    1431       65452 :     case IPPROTO_UDP:
    1432       65452 :         hints.ai_socktype = SOCK_DGRAM;
    1433       65452 :         hints.ai_protocol = IPPROTO_UDP;
    1434       65452 :         break;
    1435             : 
    1436             :     }
    1437             : 
    1438             :     // convert address to binary
    1439             :     //
    1440      131837 :     if(get_allow(allow_t::ALLOW_ADDRESS_LOOKUP))
    1441             :     {
    1442      131832 :         addrinfo * addrlist(nullptr);
    1443             :         {
    1444      131832 :             errno = 0;
    1445      131832 :             char const * service(port_str.c_str());
    1446      131832 :             if(port_str.empty())
    1447             :             {
    1448          19 :                 service = "0"; // fallback to port 0 when unspecified
    1449             :             }
    1450      131832 :             int const r(getaddrinfo(address.c_str(), service, &hints, &addrlist));
    1451      131832 :             if(r != 0)
    1452             :             {
    1453             :                 // break on invalid addresses
    1454             :                 //
    1455           2 :                 int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
    1456           6 :                 emit_error(
    1457             :                           "Invalid address in \""
    1458           4 :                         + address
    1459           6 :                         + (port_str.empty() ? "" : ":")
    1460           6 :                         + port_str
    1461           6 :                         + "\" error "
    1462           8 :                         + std::to_string(r)
    1463           6 :                         + " -- "
    1464           6 :                         + gai_strerror(r)
    1465           8 :                         + (e == 0
    1466           8 :                             ? ""
    1467             :                             : " (errno: "
    1468           4 :                             + std::to_string(e)
    1469           6 :                             + " -- "
    1470           6 :                             + strerror(e)
    1471             :                             + ")."));
    1472           2 :                 return;
    1473             :             }
    1474             :         }
    1475      263660 :         std::shared_ptr<addrinfo> ai(addrlist, addrinfo_deleter);
    1476             : 
    1477      131830 :         bool first(true);
    1478      395538 :         while(addrlist != nullptr)
    1479             :         {
    1480             :             // go through the addresses and create ranges and save that in the result
    1481             :             //
    1482      131854 :             if(addrlist->ai_family == AF_INET)
    1483             :             {
    1484       65876 :                 if(addrlist->ai_addrlen != sizeof(sockaddr_in))
    1485             :                 {
    1486             :                     emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
    1487             :                              + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
    1488             :                              + ", expected"                                  // LCOV_EXCL_LINE
    1489             :                              + std::to_string(sizeof(sockaddr_in))           // LCOV_EXCL_LINE
    1490             :                              + ").");                                        // LCOV_EXCL_LINE
    1491             :                 }
    1492             :                 else
    1493             :                 {
    1494      131752 :                     addr a(*reinterpret_cast<sockaddr_in *>(addrlist->ai_addr));
    1495             :                     // in most cases we do not get a protocol from
    1496             :                     // the getaddrinfo() function...
    1497       65876 :                     a.set_protocol(addrlist->ai_protocol);
    1498       65876 :                     a.set_hostname(address);
    1499      131752 :                     addr_range r;
    1500       65876 :                     r.set_from(a);
    1501      131752 :                     if((f_sort & SORT_NO_EMPTY) == 0
    1502       65876 :                     || !r.is_empty())
    1503             :                     {
    1504       65876 :                         result.push_back(r);
    1505             :                     }
    1506             :                 }
    1507             :             }
    1508       65978 :             else if(addrlist->ai_family == AF_INET6)
    1509             :             {
    1510       65978 :                 if(addrlist->ai_addrlen != sizeof(sockaddr_in6))
    1511             :                 {
    1512             :                     emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
    1513             :                              + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
    1514             :                              + ", expected "                                 // LCOV_EXCL_LINE
    1515             :                              + std::to_string(sizeof(sockaddr_in6))          // LCOV_EXCL_LINE
    1516             :                              + ").");                                        // LCOV_EXCL_LINE
    1517             :                 }
    1518             :                 else
    1519             :                 {
    1520      131956 :                     addr a(*reinterpret_cast<sockaddr_in6 *>(addrlist->ai_addr));
    1521       65978 :                     a.set_hostname(address);
    1522       65978 :                     a.set_protocol(addrlist->ai_protocol);
    1523      131956 :                     addr_range r;
    1524       65978 :                     r.set_from(a);
    1525      131956 :                     if((f_sort & SORT_NO_EMPTY) == 0
    1526       65978 :                     || !r.is_empty())
    1527             :                     {
    1528       65978 :                         result.push_back(r);
    1529             :                     }
    1530             :                 }
    1531             :             }
    1532             :             else if(first)                                                  // LCOV_EXCL_LINE
    1533             :             {
    1534             :                 // ignore errors from further addresses
    1535             :                 //
    1536             :                 emit_error("Unsupported address family "                     // LCOV_EXCL_LINE
    1537             :                          + std::to_string(addrlist->ai_family)               // LCOV_EXCL_LINE
    1538             :                          + ".");                                             // LCOV_EXCL_LINE
    1539             :             }
    1540             : 
    1541      131854 :             first = false;
    1542             : 
    1543      131854 :             addrlist = addrlist->ai_next;
    1544             :         }
    1545             :     }
    1546             :     else
    1547             :     {
    1548           5 :         std::int64_t port(0);
    1549          10 :         if(get_allow(allow_t::ALLOW_REQUIRED_PORT)
    1550           5 :         || !port_str.empty())
    1551             :         {
    1552           5 :             bool const valid_port(advgetopt::validator_integer::convert_string(port_str, port));
    1553           5 :             if(!valid_port
    1554           4 :             || port < 0
    1555           4 :             || port > 65535)
    1556             :             {
    1557           2 :                 emit_error("Invalid port in \""
    1558           2 :                          + port_str
    1559           3 :                          + "\" (no service name lookup allowed).");
    1560           1 :                 return;
    1561             :             }
    1562             : 
    1563           8 :             if(!get_allow(allow_t::ALLOW_PORT)
    1564           4 :             && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1565             :             {
    1566           0 :                 emit_error("Found a port (\""
    1567           0 :                          + port_str
    1568           0 :                          + "\") when it is not allowed.");
    1569           0 :                 return;
    1570             :             }
    1571             :         }
    1572           4 :         sockaddr_in in;
    1573           4 :         if(inet_pton(AF_INET, address.c_str(), &in.sin_addr) == 1)
    1574             :         {
    1575           1 :             in.sin_family = AF_INET;
    1576           1 :             in.sin_port = htons(port);
    1577           1 :             memset(in.sin_zero, 0, sizeof(in.sin_zero)); // probably useless
    1578             : 
    1579           2 :             addr a(in);
    1580           1 :             a.set_hostname(address);
    1581           1 :             a.set_protocol(f_protocol);
    1582           2 :             addr_range r;
    1583           1 :             r.set_from(a);
    1584           2 :             if((f_sort & SORT_NO_EMPTY) == 0
    1585           1 :             || !r.is_empty())
    1586             :             {
    1587           1 :                 result.push_back(r);
    1588             :             }
    1589             :         }
    1590             :         else
    1591             :         {
    1592           3 :             sockaddr_in6 in6;
    1593           3 :             if(inet_pton(AF_INET6, address.c_str(), &in6.sin6_addr) == 1)
    1594             :             {
    1595           1 :                 in6.sin6_family = AF_INET6;
    1596           1 :                 in6.sin6_port = htons(port);
    1597           1 :                 in6.sin6_flowinfo = 0;
    1598           1 :                 in6.sin6_scope_id = 0;
    1599             : 
    1600           2 :                 addr a(in6);
    1601           1 :                 a.set_hostname(address);
    1602           1 :                 a.set_protocol(f_protocol);
    1603           2 :                 addr_range r;
    1604           1 :                 r.set_from(a);
    1605           2 :                 if((f_sort & SORT_NO_EMPTY) == 0
    1606           1 :                 || !r.is_empty())
    1607             :                 {
    1608           1 :                     result.push_back(r);
    1609             :                 }
    1610             :             }
    1611             :             else
    1612             :             {
    1613           4 :                 emit_error("Unknown address in \""
    1614           4 :                          + address
    1615           6 :                          + "\" (no DNS lookup was allowed).");
    1616             :             }
    1617             :         }
    1618             :     }
    1619             : }
    1620             : 
    1621             : 
    1622             : /** \brief Parse a mask.
    1623             :  *
    1624             :  * If the input string is a decimal number, then use that as the
    1625             :  * number of bits to clear.
    1626             :  *
    1627             :  * If the mask is not just one decimal number, try to convert it
    1628             :  * as an address.
    1629             :  *
    1630             :  * If the string is neither a decimal number nor a valid IP address
    1631             :  * then the parser adds an error string to the f_error variable.
    1632             :  *
    1633             :  * \param[in] mask  The mask to transform to binary.
    1634             :  * \param[in,out] cidr  The address to which the mask will be added.
    1635             :  */
    1636         474 : void addr_parser::parse_mask(std::string const & mask, addr & cidr)
    1637             : {
    1638             :     // no mask?
    1639             :     //
    1640         474 :     if(mask.empty())
    1641             :     {
    1642             :         // in the current implementation this cannot happen since we
    1643             :         // do not call this function when mask is empty
    1644             :         //
    1645             :         // hwoever, the algorithm below expect that 'mask' is not
    1646             :         // empty (otherwise we get the case of 0 even though it
    1647             :         // may not be correct.)
    1648             :         //
    1649             :         return;  // LCOV_EXCL_LINE
    1650             :     }
    1651             : 
    1652             :     // the mask may be a decimal number or an address, if just one number
    1653             :     // then it's not an address, so test that first
    1654             :     //
    1655         470 :     uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
    1656             : 
    1657             :     // convert the mask to an integer, if possible
    1658             :     //
    1659         470 :     int mask_count(0);
    1660             :     {
    1661        1304 :         for(char const * s(mask.c_str()); *s != '\0'; ++s)
    1662             :         {
    1663        1032 :             if(*s >= '0' && *s <= '9')
    1664             :             {
    1665         849 :                 mask_count = mask_count * 10 + *s - '0';
    1666        1683 :                 if(mask_count > 1000)
    1667             :                 {
    1668          30 :                     emit_error("Mask number too large ("
    1669          30 :                              + mask
    1670          45 :                              + ", expected a maximum of 128).");
    1671          15 :                     return;
    1672             :                 }
    1673             :             }
    1674             :             else
    1675             :             {
    1676         183 :                 mask_count = -1;
    1677         183 :                 break;
    1678             :             }
    1679             :         }
    1680             :     }
    1681             : 
    1682             :     // the conversion to an integer worked if mask_count != -1
    1683             :     //
    1684         455 :     if(mask_count != -1)
    1685             :     {
    1686         272 :         if(cidr.is_ipv4())
    1687             :         {
    1688          42 :             if(mask_count > 32)
    1689             :             {
    1690          10 :                 emit_error("Unsupported mask size ("
    1691          10 :                          + std::to_string(mask_count)
    1692          15 :                          + ", expected 32 at the most for an IPv4).");
    1693           5 :                 return;
    1694             :             }
    1695          37 :             mask_count = 32 - mask_count;
    1696             :         }
    1697             :         else
    1698             :         {
    1699         230 :             if(mask_count > 128)
    1700             :             {
    1701          10 :                 emit_error("Unsupported mask size ("
    1702          10 :                          + std::to_string(mask_count)
    1703          15 :                          + ", expected 128 at the most for an IPv6).");
    1704           5 :                 return;
    1705             :             }
    1706         225 :             mask_count = 128 - mask_count;
    1707             :         }
    1708             : 
    1709             :         // clear a few bits at the bottom of mask_bits
    1710             :         //
    1711         262 :         int idx(15);
    1712        3322 :         for(; mask_count > 8; mask_count -= 8, --idx)
    1713             :         {
    1714        1530 :             mask_bits[idx] = 0;
    1715             :         }
    1716         262 :         mask_bits[idx] = 255 << mask_count;
    1717             :     }
    1718             :     else //if(mask_count < 0)
    1719             :     {
    1720             :         // prepare hints for the the getaddrinfo() function
    1721             :         //
    1722         183 :         addrinfo hints;
    1723         183 :         memset(&hints, 0, sizeof(hints));
    1724         183 :         hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
    1725         183 :         hints.ai_family = AF_UNSPEC;
    1726             : 
    1727         183 :         switch(cidr.get_protocol())
    1728             :         {
    1729          98 :         case IPPROTO_TCP:
    1730          98 :             hints.ai_socktype = SOCK_STREAM;
    1731          98 :             hints.ai_protocol = IPPROTO_TCP;
    1732          98 :             break;
    1733             : 
    1734          85 :         case IPPROTO_UDP:
    1735          85 :             hints.ai_socktype = SOCK_DGRAM;
    1736          85 :             hints.ai_protocol = IPPROTO_UDP;
    1737          85 :             break;
    1738             : 
    1739             :         }
    1740             : 
    1741         338 :         std::string const port_str(std::to_string(cidr.get_port()));
    1742             : 
    1743             :         // if the mask is an IPv6, then it has to have the '[...]'
    1744         338 :         std::string m(mask);
    1745         183 :         if(cidr.is_ipv4())
    1746             :         {
    1747          87 :             if(mask[0] == '[')
    1748             :             {
    1749           1 :                 emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
    1750           1 :                 return;
    1751             :             }
    1752             :         }
    1753             :         else //if(!cidr.is_ipv4())
    1754             :         {
    1755          96 :             if(mask[0] != '[')
    1756             :             {
    1757           1 :                 emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
    1758           1 :                 return;
    1759             :             }
    1760          95 :             if(mask.back() != ']')
    1761             :             {
    1762           1 :                 emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
    1763           1 :                 return;
    1764             :             }
    1765             : 
    1766             :             // note that we know that mask.length() >= 2 here since
    1767             :             // we at least have a '[' and ']'
    1768             :             //
    1769          94 :             m = mask.substr(1, mask.length() - 2);
    1770          94 :             if(m.empty())
    1771             :             {
    1772             :                 // an empty mask is valid, it just means keep the default
    1773             :                 // (getaddrinfo() fails on an empty string)
    1774             :                 //
    1775           3 :                 return;
    1776             :             }
    1777             :         }
    1778             : 
    1779             :         // if negative, we may have a full address here, so call the
    1780             :         // getaddrinfo() on this other string
    1781             :         //
    1782         177 :         addrinfo * masklist(nullptr);
    1783         177 :         errno = 0;
    1784         177 :         int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
    1785         177 :         if(r != 0)
    1786             :         {
    1787             :             // break on invalid addresses
    1788             :             //
    1789          15 :             int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
    1790          30 :             emit_error("Invalid mask in \"/"
    1791          30 :                      + mask
    1792          45 :                      + "\", error "
    1793          60 :                      + std::to_string(r)
    1794          45 :                      + " -- "
    1795          45 :                      + gai_strerror(r)
    1796          45 :                      + " (errno: "
    1797          60 :                      + std::to_string(e)
    1798          45 :                      + " -- "
    1799          45 :                      + strerror(e)
    1800          45 :                      + ").");
    1801          15 :             return;
    1802             :         }
    1803         317 :         std::shared_ptr<addrinfo> mask_ai(masklist, addrinfo_deleter);
    1804             : 
    1805         162 :         if(cidr.is_ipv4())
    1806             :         {
    1807          76 :             if(masklist->ai_family != AF_INET)
    1808             :             {
    1809             :                 // this one happens when the user does not put the '[...]'
    1810             :                 // around an IPv6 address
    1811             :                 //
    1812           1 :                 emit_error("Incompatible address between the address and"
    1813             :                           " mask address (first was an IPv4 second an IPv6).");
    1814           1 :                 return;
    1815             :             }
    1816          75 :             if(masklist->ai_addrlen != sizeof(sockaddr_in))
    1817             :             {
    1818             :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
    1819             :                         + std::to_string(masklist->ai_addrlen)          // LCOV_EXCL_LINE
    1820             :                         + ", expected"                                  // LCOV_EXCL_LINE
    1821             :                         + std::to_string(sizeof(sockaddr_in))           // LCOV_EXCL_LINE
    1822             :                         + ").");                                        // LCOV_EXCL_LINE
    1823             :                 return;                                                 // LCOV_EXCL_LINE
    1824             :             }
    1825          75 :             memcpy(mask_bits + 12, &reinterpret_cast<sockaddr_in *>(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
    1826             :         }
    1827             :         else //if(!cidr.is_ipv4())
    1828             :         {
    1829          86 :             if(masklist->ai_family != AF_INET6)
    1830             :             {
    1831             :                 // this one happens if the user puts the '[...]'
    1832             :                 // around an IPv4 address
    1833             :                 //
    1834           6 :                 emit_error("Incompatible address between the address"
    1835             :                           " and mask address (first was an IPv6 second an IPv4).");
    1836           6 :                 return;
    1837             :             }
    1838          80 :             if(masklist->ai_addrlen != sizeof(sockaddr_in6))
    1839             :             {
    1840             :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
    1841             :                          + std::to_string(masklist->ai_addrlen)         // LCOV_EXCL_LINE
    1842             :                          + ", expected "                                // LCOV_EXCL_LINE
    1843             :                          + std::to_string(sizeof(sockaddr_in6))         // LCOV_EXCL_LINE
    1844             :                          + ").");                                       // LCOV_EXCL_LINE
    1845             :                 return;                                                 // LCOV_EXCL_LINE
    1846             :             }
    1847          80 :             memcpy(mask_bits, &reinterpret_cast<sockaddr_in6 *>(masklist->ai_addr)->sin6_addr.s6_addr, 16);
    1848             :         }
    1849             :     }
    1850             : 
    1851         417 :     cidr.set_mask(mask_bits);
    1852             : }
    1853             : 
    1854             : 
    1855             : /** \brief Transform a string into an `addr` object.
    1856             :  *
    1857             :  * This function converts the string \p a in an IP address saved in
    1858             :  * the returned addr object or throws an error if the conversion
    1859             :  * fails.
    1860             :  *
    1861             :  * The \p default_address parameter string can be set to an address
    1862             :  * which is returned if the input in \p a does not include an
    1863             :  * address such as in ":123".
    1864             :  *
    1865             :  * The \p port parameter can be specified or set to -1. If -1, then
    1866             :  * there is no default port. Either way, the port can be defined in
    1867             :  * \p a.
    1868             :  *
    1869             :  * The protocol can be specified, as a string. For example, you can
    1870             :  * use "tcp". The default is no specific protocol which means any
    1871             :  * type of IP address can be returned. Note that if more than one
    1872             :  * result is returned when the protocol was not specified, the
    1873             :  * results will be filtered to only keep the address that uses the
    1874             :  * TCP protocol. If as a result we have a single address, then that
    1875             :  * result gets returned.
    1876             :  *
    1877             :  * \note
    1878             :  * This function does not allow for address or port ranges. It is
    1879             :  * expected to return exactly one address. You can allow a \p mask
    1880             :  * by setting that parameter to true.
    1881             :  *
    1882             :  * \exception addr_invalid_argument
    1883             :  * If the parsed address is not returning a valid `addr` object, then
    1884             :  * this function fails by throwing an error. If you would prefer to
    1885             :  * handle the error mechanism, you want to create your own addr_parser
    1886             :  * and then call the addr_parser::parse() function. This will allow
    1887             :  * you to get error messages instead of an exception.
    1888             :  *
    1889             :  * \param[in] a  The address string to be converted.
    1890             :  * \param[in] default_addrress  The default address or an empty string.
    1891             :  * \param[in] default_port  The default port or -1
    1892             :  * \param[in] protocol  The protocol the address has to be of, or the
    1893             :  *                      empty string to allow any protocol.
    1894             :  * \param[in] m  Whether to allow a mask (true) or not (false).
    1895             :  *
    1896             :  * \return The address converted in an `addr` object.
    1897             :  *
    1898             :  * \sa addr_parser::parse()
    1899             :  */
    1900          14 : addr string_to_addr(
    1901             :           std::string const & a
    1902             :         , std::string const & default_address
    1903             :         , int default_port
    1904             :         , std::string const & protocol
    1905             :         , bool mask)
    1906             : {
    1907          28 :     addr_parser p;
    1908             : 
    1909          14 :     if(!default_address.empty())
    1910             :     {
    1911          10 :         p.set_default_address(default_address);
    1912             :     }
    1913             : 
    1914          14 :     if(default_port != -1)
    1915             :     {
    1916           8 :         p.set_default_port(default_port);
    1917             :     }
    1918             : 
    1919          14 :     if(!protocol.empty())
    1920             :     {
    1921           7 :         p.set_protocol(protocol);
    1922             :     }
    1923             : 
    1924          13 :     p.set_allow(allow_t::ALLOW_MASK, mask);
    1925             : 
    1926          26 :     addr_range::vector_t result(p.parse(a));
    1927             : 
    1928          13 :     if(result.size() != 1)
    1929             :     {
    1930             :         // when the protocol is not specified, this happens like all the
    1931             :         // time, we search for an entry with protocol TCP by default
    1932             :         // because in most cases that's what people want
    1933             :         //
    1934           7 :         if(protocol.empty())
    1935             :         {
    1936          21 :             result.erase(
    1937          14 :                       std::remove_if(
    1938             :                           result.begin()
    1939             :                         , result.end()
    1940          21 :                         , [](auto const it)
    1941             :                         {
    1942          21 :                             return it.has_from() && it.get_from().get_protocol() != IPPROTO_TCP;
    1943          28 :                         })
    1944          35 :                     , result.end());
    1945             :         }
    1946           7 :         if(result.size() != 1)
    1947             :         {
    1948             :             // an invalid protocol is caught by the set_protocol()
    1949             :             // function so we should never be able to reach here
    1950             :             //
    1951             :             throw addr_invalid_argument(                                                            // LCOV_EXCL_LINE
    1952             :                       "the address \""                                                              // LCOV_EXCL_LINE
    1953             :                     + a                                                                             // LCOV_EXCL_LINE
    1954             :                     + "\" could not be converted to a single address in string_to_addr(), found "   // LCOV_EXCL_LINE
    1955             :                     + std::to_string(result.size())                                                 // LCOV_EXCL_LINE
    1956             :                     + " entries instead.");                                                         // LCOV_EXCL_LINE
    1957             :         }
    1958             :     }
    1959             : 
    1960             :     // at the movement we only can get a "to" so the following exceptions
    1961             :     // can't happen which is why we have an LCOV_EXCL_LINE
    1962             :     //
    1963          26 :     if(result[0].has_to()
    1964          13 :     || result[0].is_range())
    1965             :     {
    1966             :         throw addr_invalid_argument("string_to_addr() does not support ranges.");     // LCOV_EXCL_LINE
    1967             :     }
    1968             : 
    1969          13 :     if(!result[0].has_from())
    1970             :     {
    1971             :         throw addr_invalid_argument("string_to_addr() has no 'from' address.");       // LCOV_EXCL_LINE
    1972             :     }
    1973             : 
    1974          26 :     return result[0].get_from();
    1975             : }
    1976             : 
    1977             : 
    1978             : 
    1979           6 : }
    1980             : // namespace addr
    1981             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13