LCOV - code coverage report
Current view: top level - libaddr - addr_parser.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 632 632
Test Date: 2025-06-19 19:30:42 Functions: 100.0 % 37 37
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2012-2025  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              :  * The type of addresses support is really wide:
      32              :  *
      33              :  * * <domain name> -- if allowed to do a lookup
      34              :  * * <ipv4> -- an IPv4 with syntax x.x.x.x
      35              :  * * <ipv6> -- an IPv6 with syntax x:x:x:...:x
      36              :  * * <port> -- a decimal number from 0 to 65535
      37              :  * * <mask> -- a number from 0 to 128 or an <ipv4> or an <ipv6>
      38              :  * * <ip>-<ip> -- a range of <ipv4> or <ipv6> addresses
      39              :  *
      40              :  * The port is separated from the address by a colon (:). For IPv6, this means
      41              :  * the IPv6 address itself must be defined between square brackets as in
      42              :  * `[x:x:...:x]`. The square brackets are not required if the port is not
      43              :  * allowed.
      44              :  *
      45              :  * The `<mask>` appears after a slash (/). It is expected to be a number
      46              :  * from 0 to 128 (0 to 32 for IPv4 addresses). It can be written as an
      47              :  * address only if the ALLOW_ADDRESS_MASK flag is set to true.
      48              :  *
      49              :  * \code
      50              :  * start: ips
      51              :  *
      52              :  * ips: domain port mask
      53              :  *    | port mask
      54              :  *    | ip port mask
      55              :  *    | ip '-' ip port mask
      56              :  *    | ip '-' port mask
      57              :  *    | '-' ip port mask
      58              :  *
      59              :  * ip: '[' ipv6 ']'
      60              :  *   | ipv6
      61              :  *   | ipv4
      62              :  *
      63              :  * ipv4: number '.' number '.' number '.' number
      64              :  *
      65              :  * ipv6: hex
      66              :  *     | ':'
      67              :  *     | ipv6 ipv6
      68              :  *
      69              :  * port: <empty>
      70              :  *     | ':' number
      71              :  *
      72              :  * mask: <empty>
      73              :  *     | '/' number
      74              :  *     | '/' ip
      75              :  *
      76              :  * number: number number
      77              :  *       | '0' | '1' | '2' | ... | '9'
      78              :  *
      79              :  * hex: hex hex
      80              :  *    | number
      81              :  *    | 'a' | 'b' | ... | 'f'
      82              :  *    | 'A' | 'B' | ... | 'F'
      83              :  *
      84              :  * domain: domain domain
      85              :  *       | number
      86              :  *       | 'a' | 'b' | ... | 'z'
      87              :  *       | 'A' | 'B' | ... | 'Z'
      88              :  *       | '.' | '-'
      89              :  *       | UTF8_CHARACTER (most domain systems do not support all UTF-8)
      90              :  * \endcode
      91              :  */
      92              : 
      93              : // self
      94              : //
      95              : #include    "libaddr/addr_parser.h"
      96              : #include    "libaddr/exception.h"
      97              : 
      98              : 
      99              : // advgetopt
     100              : //
     101              : #include    <advgetopt/validator_integer.h>
     102              : 
     103              : 
     104              : // snapdev
     105              : //
     106              : #include    <snapdev/trim_string.h>
     107              : 
     108              : 
     109              : // C++
     110              : //
     111              : #include    <algorithm>
     112              : #include    <iostream>
     113              : 
     114              : 
     115              : // C
     116              : //
     117              : #include    <ifaddrs.h>
     118              : #include    <netdb.h>
     119              : 
     120              : 
     121              : // last include
     122              : //
     123              : #include    <snapdev/poison.h>
     124              : 
     125              : 
     126              : 
     127              : namespace addr
     128              : {
     129              : 
     130              : 
     131              : namespace
     132              : {
     133              : 
     134              : 
     135              : /** \brief Delete an addrinfo structure.
     136              :  *
     137              :  * This deleter is used to make sure all the addinfo get released when
     138              :  * an exception occurs or the function using such exists.
     139              :  *
     140              :  * \param[in] ai  The addrinfo structure to free.
     141              :  */
     142       132258 : void addrinfo_deleter(addrinfo * ai)
     143              : {
     144       132258 :     freeaddrinfo(ai);
     145       132258 : }
     146              : 
     147              : 
     148              : }
     149              : 
     150              : 
     151              : 
     152              : 
     153              : 
     154              : /** \brief Initialize an addr_parser object.
     155              :  *
     156              :  * This function initializes the addr_parser object.
     157              :  *
     158              :  * Especially, it calls the set_allow() functions a few times to set
     159              :  * flags which are expected to be true on initialization.
     160              :  */
     161       132066 : addr_parser::addr_parser()
     162              : {
     163              :     // allow addresses & DNS lookups by default
     164              :     //
     165       132066 :     set_allow(allow_t::ALLOW_ADDRESS, true);
     166       132066 :     set_allow(allow_t::ALLOW_ADDRESS_LOOKUP, true);
     167              : 
     168              :     // allow port after address
     169              :     //
     170       132066 :     set_allow(allow_t::ALLOW_PORT, true);
     171       132066 : }
     172              : 
     173              : 
     174              : /** \brief Set the default IP addresses.
     175              :  *
     176              :  * This function sets the default IP addresses to be used by the parser
     177              :  * when the input string of the parse() function does not include an IP
     178              :  * address.
     179              :  *
     180              :  * The \p address parameter cannot include a port. See
     181              :  * set_default_port() as a way to change the default port.
     182              :  *
     183              :  * The function expects either an IPv4 or an IPv6 address. It can be
     184              :  * called twice if you need to define both types of addresses (which
     185              :  * is often a good idea.)
     186              :  *
     187              :  * For example, the following input is considered valid when a default
     188              :  * address is defined:
     189              :  *
     190              :  * \code
     191              :  *      parser.parse(":123");
     192              :  * \endcode
     193              :  *
     194              :  * It returns the default address and port 123. Note that by default
     195              :  * an address is mandatory unless a default address is defined.
     196              :  *
     197              :  * To prevent the parser from working when no default and no address
     198              :  * are specified, then make sure to set the REQUIRED_ADDRESS allow
     199              :  * flag to true:
     200              :  *
     201              :  * \code
     202              :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, true);
     203              :  *      // now address is mandatory
     204              :  * \endcode
     205              :  *
     206              :  * To completely prevent the use of an address in an input string, set
     207              :  * the `ADDRESS` and `REQUIRED_ADDRESS` values to false:
     208              :  *
     209              :  * \code
     210              :  *      parser.set_allow(parser.allow_t::ALLOW_ADDRESS,          false);
     211              :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, false);
     212              :  * \endcode
     213              :  *
     214              :  * To remove both default IP addresses, call this function with an empty
     215              :  * string:
     216              :  *
     217              :  * \code
     218              :  *      parser.set_default_address(std::string());
     219              :  * \endcode
     220              :  *
     221              :  * \todo
     222              :  * Consider saving the default IPs as addr structures and allow such
     223              :  * as input (keep in mind that the default could also represent multiple
     224              :  * addresses).
     225              :  *
     226              :  * \param[in] addr  The new address.
     227              :  */
     228          230 : void addr_parser::set_default_address(std::string const & address)
     229              : {
     230          230 :     if(address.empty())
     231              :     {
     232            4 :         f_default_address4.clear();
     233            4 :         f_default_address6.clear();
     234              :     }
     235          226 :     else if(address[0] == '[')
     236              :     {
     237              :         // remove the '[' and ']'
     238              :         //
     239            6 :         if(address.back() != ']')
     240              :         {
     241            9 :             throw addr_invalid_argument("an IPv6 address starting with '[' must end with ']'.");
     242              :         }
     243            3 :         f_default_address6 = address.substr(1, address.length() - 2);
     244              :     }
     245          220 :     else if(address.find(':') != std::string::npos)
     246              :     {
     247          103 :         f_default_address6 = address;
     248              :     }
     249              :     else
     250              :     {
     251          117 :         f_default_address4 = address;
     252              :     }
     253          227 : }
     254              : 
     255              : 
     256              : /** \brief Retrieve the default IP address for IPv4 parsing.
     257              :  *
     258              :  * This function returns a copy of the default IP address used by
     259              :  * the parser when the input string does not include an IP address.
     260              :  *
     261              :  * If the function returns an empty string, then no default address
     262              :  * is defined.
     263              :  *
     264              :  * \return The default IPv4 address.
     265              :  *
     266              :  * \sa get_default_address6()
     267              :  * \sa set_default_address()
     268              :  */
     269          115 : std::string const & addr_parser::get_default_address4() const
     270              : {
     271          115 :     return f_default_address4;
     272              : }
     273              : 
     274              : 
     275              : /** \brief Retrieve the default IP address for IPv4 parsing.
     276              :  *
     277              :  * This function returns a copy of the default IP address used by
     278              :  * the parser when the input string does not include an IP address.
     279              :  *
     280              :  * If the function returns an empty string, then no default address
     281              :  * is defined.
     282              :  *
     283              :  * \return The default IPv6 address, without square brackets.
     284              :  *
     285              :  * \sa get_default_address4()
     286              :  * \sa set_default_address()
     287              :  */
     288          115 : std::string const & addr_parser::get_default_address6() const
     289              : {
     290          115 :     return f_default_address6;
     291              : }
     292              : 
     293              : 
     294              : /** \brief Set the default port using a string.
     295              :  *
     296              :  * By default, you are expected to call the set_default_port() function
     297              :  * with an integer. If you do not have a number for the port (which
     298              :  * happens quite frequently) we offer a string version. The string is
     299              :  * expected to be an exact integer between 0 and 65535 inclusive.
     300              :  *
     301              :  * The function will also accept -1 and the empty string to reset the
     302              :  * default port to its default value (i.e. "no default port").
     303              :  *
     304              :  * \exception addr_invalid_argument
     305              :  * When the input \p port_str is not a valid integer, this exception is
     306              :  * raised.
     307              :  *
     308              :  * \param[in] port_str  The string to convert as a port.
     309              :  */
     310           54 : void addr_parser::set_default_port(std::string const & port_str)
     311              : {
     312           54 :     std::int64_t port(-1);
     313           54 :     if(!port_str.empty())
     314              :     {
     315           53 :         if(!advgetopt::validator_integer::convert_string(port_str, port))
     316              :         {
     317              :             // TODO: add a lookup for string to port number via /etc/service
     318              :             throw addr_invalid_argument(
     319              :                       "invalid port in \""
     320            2 :                     + port_str
     321            3 :                     + "\" (no service name lookup allowed).");
     322              :         }
     323              :     }
     324              : 
     325           53 :     set_default_port(port);
     326           28 : }
     327              : 
     328              : 
     329              : /** \brief Define the default port.
     330              :  *
     331              :  * This function is used to define the default port to use in the address
     332              :  * parser object. By default this is set to -1 meaning: no default port.
     333              :  *
     334              :  * This function accepts any port number from 0 to 65535. It also accepts
     335              :  * -1 to reset the port back to "no default".
     336              :  *
     337              :  * To prevent the parser from working when no default and no port
     338              :  * are specified, then make sure to set the REQUIRED_PORT allow
     339              :  * flag to true:
     340              :  *
     341              :  * \code
     342              :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, true);
     343              :  *      // now port is mandatory
     344              :  * \endcode
     345              :  *
     346              :  * To completely prevent the use of a port in an input string, set
     347              :  * the `PORT` and `REQUIRED_PORT` values to false:
     348              :  *
     349              :  * \code
     350              :  *      parser.set_allow(parser.allow_t::ALLOW_PORT,          false);
     351              :  *      parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, false);
     352              :  * \endcode
     353              :  *
     354              :  * \exception addr_invalid_argument_exception
     355              :  * If the port number is out of range, then this exception is raised.
     356              :  * The allowed range for a port is 0 to 65535. This function also
     357              :  * accepts -1 meaning that no default port is specified.
     358              :  *
     359              :  * \param[in] port  The new default port.
     360              :  */
     361          113 : void addr_parser::set_default_port(int const port)
     362              : {
     363          113 :     if(port < -1
     364           89 :     || port > 65535)
     365              :     {
     366          150 :         throw addr_invalid_argument("addr_parser::set_default_port(): port must be in range [-1..65535].");
     367              :     }
     368              : 
     369           63 :     f_default_port = port;
     370           63 : }
     371              : 
     372              : 
     373              : /** \brief Retrieve the default port.
     374              :  *
     375              :  * This function retrieves the default port as defined by the
     376              :  * set_default_port() function.
     377              :  */
     378           76 : int addr_parser::get_default_port() const
     379              : {
     380           76 :     return f_default_port;
     381              : }
     382              : 
     383              : 
     384              : /** \brief Define the default mask.
     385              :  *
     386              :  * This function is used to define the default mask. Note that the
     387              :  * default mask will not be used at all if the allow_t::ALLOW_MASK allow
     388              :  * flag is not set to true:
     389              :  *
     390              :  * \code
     391              :  *      parser.set_allow(parser.allow_t::ALLOW_MASK, true);
     392              :  *      parser.set_default_mask("16");  // IPv4 is 0 to 32
     393              :  *      parser.set_default_mask("48");  // IPv6 is 0 to 128
     394              :  * \endcode
     395              :  *
     396              :  * If you want to allow the old syntax (i.e. the mask as an IP address
     397              :  * instead of just a number), make sure to also allow that:
     398              :  *
     399              :  * \code
     400              :  *      parser.set_allow(parser.allow_t::ALLOW_MASK, true);
     401              :  *      parser.set_allow(parser.allow_t::ALLOW_ADDRESS_MASK, true);
     402              :  *      parser.set_default_mask("255.255.0.0");
     403              :  *      parser.set_default_mask("[ffff:ffff:ffff::]");
     404              :  * \endcode
     405              :  *
     406              :  * The IPv6 mask does not require the square brackets (`'['` and `']'`).
     407              :  *
     408              :  * To remove the default mask, call this function with an empty
     409              :  * string:
     410              :  *
     411              :  * \code
     412              :  *      parser.set_default_mask(std::string());
     413              :  * \endcode
     414              :  *
     415              :  * \note
     416              :  * As you can see, here we expect the mask to be a string. This is because
     417              :  * it gets parsed as if it came from the input string of the parser. This
     418              :  * also means that if the mask is invalid, it will not be detected until
     419              :  * you attempt to parse an input string that does not include a mask and
     420              :  * the default gets used.
     421              :  *
     422              :  * \warning
     423              :  * This function accepts address like values as the default mask. This
     424              :  * generates an error if no mask is defined by the user and you did not
     425              :  * do:
     426              :  *
     427              :  * \code
     428              :  *     parser.set_allow(addr::allow_t::ALLOW_ADDRESS_MASK, true);
     429              :  * \endcode
     430              :  *
     431              :  * \exception addr_invalid_argument
     432              :  * An IPv6 address that start with a '[' must end with a ']'. If only one
     433              :  * of these characters appears in the string, then it is an error and this
     434              :  * exception is raised.
     435              :  *
     436              :  * \todo
     437              :  * The mask accepts a simple number from 0 to 128. This function is not
     438              :  * capable of understand whether a smaller number (0 to 32) is an IPv4
     439              :  * or an IPv6 mask. At the moment, small numbers are viewed as an IPv4
     440              :  * mask.
     441              :  *
     442              :  * \todo
     443              :  * Add a check of the default mask when it gets set so we can throw on
     444              :  * errors and that way it is much more likely that programmers can fix
     445              :  * their errors early. (Actually by pre-parsing we could save it as
     446              :  * an addr and allow a `set_default_mask(addr ...)`!)
     447              :  *
     448              :  * \param[in] mask  The mask to use by default.
     449              :  */
     450          158 : void addr_parser::set_default_mask(std::string const & mask)
     451              : {
     452          158 :     if(mask.empty())
     453              :     {
     454            4 :         f_default_mask4.clear();
     455            4 :         f_default_mask6.clear();
     456            4 :         return;
     457              :     }
     458              : 
     459          154 :     bool const front_ipv6(mask.front() == '[');
     460          154 :     bool const back_ipv6(mask.back() == ']');
     461          154 :     if(front_ipv6 && back_ipv6)
     462              :     {
     463              :         // remove the '[' and ']'
     464              :         //
     465           28 :         f_default_mask6 = mask.substr(1, mask.length() - 2);
     466           28 :         return;
     467              :     }
     468              : 
     469          126 :     if(front_ipv6 || back_ipv6)
     470              :     {
     471            9 :         throw addr_invalid_argument("an IPv6 mask starting with '[' must end with ']' and vice versa.");
     472              :     }
     473              : 
     474          123 :     if(mask.find(':') != std::string::npos)
     475              :     {
     476           25 :         f_default_mask6 = mask;
     477           25 :         return;
     478              :     }
     479              : 
     480           98 :     std::int64_t m(0);
     481           98 :     bool const valid(advgetopt::validator_integer::convert_string(mask, m));
     482           98 :     if(valid)
     483              :     {
     484           20 :         if(m < 0 || m > 128)
     485              :         {
     486           51 :             throw addr_invalid_argument("a mask number must be between 0 and 128.");
     487              :         }
     488            3 :         if(m > 32)
     489              :         {
     490            2 :             f_default_mask6 = mask;
     491            2 :             return;
     492              :         }
     493              :     }
     494              : 
     495           79 :     f_default_mask4 = mask;
     496              : }
     497              : 
     498              : 
     499              : /** \brief Retrieve the default mask.
     500              :  *
     501              :  * This function returns a reference to the mask as set by the
     502              :  * set_default_mask() function. The value is an empty string by
     503              :  * default.
     504              :  *
     505              :  * The default mask will be used if no mask is specified in the
     506              :  * input string to the parse() function. When no default mask
     507              :  * is defined, the mask is set to all 1s.
     508              :  *
     509              :  * \note
     510              :  * The default mask is a string, not a binary mask. It gets
     511              :  * converted by the parser at the time it is required.
     512              :  *
     513              :  * \return The default mask.
     514              :  *
     515              :  * \sa get_default_mask6()
     516              :  * \sa set_default_mask()
     517              :  */
     518           14 : std::string const & addr_parser::get_default_mask4() const
     519              : {
     520           14 :     return f_default_mask4;
     521              : }
     522              : 
     523              : 
     524              : /** \brief Retrieve the default mask.
     525              :  *
     526              :  * This function returns a reference to the mask as set by the
     527              :  * set_default_mask() function. The value is an empty string by
     528              :  * default.
     529              :  *
     530              :  * The default mask will be used if no mask is specified in the
     531              :  * input string to the parse() function. When no default mask
     532              :  * is defined, the mask is set to all 1s.
     533              :  *
     534              :  * \note
     535              :  * The default mask is a string, not a binary mask. It gets
     536              :  * converted by the parser at the time it is required.
     537              :  *
     538              :  * \return The default mask.
     539              :  *
     540              :  * \sa get_default_mask4()
     541              :  * \sa set_default_mask()
     542              :  */
     543           14 : std::string const & addr_parser::get_default_mask6() const
     544              : {
     545           14 :     return f_default_mask6;
     546              : }
     547              : 
     548              : 
     549              : /** \brief Set the protocol to use to filter addresses.
     550              :  *
     551              :  * This function sets the protocol. The accepted names are defined in
     552              :  * the /etc/protocols file. In most cases, we support "tcp" and
     553              :  * "udp". Other transfer protocols may work too, but we have not
     554              :  * tested them.
     555              :  *
     556              :  * Any other value is refused. To reset the protocol to the default,
     557              :  * which is "do not filter by protocol", call the clear_protocol().
     558              :  *
     559              :  * \exception addr_invalid_argument_exception
     560              :  * If the string passed to this function is not one of the acceptable
     561              :  * protocols (ip, tcp, udp), then this exception is raised.
     562              :  *
     563              :  * \param[in] protocol  The default protocol for this parser.
     564              :  *
     565              :  * \sa clear_protocol()
     566              :  * \sa get_protocol()
     567              :  */
     568          119 : void addr_parser::set_protocol(std::string const & protocol)
     569              : {
     570          119 :     char buf[1024];
     571          119 :     protoent p = {};
     572          119 :     protoent * ptr(&p);
     573          119 :     if(getprotobyname_r(
     574              :               protocol.c_str()
     575              :             , &p
     576              :             , buf
     577              :             , sizeof(buf)
     578              :             , &ptr) != 0
     579          119 :     || ptr == nullptr)
     580              :     {
     581              :         throw addr_invalid_argument(
     582              :                   "unknown protocol named \""
     583            4 :                 + protocol
     584            6 :                 + "\", expected \"tcp\" or \"udp\" or another name from /etc/protocols.");
     585              :     }
     586          117 :     f_protocol = p.p_proto;
     587          117 : }
     588              : 
     589              : 
     590              : /** \brief Set the protocol to use to filter addresses.
     591              :  *
     592              :  * This function sets the protocol as one of the following:
     593              :  *
     594              :  * \li IPPROTO_IP -- only return IP address supporting the IP protocol
     595              :  * (this is offered because getaddrinfo() may return such IP addresses.)
     596              :  * \li IPPROTO_TCP -- only return IP address supporting TCP
     597              :  * \li IPPROTO_UDP -- only return IP address supporting UDP
     598              :  *
     599              :  * Any other value is refused. To reset the protocol to the default,
     600              :  * which is "do not filter by protocol", call the clear_protocol().
     601              :  *
     602              :  * \exception addr_invalid_argument_exception
     603              :  * If the string passed to this function is not one of the acceptable
     604              :  * protocols (ip, tcp, udp), then this exception is raised.
     605              :  *
     606              :  * \param[in] protocol  The default protocol for this parser.
     607              :  *
     608              :  * \sa clear_protocol()
     609              :  * \sa get_protocol()
     610              :  */
     611       132087 : void addr_parser::set_protocol(int const protocol)
     612              : {
     613              :     // make sure that's a protocol we support
     614              :     //
     615       132087 :     switch(protocol)
     616              :     {
     617       131887 :     case IPPROTO_IP:
     618              :     case IPPROTO_TCP:
     619              :     case IPPROTO_UDP:
     620       131887 :         break;
     621              : 
     622          200 :     default:
     623              :         throw addr_invalid_argument(
     624          800 :                   std::string("unknown protocol number \"")
     625          800 :                 + std::to_string(protocol)
     626          600 :                 + "\", expected \"tcp\" or \"udp\".");
     627              : 
     628              :     }
     629              : 
     630       131887 :     f_protocol = protocol;
     631       131887 : }
     632              : 
     633              : 
     634              : /** \brief Use this function to reset the protocol back to "no default."
     635              :  *
     636              :  * This function sets the protocol to -1 (which is something you cannot
     637              :  * do by calling the set_protocol() functions above.)
     638              :  *
     639              :  * The -1 special value means that the protocol is not defined, that
     640              :  * there is no default. In most cases this means all the addresses
     641              :  * that match, ignoring the protocol, will be returned by the parse()
     642              :  * function.
     643              :  *
     644              :  * \sa set_protocol()
     645              :  * \sa get_protocol()
     646              :  */
     647            3 : void addr_parser::clear_protocol()
     648              : {
     649            3 :     f_protocol = -1;
     650            3 : }
     651              : 
     652              : 
     653              : /** \brief Retrieve the protocol as defined by the set_protocol().
     654              :  *
     655              :  * This function returns the protocol number as defined by the
     656              :  * set_protocol.
     657              :  *
     658              :  * When defined, the protocol is used whenever we call the
     659              :  * getaddrinfo() function. In general, this means the IP addresses
     660              :  * returned will have  to match that protocol.
     661              :  *
     662              :  * This function may return -1. The value -1 is used as "do not
     663              :  * filter by protocol". The protocol can be set to -1 by calling
     664              :  * the clear_protocol() function.
     665              :  *
     666              :  * \return The parser default protocol.
     667              :  *
     668              :  * \sa set_protocol()
     669              :  * \sa clear_protocol()
     670              :  */
     671          216 : int addr_parser::get_protocol() const
     672              : {
     673          216 :     return f_protocol;
     674              : }
     675              : 
     676              : 
     677              : /** \brief Change the set of flags defining the sorting order.
     678              :  *
     679              :  * The parser, once done parsing all the input, will sort the addresses
     680              :  * according to these flags.
     681              :  *
     682              :  * By default, it does not re-arrange the order in which the addresses
     683              :  * were defined.
     684              :  *
     685              :  * The parser is capable of sorting by IP type (IPv6 or IPv4 first),
     686              :  * and simply by IP addresses. It can also merge adjacent or overlapping
     687              :  * ranges into a single range.
     688              :  *
     689              :  * \exception addr_invalid_argument
     690              :  * This exception is raised you set SORT_IPV6_FIRST and SORT_IPV4_FIRST
     691              :  * at the same time because these flags are mutually exclusive.
     692              :  *
     693              :  * \param[in] sort  The sort parameters.
     694              :  *
     695              :  * \sa get_sort_order()
     696              :  */
     697           12 : void addr_parser::set_sort_order(sort_t const sort)
     698              : {
     699           12 :     if((sort & (SORT_IPV6_FIRST | SORT_IPV4_FIRST)) == (SORT_IPV6_FIRST | SORT_IPV4_FIRST))
     700              :     {
     701            3 :         throw addr_invalid_argument("addr_parser::set_sort_order(): flags SORT_IPV6_FIRST and SORT_IPV4_FIRST are mutually exclusive.");
     702              :     }
     703              : 
     704           11 :     f_sort = sort;
     705           11 : }
     706              : 
     707              : 
     708              : /** \brief Get the flags defining the sort order of the parser.
     709              :  *
     710              :  * This function returns the sort order of the parser as a set of flags.
     711              :  *
     712              :  * By default this value is set to NO_SORT meaning that the input is
     713              :  * kept as is.
     714              :  *
     715              :  * \return The sort order flags.
     716              :  *
     717              :  * \sa set_sort_order()
     718              :  */
     719           23 : sort_t addr_parser::get_sort_order() const
     720              : {
     721           23 :     return f_sort;
     722              : }
     723              : 
     724              : 
     725              : /** \brief Set or clear allow flags in the parser.
     726              :  *
     727              :  * This parser has a set of flags it uses to know whether the input
     728              :  * string can include certain things such as a port or a mask.
     729              :  *
     730              :  * This function is used to allow or require certain parameters and
     731              :  * to disallow others.
     732              :  *
     733              :  * By default, the ADDRESS and PORT flags are set, meaning that an
     734              :  * address and a port can appear, but either or both are optinal.
     735              :  * If unspecified, then the default will be used. If not default
     736              :  * is defined, then the parser may fail in this situation.
     737              :  *
     738              :  * One problem is that we include contradictory syntatical features.
     739              :  * The parser supports lists of addresses separated by commas and
     740              :  * lists of ports separated by commas. Both are not supported
     741              :  * simultaneously. This means you want to allow multiple addresses
     742              :  * separated by commas, the function makes sure that the multiple
     743              :  * port separated by commas support is turned off.
     744              :  *
     745              :  * \li `ADDRESS` -- the IP address is allowed, but optional
     746              :  * \li `REQUIRED_ADDRESS` -- the IP address is mandatory
     747              :  * \li `PORT` -- the port is allowed, but optional
     748              :  * \li `REQUIRED_PORT` -- the port is mandatory
     749              :  * \li `MASK` -- the mask is allowed, but optional
     750              :  * \li `MULTI_ADDRESSES_COMMAS` -- the input can have multiple addresses
     751              :  * separated by commas (prevents MULTI_PORTS_COMMAS)
     752              :  * \li `MULTI_ADDRESSES_SPACES` -- the input can have multiple addresses
     753              :  * separated by spaces
     754              :  * \li `MULTI_ADDRESSES_NEWLINES` -- the input can have multiple addresses
     755              :  * separated by newlines (one address:port per line)
     756              :  * \li `MULTI_PORTS_SEMICOLONS` -- the input can  have multiple ports
     757              :  * separated by semicolons _NOT IMPLEMENTED YET_
     758              :  * \li `MULTI_PORTS_COMMAS` -- the input can have multiple ports separated
     759              :  * by commas (prevents MULTI_ADDRESSES_COMMAS) _NOT IMPLEMENTED YET_
     760              :  * \li `PORT_RANGE` -- the input supports port ranges (p1-p2) _NOT
     761              :  * IMPLEMENTED YET_
     762              :  * \li `ADDRESS_RANGE` -- the input supports address ranges (addr-addr) _NOT
     763              :  * IMPLEMENTED YET_
     764              :  *
     765              :  * The `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, and
     766              :  * `MULTI_ADDRESSES_NEWLINES` can be used together in which case any
     767              :  * number of both characters are accepted between addresses.
     768              :  *
     769              :  * Note that the `MULTI_ADDRESSES_COMMAS` and `MULTI_PORTS_COMMAS` are
     770              :  * mutually exclusive. The last set_allow() counts as the one you are
     771              :  * interested in.
     772              :  *
     773              :  * \param[in] flag  The flag to set or clear.
     774              :  * \param[in] allow  Whether to allow (true) or disallow (false).
     775              :  *
     776              :  * \sa get_allow()
     777              :  */
     778       397389 : void addr_parser::set_allow(allow_t const flag, bool const allow)
     779              : {
     780       397389 :     if(flag < static_cast<allow_t>(0)
     781       397369 :     || flag >= allow_t::ALLOW_max)
     782              :     {
     783          120 :         throw addr_invalid_argument("addr_parser::set_allow(): flag has to be one of the valid flags.");
     784              :     }
     785              : 
     786       397349 :     f_flags[static_cast<int>(flag)] = allow;
     787              : 
     788              :     // if we just set a certain flag, others may need to go to false
     789              :     //
     790       397349 :     if(allow)
     791              :     {
     792              :         // we can only support one type of commas
     793              :         //
     794       397022 :         switch(flag)
     795              :         {
     796           20 :         case allow_t::ALLOW_MULTI_ADDRESSES_COMMAS:
     797           20 :             f_flags[static_cast<int>(allow_t::ALLOW_MULTI_PORTS_COMMAS)] = false;
     798           20 :             break;
     799              : 
     800            2 :         case allow_t::ALLOW_MULTI_PORTS_COMMAS:
     801            2 :             f_flags[static_cast<int>(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS)] = false;
     802            2 :             break;
     803              : 
     804       397000 :         default:
     805       397000 :             break;
     806              : 
     807              :         }
     808              :     }
     809       397349 : }
     810              : 
     811              : 
     812              : /** \brief Retrieve the current statius of an allow flag.
     813              :  *
     814              :  * This function returns the current status of the allow flags.
     815              :  *
     816              :  * By default, the `ADDRESS` and `PORT` flags are set to true.
     817              :  * All the other flags are set to false.
     818              :  *
     819              :  * You may change the value of an allow flag by calling the
     820              :  * set_allow() function.
     821              :  *
     822              :  * \param[in] flag  Which flag is to be checked.
     823              :  *
     824              :  * \return The value of the flag: true or false.
     825              :  *
     826              :  * \sa set_allow()
     827              :  */
     828      1255844 : bool addr_parser::get_allow(allow_t const flag) const
     829              : {
     830      1255844 :     if(flag < static_cast<allow_t>(0)
     831      1255834 :     || flag >= allow_t::ALLOW_max)
     832              :     {
     833           60 :         throw addr_invalid_argument("addr_parser::get_allow(): flag has to be one of the valid flags.");
     834              :     }
     835              : 
     836      1255824 :     return f_flags[static_cast<int>(flag)];
     837              : }
     838              : 
     839              : 
     840              : /** \brief Check whether errors were registered so far.
     841              :  *
     842              :  * This function returns true if the system detected errors in one
     843              :  * of the previous calls to parse(). The flag can be cleared using
     844              :  * the clear_errors() function.
     845              :  *
     846              :  * On construction and after a call to clear_error(), this flag is
     847              :  * always false. If you are to call parser() multiple times with
     848              :  * the same addr_parser object, then you want to make sure to call
     849              :  * the clear_errors() function before calling the parse() function.
     850              :  * Otherwise you won't know whether errors occurred in a earlier
     851              :  * or later call.
     852              :  *
     853              :  * \code
     854              :  *      // first time, not required
     855              :  *      parser.parse(...);
     856              :  *      ...
     857              :  *
     858              :  *      // next time, required
     859              :  *      parser.clear_errors();
     860              :  *      parser.parse(...);
     861              :  *      ...
     862              :  * \endcode
     863              :  *
     864              :  * \return true if errors were generated.
     865              :  */
     866       132228 : bool addr_parser::has_errors() const
     867              : {
     868       132228 :     return !f_error.empty();
     869              : }
     870              : 
     871              : 
     872              : /** \brief Emit an error and save it in this class.
     873              :  *
     874              :  * This function adds the message to the error string part of this
     875              :  * object. A newline is also added at the end of the message.
     876              :  *
     877              :  * Next the function increments the error counter.
     878              :  *
     879              :  * \note
     880              :  * You are expected to emit one error at a time. If you want to
     881              :  * emit several messages in a row, that will work and properly
     882              :  * count each message.
     883              :  *
     884              :  * \param[in] msg  The message to add to the parser error messages.
     885              :  *
     886              :  * \sa error_messages()
     887              :  */
     888          136 : void addr_parser::emit_error(std::string const & msg)
     889              : {
     890          136 :     f_error += msg;
     891          136 :     f_error += "\n";
     892          136 :     ++f_error_count;
     893          136 : }
     894              : 
     895              : 
     896              : /** \brief Return the current error messages.
     897              :  *
     898              :  * The error messages are added to the addr_parser using the
     899              :  * emit_error() function.
     900              :  *
     901              :  * This function does not clear the list of error messages.
     902              :  * To do that, call the clear_errors() function.
     903              :  *
     904              :  * The number of messages can be determined by counting the
     905              :  * number of "\n" characters in the string. The error_count()
     906              :  * will return that same number (assuming no message included
     907              :  * a '\n' character when emit_error() was called.)
     908              :  *
     909              :  * \return A string with the list of messages.
     910              :  *
     911              :  * \sa emit_error()
     912              :  * \sa clear_errors()
     913              :  */
     914           86 : std::string const & addr_parser::error_messages() const
     915              : {
     916           86 :     return f_error;
     917              : }
     918              : 
     919              : 
     920              : /** \brief Return the number of error messages that were emitted.
     921              :  *
     922              :  * Each time the emit_error() function is called, the error
     923              :  * counter is incremented by 1. This function returns that
     924              :  * error counter.
     925              :  *
     926              :  * The clear_errors() function can be used to clear the
     927              :  * counter back to zero.
     928              :  *
     929              :  * \return The number of errors that were emitted so far.
     930              :  *
     931              :  * \sa emit_error()
     932              :  */
     933           63 : int addr_parser::error_count() const
     934              : {
     935           63 :     return f_error_count;
     936              : }
     937              : 
     938              : 
     939              : /** \brief Clear the error message and error counter.
     940              :  *
     941              :  * This function clears all the error messages and reset the
     942              :  * counter back to zero. In order words, it will be possible
     943              :  * to tell how many times the emit_error() was called since
     944              :  * the start or the last clear_errors() call.
     945              :  *
     946              :  * To retrieve a copy of the error counter, use the error_count()
     947              :  * function.
     948              :  *
     949              :  * \sa error_count()
     950              :  */
     951          171 : void addr_parser::clear_errors()
     952              : {
     953          171 :     f_error.clear();
     954          171 :     f_error_count = 0;
     955          171 : }
     956              : 
     957              : 
     958              : /** \brief Parse a string of addresses, ports, and masks.
     959              :  *
     960              :  * This function is used to parse the list of addresses defined
     961              :  * in the \p in parameter.
     962              :  *
     963              :  * One address is composed of one to three elements:
     964              :  *
     965              :  * \code
     966              :  *          [ address ] [ ':' port ] [ '/' mask ]
     967              :  * \endcode
     968              :  *
     969              :  * Although all three elements are optional (at least by default),
     970              :  * a valid address is expected to include at least one of the
     971              :  * three elements. (i.e. an empty string is just skipped silently.)
     972              :  *
     973              :  * ### Multiple Addresses
     974              :  *
     975              :  * Multiple addresses can be defined if at least one of the
     976              :  * `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, or
     977              :  * `MULTI_ADDRESSES_NEWLINES` allow flags is set to true.
     978              :  *
     979              :  * The separator characters are limited to what is allowed. If all three
     980              :  * flags are set, then all the characters are allowed and are viewed as
     981              :  * valid address separators.
     982              :  *
     983              :  * ### Make Address Field Required
     984              :  *
     985              :  * To make the address field a required field, set the
     986              :  * `REQUIRED_ADDRESS` flag (see set_allow()) to true and do not define a
     987              :  * default address (see set_default_address()).
     988              :  *
     989              :  * ### Make Port Field Required
     990              :  *
     991              :  * To make the port field a required fiel, set the `REQUIRED_PORT`
     992              :  * flag (see set_allow()) to true and do not define a default port
     993              :  * (see set_default_port()).
     994              :  *
     995              :  * ### Allow Mask
     996              :  *
     997              :  * The mask cannot be made mandatory. However, you have to set
     998              :  * the `MASK` flag to true to allow it. By default it is not
     999              :  * allowed.
    1000              :  *
    1001              :  * ### Ranges
    1002              :  *
    1003              :  * Ranges are not yet implemented.
    1004              :  *
    1005              :  * ### Sort
    1006              :  *
    1007              :  * After the function parsed all the input, it sorts the results in
    1008              :  * the vector of ranges. Ranges are sorted using the addr_range::compare()
    1009              :  * function. Sorting can also be used to merge ranges. So if two ranges
    1010              :  * have an overlap or are adjacent, the union of those two ranges will
    1011              :  * be kept and the two ranges are otherwise removed from the result.
    1012              :  *
    1013              :  * The sort is particularly useful if you first want to connect with
    1014              :  * IPv6 addresses instead of IPv4 (which is the current expected behavior
    1015              :  * of your services and tools).
    1016              :  *
    1017              :  * \todo
    1018              :  * The ALLOW_COMMENT_HASH and ALLOW_COMMENT_SEMICOLON currently gives the
    1019              :  * user a way to comment with any type of separator (commas, spaces or new
    1020              :  * lines). I think that in the following, everything after the # should be
    1021              :  * viewed as a comment. Right now, the 3rd IP:port is viewed as a valid entry:
    1022              :  * \todo
    1023              :  * \code
    1024              :  *     127.0.0.1,#10.0.0.1,192.168.0.1
    1025              :  * \endcode
    1026              :  * \todo
    1027              :  * So the result is: 127.0.0.1 and 192.168.0.1. Some day, though, that
    1028              :  * line would be viewed as just 127.0.0.1.
    1029              :  *
    1030              :  * \param[in] in  The input string to be parsed.
    1031              :  *
    1032              :  * \return A vector of address ranges, see has_errors() to determine whether
    1033              :  * errors occurred while parsing the input.
    1034              :  *
    1035              :  * \sa has_errors()
    1036              :  */
    1037       132078 : addr_range::vector_t addr_parser::parse(std::string const & in)
    1038              : {
    1039       132078 :     addr_range::vector_t result;
    1040       132078 :     bool new_lines_allowed(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_NEWLINES));
    1041              : 
    1042       132078 :     std::string separators;
    1043       132078 :     if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS))
    1044              :     {
    1045           39 :         separators += ',';
    1046              :     }
    1047       132078 :     if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_SPACES))
    1048              :     {
    1049           26 :         separators += ' ';
    1050              :     }
    1051       132078 :     if(new_lines_allowed)
    1052              :     {
    1053              :         // TBD: consider supporting '\r'?
    1054              :         //
    1055           26 :         separators += '\n';
    1056              :     }
    1057              : 
    1058       132078 :     std::string const comment_chars(
    1059       528312 :               std::string(get_allow(allow_t::ALLOW_COMMENT_HASH) ? "#" : "")
    1060       264156 :                        + (get_allow(allow_t::ALLOW_COMMENT_SEMICOLON) ? ";" : ""));
    1061              : 
    1062       132078 :     if(separators.empty())
    1063              :     {
    1064       132036 :         std::string::size_type ec(in.length());
    1065       132036 :         std::string::size_type s(0);
    1066       132066 :         while(s < in.length() && isspace(in[s]))
    1067              :         {
    1068           30 :             ++s;
    1069              :         }
    1070       132036 :         if(!comment_chars.empty())
    1071              :         {
    1072           16 :             auto const comment(std::find_first_of(in.begin() + s, in.end(), comment_chars.begin(), comment_chars.end()));
    1073           16 :             if(comment != in.end())
    1074              :             {
    1075           12 :                 ec = comment - in.begin();
    1076              :             }
    1077              :         }
    1078       132050 :         while(ec > 0 && isspace(in[ec - 1]))
    1079              :         {
    1080           14 :             --ec;
    1081              :         }
    1082              : 
    1083       132036 :         if(s != 0
    1084       132036 :         || ec != in.length())
    1085              :         {
    1086           22 :             parse_cidr(in.substr(s, ec - s), result);
    1087              :         }
    1088              :         else
    1089              :         {
    1090       132014 :             parse_cidr(in, result);
    1091              :         }
    1092              :     }
    1093              :     else
    1094              :     {
    1095           42 :         std::string::size_type s(0);
    1096          198 :         while(s < in.length())
    1097              :         {
    1098          156 :             auto const it(std::find_first_of(in.begin() + s, in.end(), separators.begin(), separators.end()));
    1099          156 :             std::string::size_type e(it - in.begin());
    1100          156 :             if(e > s)
    1101              :             {
    1102          142 :                 std::string::size_type ec(e);
    1103          142 :                 if(!comment_chars.empty())
    1104              :                 {
    1105           57 :                     auto const comment(std::find_first_of(in.begin() + s, in.begin() + ec, comment_chars.begin(), comment_chars.end()));
    1106           57 :                     if(comment != in.begin() + ec)
    1107              :                     {
    1108           17 :                         ec = comment - in.begin();
    1109              :                     }
    1110           57 :                     if(new_lines_allowed
    1111           48 :                     && e < in.length()
    1112          105 :                     && in[e] != '\n')
    1113              :                     {
    1114            8 :                         auto const nl(std::find(in.begin() + e, in.end(), '\n'));
    1115            8 :                         e = nl - in.begin();
    1116              :                     }
    1117              :                 }
    1118              : 
    1119              :                 // ignore lines with just a comment
    1120              :                 //
    1121          142 :                 if(ec > s)
    1122              :                 {
    1123          134 :                     parse_cidr(in.substr(s, ec - s), result);
    1124              :                 }
    1125              :             }
    1126          156 :             s = e + 1;
    1127              :         }
    1128              :     }
    1129              : 
    1130              :     // run a normal sort first then attempt a merge if requested
    1131              :     //
    1132       132078 :     if((f_sort & (SORT_FULL | SORT_MERGE)) != 0)
    1133              :     {
    1134            7 :         std::stable_sort(result.begin(), result.end());
    1135              :     }
    1136              : 
    1137       132078 :     if((f_sort & SORT_MERGE) != 0)
    1138              :     {
    1139            6 :         std::size_t max(result.size());
    1140            6 :         if(max > 1)
    1141              :         {
    1142           44 :             for(std::size_t idx(0); idx < max - 1; ++idx)
    1143              :             {
    1144           38 :                 addr_range const r(result[idx].union_if_possible(result[idx + 1]));
    1145           38 :                 if(r.is_defined()
    1146           38 :                 && !r.is_empty())
    1147              :                 {
    1148              :                     // merge worked, update the vector
    1149              :                     //
    1150            6 :                     result[idx] = r;
    1151            6 :                     result.erase(result.begin() + idx + 1);
    1152            6 :                     --max;
    1153            6 :                     --idx;
    1154              :                 }
    1155           38 :             }
    1156              :         }
    1157              :     }
    1158              : 
    1159              :     // move IPv4 or IPv6 first (should be IPv6 in newer systems)
    1160              :     //
    1161       132078 :     if((f_sort & SORT_IPV4_FIRST) != 0)
    1162              :     {
    1163            2 :         std::stable_sort(
    1164              :               result.begin()
    1165              :             , result.end()
    1166           35 :             , [](auto const & a, auto const & b)
    1167              :             {
    1168           35 :                 switch(a.compare(b))
    1169              :                 {
    1170           10 :                 case compare_t::COMPARE_IPV4_VS_IPV6:
    1171              :                 case compare_t::COMPARE_FIRST:
    1172           10 :                     return true;
    1173              : 
    1174           25 :                 default:
    1175           25 :                     return false;
    1176              : 
    1177              :                 }
    1178              :             });
    1179              :     }
    1180       132076 :     else if((f_sort & SORT_IPV6_FIRST) != 0)
    1181              :     {
    1182            5 :         std::stable_sort(
    1183              :               result.begin()
    1184              :             , result.end()
    1185           68 :             , [](auto const & a, auto const & b)
    1186              :             {
    1187           68 :                 switch(a.compare(b))
    1188              :                 {
    1189           21 :                 case compare_t::COMPARE_IPV6_VS_IPV4:
    1190              :                 case compare_t::COMPARE_FIRST:
    1191           21 :                     return true;
    1192              : 
    1193           47 :                 default:
    1194           47 :                     return false;
    1195              : 
    1196              :                 }
    1197              :             });
    1198              :     }
    1199              : 
    1200       264156 :     return result;
    1201       132078 : }
    1202              : 
    1203              : 
    1204              : /** \brief Check one address.
    1205              :  *
    1206              :  * This function checks one address, although if it is a name, it could
    1207              :  * represent multiple IP addresses.
    1208              :  *
    1209              :  * This function separate the address:port from the mask if the mask is
    1210              :  * allowed. Then it parses the address:port part and the mask separately.
    1211              :  *
    1212              :  * \param[in] in  The address to parse.
    1213              :  * \param[in,out] result  The list of resulting addresses.
    1214              :  */
    1215       132170 : void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
    1216              : {
    1217       132170 :     std::string address(snapdev::trim_string(in));
    1218       132170 :     if(get_allow(allow_t::ALLOW_MASK))
    1219              :     {
    1220              :         // check whether there is a mask
    1221              :         //
    1222          547 :         std::string mask;
    1223              : 
    1224          547 :         std::string::size_type const p(address.find('/'));
    1225          547 :         if(p != std::string::npos)
    1226              :         {
    1227          454 :             mask = address.substr(p + 1);
    1228          454 :             address = address.substr(0, p);
    1229              :         }
    1230              : 
    1231          547 :         int const errcnt(f_error_count);
    1232              : 
    1233              :         // handle the address first
    1234              :         //
    1235          547 :         addr_range::vector_t addr_mask;
    1236          547 :         bool const is_ipv4(parse_address(address, mask, addr_mask));
    1237              : 
    1238              :         // now check for the mask
    1239              :         //
    1240         1137 :         for(auto & am : addr_mask)
    1241              :         {
    1242          590 :             std::string m(mask);
    1243          590 :             if(m.empty())
    1244              :             {
    1245              :                 // the mask was not defined in the input, then adapt it to
    1246              :                 // the type of address we got in 'am'
    1247              :                 //
    1248          116 :                 if(is_ipv4)
    1249              :                 {
    1250           77 :                     m = f_default_mask4;
    1251              :                 }
    1252           39 :                 else if(!f_default_mask6.empty())
    1253              :                 {
    1254              :                     // parse_mask() expects '[...]' around IPv6 addresses
    1255              :                     // we remove them when set_mask() is called
    1256              :                     //
    1257              :                     // however, we have to make sure that we do not add
    1258              :                     // brackets around a simple decimal number (i.e. a
    1259              :                     // CIDR opposed to an IPv6 address)
    1260              :                     //
    1261           37 :                     if(f_default_mask6.find(':') != std::string::npos)
    1262              :                     {
    1263           25 :                         m = "[" + f_default_mask6 + "]";
    1264              :                     }
    1265              :                 }
    1266              :             }
    1267              : 
    1268              :             // TODO: the am.get_from() may be wrong since now we support
    1269              :             //       ranges so it could require am.get_to() instead
    1270              :             //
    1271          590 :             parse_mask(m, am.get_from(), is_ipv4 && am.get_from().is_ipv4());
    1272          590 :         }
    1273              : 
    1274              :         // now append the list to the result if no errors occurred
    1275              :         //
    1276          547 :         if(errcnt == f_error_count)
    1277              :         {
    1278          450 :             result.insert(result.end(), addr_mask.begin(), addr_mask.end());
    1279              :         }
    1280          547 :     }
    1281              :     else
    1282              :     {
    1283              :         // no mask allowed, if there is one, this call will fail
    1284              :         //
    1285       131623 :         parse_address(address, std::string(), result);
    1286              :     }
    1287       264340 : }
    1288              : 
    1289              : 
    1290              : /** \brief Parse one address.
    1291              :  *
    1292              :  * This function is called with one address. It determines whether we
    1293              :  * are dealing with an IPv4 or an IPv6 address and call the
    1294              :  * corresponding sub-function.
    1295              :  *
    1296              :  * An address is considered an IPv6 address if it starts with a '['
    1297              :  * character.
    1298              :  *
    1299              :  * \note
    1300              :  * The input cannot include a mask. It has to already have been
    1301              :  * removed.
    1302              :  *
    1303              :  * \note
    1304              :  * The mask parameter is only used to determine whether this function
    1305              :  * is being called with an IPv6 or not. It is otherwise ignored.
    1306              :  *
    1307              :  * \param[in] in  The input address eventually including a port.
    1308              :  * \param[in] mask  The mask used to determine whether we are dealing with
    1309              :  *                  an IPv6 or not.
    1310              :  * \param[in,out] result  The list of resulting addresses.
    1311              :  *
    1312              :  * \return true if the address was parsed as an IPv4 address, false if it
    1313              :  * was determined to be an IPv6 address.
    1314              :  */
    1315       132170 : bool addr_parser::parse_address(
    1316              :       std::string const & in
    1317              :     , std::string const & mask
    1318              :     , addr_range::vector_t & result)
    1319              : {
    1320              :     // if the number of colons is 2 or more, the address has to be an
    1321              :     // IPv6 address so we have a very special case at the start for that
    1322              :     //
    1323       132170 :     std::ptrdiff_t const colons(std::count(in.begin(), in.end(), ':'));
    1324       132170 :     if(colons >= 2LL)
    1325              :     {
    1326        65881 :         parse_address6(in, colons, result);
    1327        65881 :         return false;
    1328              :     }
    1329              : 
    1330        66289 :     if(in.empty()
    1331        66289 :     || in[0] == ':')    // if it start with ':' then there is no address
    1332              :     {
    1333              :         // if the address is empty, then use the mask to determine the
    1334              :         // type of IP address (note: if the address starts with ':'
    1335              :         // it is considered empty since an IPv6 would have a '[' at
    1336              :         // the start)
    1337              :         //
    1338          326 :         if(!mask.empty())
    1339              :         {
    1340          115 :             if(mask[0] == '[')
    1341              :             {
    1342              :                 // IPv6 parsing
    1343              :                 //
    1344            5 :                 parse_address6(in, colons, result);
    1345            5 :                 return false;
    1346              :             }
    1347              :             else
    1348              :             {
    1349              :                 // if the number is 33 or more, it has to be IPv6, otherwise
    1350              :                 // we cannot know...
    1351              :                 //
    1352          110 :                 int mask_count(0);
    1353          372 :                 for(char const * s(mask.c_str()); *s != '\0'; ++s)
    1354              :                 {
    1355          276 :                     if(*s >= '0' && *s <= '9')
    1356              :                     {
    1357          267 :                         mask_count = mask_count * 10 + *s - '0';
    1358          267 :                         if(mask_count > 1000)
    1359              :                         {
    1360              :                             // not valid
    1361              :                             //
    1362            5 :                             mask_count = -1;
    1363            5 :                             break;;
    1364              :                         }
    1365              :                     }
    1366              :                     else
    1367              :                     {
    1368              :                         // not a valid decimal number
    1369              :                         //
    1370            9 :                         mask_count = -1;
    1371            9 :                         break;
    1372              :                     }
    1373              :                 }
    1374          110 :                 if(mask_count > 32)
    1375              :                 {
    1376           96 :                     parse_address6(in, colons, result);
    1377           96 :                     return false;
    1378              :                 }
    1379              :                 else
    1380              :                 {
    1381           14 :                     parse_address4(in, result);
    1382           14 :                     return true;
    1383              :                 }
    1384              :             }
    1385              :         }
    1386              :         else
    1387              :         {
    1388          211 :             if(f_default_address4.empty()
    1389          211 :             && !f_default_address6.empty())
    1390              :             {
    1391          102 :                 parse_address6(in, colons, result);
    1392          102 :                 return false;
    1393              :             }
    1394              :             else
    1395              :             {
    1396          109 :                 parse_address4(in, result);
    1397          109 :                 return true;
    1398              :             }
    1399              :         }
    1400              :     }
    1401              :     else
    1402              :     {
    1403              :         // if an address has a ']' then it is IPv6 even if the '['
    1404              :         // is missing, that being said, it is still considered
    1405              :         // invalid as per our processes
    1406              :         //
    1407        65963 :         if(in[0] == '['
    1408        65963 :         || in.find(']') != std::string::npos)
    1409              :         {
    1410            2 :             parse_address6(in, colons, result);
    1411            2 :             return false;
    1412              :         }
    1413              :         else
    1414              :         {
    1415              :             // if there is no port, then a ':' can be viewed as an IPv6
    1416              :             // address because there is no other ':', but if there are
    1417              :             // '.' before the ':' then we assume that it is IPv4 still
    1418              :             //
    1419        65961 :             if(!get_allow(allow_t::ALLOW_PORT)
    1420        65961 :             && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1421              :             {
    1422            4 :                 std::string::size_type const p(in.find(':'));
    1423            4 :                 if(p != std::string::npos
    1424            4 :                 && in.find('.') > p)
    1425              :                 {
    1426            1 :                     parse_address6(in, colons, result);
    1427            1 :                     return false;
    1428              :                 }
    1429              :                 else
    1430              :                 {
    1431            3 :                     parse_address4(in, result);
    1432            3 :                     return true;
    1433              :                 }
    1434              :             }
    1435              :             else
    1436              :             {
    1437        65957 :                 parse_address4(in, result);
    1438        65957 :                 return true;
    1439              :             }
    1440              :         }
    1441              :     }
    1442              : }
    1443              : 
    1444              : 
    1445              : /** \brief Parse one IPv4 address.
    1446              :  *
    1447              :  * This function checks the input parameter \p in and extracts the
    1448              :  * address and port. There is a port if the input strings includes
    1449              :  * a `':'` character.
    1450              :  *
    1451              :  * If this function detects that a port is not allowed and yet
    1452              :  * a `':'` character is found, then it generates an error and
    1453              :  * returns without adding anything to `result`.
    1454              :  *
    1455              :  * \param[in] in  The input string with the address and optional port.
    1456              :  * \param[in,out] result  The list of resulting addresses.
    1457              :  */
    1458        66083 : void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
    1459              : {
    1460        66083 :     std::string address;
    1461        66083 :     std::string port_str;
    1462              : 
    1463        66083 :     std::string::size_type const p(in.find(':'));
    1464              : 
    1465        66083 :     if(get_allow(allow_t::ALLOW_PORT)
    1466        66083 :     || get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1467              :     {
    1468              :         // the address can include a port
    1469              :         //
    1470        66079 :         if(p != std::string::npos)
    1471              :         {
    1472        65933 :             address = in.substr(0, p);
    1473        65933 :             port_str = in.substr(p + 1);
    1474              :         }
    1475              :         else
    1476              :         {
    1477          146 :             address = in;
    1478              :         }
    1479              :     }
    1480            4 :     else if(p == std::string::npos)
    1481              :     {
    1482            3 :         address = in;
    1483              :     }
    1484              :     else
    1485              :     {
    1486            1 :         emit_error("Port not allowed (" + in + ").");
    1487            1 :         return;
    1488              :     }
    1489              : 
    1490        66082 :     parse_address_range_port(address, port_str, result, false);
    1491        66084 : }
    1492              : 
    1493              : 
    1494              : /** \brief Parse one IPv6 address.
    1495              :  *
    1496              :  * This function checks the input parameter \p in and extracts the
    1497              :  * address and port. There is a port if the input strings includes
    1498              :  * a `':'` character after the closing square bracket (`']'`).
    1499              :  *
    1500              :  * If this function detects that a port is not allowed and yet
    1501              :  * a `':'` character is found, then it generates an error and
    1502              :  * returns without adding anything to `result`.
    1503              :  *
    1504              :  * \note
    1505              :  * This function is expected to be called with an IPv6.
    1506              :  *
    1507              :  * \param[in] in  The input string with the address and optional port.
    1508              :  * \param[in] colons  The number of colons in \p in.
    1509              :  * \param[in,out] result  The list of resulting addresses.
    1510              :  */
    1511        66087 : void addr_parser::parse_address6(std::string const & in, std::size_t const colons, addr_range::vector_t & result)
    1512              : {
    1513        66087 :     std::string address;
    1514        66087 :     std::string port_str;
    1515              : 
    1516              :     // remove the square brackets if present
    1517              :     //
    1518        66087 :     if(!in.empty()
    1519        66087 :     && in[0] == '[')
    1520              :     {
    1521        65779 :         std::string::size_type p(in.find(']'));
    1522              : 
    1523        65779 :         if(p == std::string::npos)
    1524              :         {
    1525            1 :             emit_error("IPv6 is missing the ']' (" + in + ").");
    1526            1 :             return;
    1527              :         }
    1528              : 
    1529        65778 :         address = in.substr(1, p - 1);
    1530              : 
    1531        65778 :         ++p;
    1532        65778 :         if(p < in.length())
    1533              :         {
    1534        65770 :             if(in[p] != ':')
    1535              :             {
    1536            1 :                 emit_error("The IPv6 address \"" + in + "\" is followed by unknown data.");
    1537            1 :                 return;
    1538              :             }
    1539              : 
    1540        65769 :             if(!get_allow(allow_t::ALLOW_PORT)
    1541        65769 :             && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1542              :             {
    1543              :                 // even just a ':' is no allowed in this case
    1544              :                 //
    1545            1 :                 emit_error("Port not allowed (" + in + ").");
    1546            1 :                 return;
    1547              :             }
    1548              : 
    1549        65768 :             port_str = in.substr(p + 1);
    1550              :         }
    1551              :     }
    1552          308 :     else if(colons == 1)
    1553              :     {
    1554              :         // this usually happens when only a port was specified
    1555              :         // (so here p == 0 will be true 99% of the time)
    1556              :         //
    1557          202 :         std::string::size_type const p(in.find(':'));
    1558          202 :         if(p == std::string::npos)
    1559              :         {
    1560              :             throw logic_error("colons == 1 & we did not find it!"); // LCOV_EXCL_LINE
    1561              :         }
    1562          202 :         address = in.substr(0, p);
    1563          202 :         port_str = in.substr(p + 1);
    1564              :     }
    1565              :     else
    1566              :     {
    1567          106 :         address = in;
    1568              :     }
    1569              : 
    1570        66084 :     parse_address_range_port(address, port_str, result, true);
    1571        66090 : }
    1572              : 
    1573              : 
    1574              : /** \brief Parse an address range and a port.
    1575              :  *
    1576              :  * This function checks whether the address part includes a dash, if so, it
    1577              :  * is considered an address range and the function transforms it in a "from"
    1578              :  * and a "to" set of addresses.
    1579              :  *
    1580              :  * This function emits an error if the address is just a dash (-). If you
    1581              :  * want to get the default IP address, use the empty string instead.
    1582              :  *
    1583              :  * \note
    1584              :  * The address range has to be enabled for it to be active. If a range is
    1585              :  * not allowed, then any '-' is ignored. That also allows you to use domain
    1586              :  * names that may otherwise include a dash character (i.e. "bad-domain.com").
    1587              :  *
    1588              :  * \param[in] addresses  One or two addresses separated by a dash (-).
    1589              :  * \param[in] port_str  The port as a string.
    1590              :  * \param[out] result  The vector where the results get saved.
    1591              :  * \param[in] ipv6  Whether the parser needs to use AF_INET or AF_INET6.
    1592              :  */
    1593       132166 : void addr_parser::parse_address_range_port(
    1594              :       std::string const & addresses
    1595              :     , std::string const & port_str
    1596              :     , addr_range::vector_t & result
    1597              :     , bool ipv6)
    1598              : {
    1599       132166 :     std::string::size_type p(std::string::npos);
    1600       132166 :     if(get_allow(allow_t::ALLOW_ADDRESS_RANGE))
    1601              :     {
    1602          118 :         p = addresses.find('-');
    1603              :     }
    1604       132166 :     if(p == std::string::npos)
    1605              :     {
    1606       132112 :         parse_address_port(addresses, port_str, result, ipv6);
    1607       132112 :         return;
    1608              :     }
    1609              : 
    1610           54 :     std::string const from(addresses.substr(0, p));
    1611           54 :     std::string const to(addresses.substr(p + 1));
    1612              : 
    1613           54 :     if(from.empty()
    1614           54 :     && to.empty())
    1615              :     {
    1616            6 :         emit_error("An address range requires at least one of the \"from\" or \"to\" addresses.");
    1617            2 :         return;
    1618              :     }
    1619              : 
    1620           52 :     addr_range::vector_t from_result;
    1621           52 :     if(!from.empty())
    1622              :     {
    1623           49 :         parse_address_port_ignore_duplicates(from, port_str, from_result, ipv6);
    1624           49 :         if(from_result.size() > 1)
    1625              :         {
    1626              :             emit_error("The \"from\" of an address range must be exactly one address."); // LCOV_EXCL_LINE
    1627              :             return; // LCOV_EXCL_LINE
    1628              :         }
    1629           49 :         if(from_result.empty())
    1630              :         {
    1631              :             // parse_address_port_ignore_duplicates() failed
    1632              :             //
    1633            1 :             return;
    1634              :         }
    1635              :     }
    1636              : 
    1637           51 :     addr_range::vector_t to_result;
    1638           51 :     if(!to.empty())
    1639              :     {
    1640              :         // our parse_address_port_ignore_duplicates() function sees the input address as
    1641              :         // the "from" address; the following moves that result to the
    1642              :         // "to" address of our own result (assuming the parsing worked
    1643              :         // as expected)
    1644              :         //
    1645           49 :         parse_address_port_ignore_duplicates(to, port_str, to_result, ipv6);
    1646           49 :         if(to_result.size() > 1)
    1647              :         {
    1648              :             emit_error("The \"to\" of an address range must be exactly one address."); // LCOV_EXCL_LINE
    1649              :             return; // LCOV_EXCL_LINE
    1650              :         }
    1651           49 :         if(to_result.empty())
    1652              :         {
    1653              :             // parse_address_port_ignore_duplicates() failed
    1654              :             //
    1655            1 :             return;
    1656              :         }
    1657           48 :         to_result[0].swap_from_to();
    1658              :     }
    1659              : 
    1660           50 :     if(!from_result.empty()
    1661           50 :     && !to_result.empty())
    1662              :     {
    1663           46 :         from_result[0].set_to(to_result[0].get_to());
    1664           92 :         if((f_sort & SORT_NO_EMPTY) == 0
    1665           46 :         || !from_result[0].is_empty())
    1666              :         {
    1667           40 :             result.push_back(from_result[0]);
    1668              :         }
    1669              :     }
    1670            4 :     else if(!from_result.empty())
    1671              :     {
    1672            2 :         result.push_back(from_result[0]);
    1673              :     }
    1674              :     else //if(!to_result.empty())
    1675              :     {
    1676            2 :         result.push_back(to_result[0]);
    1677              :     }
    1678           61 : }
    1679              : 
    1680              : 
    1681              : /** \brief Parse the address and port.
    1682              :  *
    1683              :  * This function receives an address and a port string and
    1684              :  * convert them in an addr object which gets saved in
    1685              :  * the specified result range vector.
    1686              :  *
    1687              :  * The address can be an IPv4 or an IPv6 address.
    1688              :  *
    1689              :  * The port may be numeric or a name such as `"http"`.
    1690              :  *
    1691              :  * \note
    1692              :  * When this function gets called with an empty string as the address or
    1693              :  * the port, then it makes use of the user defined default unless that
    1694              :  * default string is also empty in which case it uses a system default.
    1695              :  *
    1696              :  * \param[in] address  The address to convert to binary.
    1697              :  * \param[in] port_str  The port as a string.
    1698              :  * \param[out] result  The range where we save the results.
    1699              :  * \param[in] ipv6  Use the default IPv6 address if the address is empty.
    1700              :  */
    1701       132210 : void addr_parser::parse_address_port(
    1702              :       std::string address
    1703              :     , std::string port_str
    1704              :     , addr_range::vector_t & result
    1705              :     , bool ipv6)
    1706              : {
    1707              :     // make sure the port is good
    1708              :     //
    1709       132210 :     bool const defined_port(!port_str.empty());
    1710       132210 :     if(!defined_port)
    1711              :     {
    1712          334 :         if(get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1713              :         {
    1714           18 :             emit_error("Required port is missing.");
    1715            6 :             return;
    1716              :         }
    1717          328 :         if(f_default_port != -1)
    1718              :         {
    1719           69 :             port_str = std::to_string(f_default_port);
    1720              :         }
    1721              :     }
    1722              : 
    1723              :     // make sure the address is good
    1724              :     //
    1725       132204 :     if(address.empty())
    1726              :     {
    1727          326 :         if(get_allow(allow_t::ALLOW_REQUIRED_ADDRESS))
    1728              :         {
    1729            9 :             emit_error("Required address is missing.");
    1730            3 :             return;
    1731              :         }
    1732              :         // internal default if no address was defined
    1733              :         //
    1734          323 :         if(ipv6)
    1735              :         {
    1736          203 :             if(f_default_address6.empty())
    1737              :             {
    1738          101 :                 address = "::";
    1739              :             }
    1740              :             else
    1741              :             {
    1742          102 :                 address = f_default_address6;
    1743              :             }
    1744              :         }
    1745              :         else
    1746              :         {
    1747          120 :             if(f_default_address4.empty())
    1748              :             {
    1749           15 :                 address = "0.0.0.0";
    1750              :             }
    1751              :             else
    1752              :             {
    1753          105 :                 address = f_default_address4;
    1754              :             }
    1755              :         }
    1756              :     }
    1757              : 
    1758              :     // prepare hints for the the getaddrinfo() function
    1759              :     //
    1760       132201 :     addrinfo hints = {};
    1761       132201 :     hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
    1762       132201 :     hints.ai_family = AF_UNSPEC;
    1763              : 
    1764       132201 :     switch(f_protocol)
    1765              :     {
    1766        66190 :     case IPPROTO_TCP:
    1767        66190 :         hints.ai_socktype = SOCK_STREAM;
    1768        66190 :         hints.ai_protocol = IPPROTO_TCP;
    1769        66190 :         break;
    1770              : 
    1771        65832 :     case IPPROTO_UDP:
    1772        65832 :         hints.ai_socktype = SOCK_DGRAM;
    1773        65832 :         hints.ai_protocol = IPPROTO_UDP;
    1774        65832 :         break;
    1775              : 
    1776              :     }
    1777              : 
    1778              :     // convert address to binary
    1779              :     //
    1780       132201 :     if(get_allow(allow_t::ALLOW_ADDRESS_LOOKUP))
    1781              :     {
    1782       132098 :         addrinfo * addrlist(nullptr);
    1783              :         {
    1784       132098 :             errno = 0;
    1785       132098 :             char const * service(port_str.c_str());
    1786       132098 :             if(port_str.empty())
    1787              :             {
    1788          192 :                 service = "0"; // fallback to port 0 when unspecified
    1789              :             }
    1790       132098 :             int const r(getaddrinfo(address.c_str(), service, &hints, &addrlist));
    1791       132098 :             if(r != 0)
    1792              :             {
    1793              :                 // break on invalid addresses
    1794              :                 //
    1795            9 :                 int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
    1796           18 :                 emit_error(
    1797              :                           "Invalid address in \""
    1798           18 :                         + address
    1799           36 :                         + (port_str.empty() ? "" : ":")
    1800           36 :                         + port_str
    1801           36 :                         + "\" error "
    1802           36 :                         + std::to_string(r)
    1803           36 :                         + " -- "
    1804           36 :                         + gai_strerror(r)
    1805           45 :                         + (e == 0
    1806           38 :                             ? ""
    1807              :                             : " (errno: "
    1808           16 :                             + std::to_string(e)
    1809           23 :                             + " -- "
    1810           23 :                             + strerror(e)
    1811              :                             + ")."));
    1812            9 :                 return;
    1813              :             }
    1814              :         }
    1815       132089 :         std::shared_ptr<addrinfo> ai(addrlist, addrinfo_deleter);
    1816              : 
    1817       132089 :         bool first(true);
    1818       264332 :         while(addrlist != nullptr)
    1819              :         {
    1820              :             // go through the addresses and create ranges and save that in the result
    1821              :             //
    1822       132243 :             if(addrlist->ai_family == AF_INET)
    1823              :             {
    1824        66125 :                 if(addrlist->ai_addrlen != sizeof(sockaddr_in))
    1825              :                 {
    1826              :                     emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
    1827              :                              + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
    1828              :                              + ", expected"                                  // LCOV_EXCL_LINE
    1829              :                              + std::to_string(sizeof(sockaddr_in))           // LCOV_EXCL_LINE
    1830              :                              + ").");                                        // LCOV_EXCL_LINE
    1831              :                 }
    1832              :                 else
    1833              :                 {
    1834        66125 :                     addr a(*reinterpret_cast<sockaddr_in *>(addrlist->ai_addr));
    1835        66125 :                     a.set_hostname(address);
    1836              :                     // in most cases we do not get a protocol from
    1837              :                     // the getaddrinfo() function...
    1838        66125 :                     if(addrlist->ai_protocol != -1)
    1839              :                     {
    1840        66125 :                         a.set_protocol(addrlist->ai_protocol);
    1841              :                     }
    1842        66125 :                     a.set_port_defined(defined_port);
    1843        66125 :                     addr_range r;
    1844        66125 :                     r.set_from(a);
    1845        66125 :                     result.push_back(r);
    1846        66125 :                 }
    1847              :             }
    1848        66118 :             else if(addrlist->ai_family == AF_INET6)
    1849              :             {
    1850        66118 :                 if(addrlist->ai_addrlen != sizeof(sockaddr_in6))
    1851              :                 {
    1852              :                     emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
    1853              :                              + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
    1854              :                              + ", expected "                                 // LCOV_EXCL_LINE
    1855              :                              + std::to_string(sizeof(sockaddr_in6))          // LCOV_EXCL_LINE
    1856              :                              + ").");                                        // LCOV_EXCL_LINE
    1857              :                 }
    1858              :                 else
    1859              :                 {
    1860        66118 :                     addr a(*reinterpret_cast<sockaddr_in6 *>(addrlist->ai_addr));
    1861        66118 :                     a.set_hostname(address);
    1862        66118 :                     if(addrlist->ai_protocol != -1)
    1863              :                     {
    1864        66118 :                         a.set_protocol(addrlist->ai_protocol);
    1865              :                     }
    1866        66118 :                     a.set_port_defined(defined_port);
    1867        66118 :                     addr_range r;
    1868        66118 :                     r.set_from(a);
    1869        66118 :                     result.push_back(r);
    1870        66118 :                 }
    1871              :             }
    1872              :             else if(first)                                                  // LCOV_EXCL_LINE
    1873              :             {
    1874              :                 // ignore errors from other unsupported addresses
    1875              :                 //
    1876              :                 first = false;                                              // LCOV_EXCL_LINE
    1877              : 
    1878              :                 emit_error("Unsupported address family "                    // LCOV_EXCL_LINE
    1879              :                          + std::to_string(addrlist->ai_family)              // LCOV_EXCL_LINE
    1880              :                          + ".");                                            // LCOV_EXCL_LINE
    1881              :             }
    1882              : 
    1883       132243 :             addrlist = addrlist->ai_next;
    1884              :         }
    1885       132089 :     }
    1886              :     else
    1887              :     {
    1888          103 :         std::int64_t port(0);
    1889          103 :         if(get_allow(allow_t::ALLOW_REQUIRED_PORT)
    1890          103 :         || !port_str.empty())
    1891              :         {
    1892           38 :             bool const valid_port(advgetopt::validator_integer::convert_string(port_str, port));
    1893           38 :             if(!valid_port
    1894           29 :             || port < 0
    1895           29 :             || port > 65535)
    1896              :             {
    1897           18 :                 emit_error("invalid port in \""
    1898           18 :                          + port_str
    1899           36 :                          + "\" (no service name lookup allowed).");
    1900            9 :                 return;
    1901              :             }
    1902              : 
    1903           29 :             if(!get_allow(allow_t::ALLOW_PORT)
    1904           29 :             && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
    1905              :             {
    1906              :                 // TBD: this is probably a logic error because as far as I
    1907              :                 //      know it can only happen if the programmer defined
    1908              :                 //      a default port and also told the parser that no
    1909              :                 //      port is allowed
    1910              :                 //
    1911            2 :                 emit_error("Found a port (\""
    1912            2 :                          + port_str
    1913            4 :                          + "\") when it is not allowed.");
    1914            1 :                 return;
    1915              :             }
    1916              :         }
    1917           93 :         sockaddr_in in;
    1918           93 :         if(inet_pton(AF_INET, address.c_str(), &in.sin_addr) == 1)
    1919              :         {
    1920           52 :             in.sin_family = AF_INET;
    1921           52 :             in.sin_port = htons(port);
    1922           52 :             memset(in.sin_zero, 0, sizeof(in.sin_zero)); // probably useless
    1923              : 
    1924           52 :             addr a(in);
    1925           52 :             a.set_hostname(address);
    1926           52 :             if(f_protocol != -1)
    1927              :             {
    1928            1 :                 a.set_protocol(f_protocol);
    1929              :             }
    1930           52 :             a.set_port_defined(defined_port);
    1931           52 :             addr_range r;
    1932           52 :             r.set_from(a);
    1933           52 :             result.push_back(r);
    1934           52 :         }
    1935              :         else
    1936              :         {
    1937           41 :             sockaddr_in6 in6;
    1938           41 :             if(inet_pton(AF_INET6, address.c_str(), &in6.sin6_addr) == 1)
    1939              :             {
    1940           35 :                 in6.sin6_family = AF_INET6;
    1941           35 :                 in6.sin6_port = htons(port);
    1942           35 :                 in6.sin6_flowinfo = 0;
    1943           35 :                 in6.sin6_scope_id = 0;
    1944              : 
    1945           35 :                 addr a(in6);
    1946           35 :                 a.set_hostname(address);
    1947           35 :                 if(f_protocol != -1)
    1948              :                 {
    1949            1 :                     a.set_protocol(f_protocol);
    1950              :                 }
    1951           35 :                 a.set_port_defined(defined_port);
    1952           35 :                 addr_range r;
    1953           35 :                 r.set_from(a);
    1954           35 :                 result.push_back(r);
    1955           35 :             }
    1956              :             else
    1957              :             {
    1958           12 :                 emit_error("Unknown address in \""
    1959           12 :                          + address
    1960           24 :                          + "\" (no DNS lookup was allowed).");
    1961              :             }
    1962              :         }
    1963              :     }
    1964              : }
    1965              : 
    1966              : 
    1967              : /** \brief Parse the address and port and ignore duplicates.
    1968              :  *
    1969              :  * The default system search finds one address per protocol. So if the
    1970              :  * address matches TCP, UDP, and other protocols, then we get one
    1971              :  * address for each protocol.
    1972              :  *
    1973              :  * This function removes those duplicates which are an issue in a list
    1974              :  * of IPs when working with ranges (i.e. 192.168.1.1-192.168.1.254 would
    1975              :  * fail because we find at least a TCP and a UDP protocol for those
    1976              :  * two addresses).
    1977              :  *
    1978              :  * \param[in] address  The address to convert to binary.
    1979              :  * \param[in] port_str  The port as a string.
    1980              :  * \param[out] result  The range where we save the results.
    1981              :  * \param[in] ipv6  Use the default IPv6 address if the address is empty.
    1982              :  */
    1983           98 : void addr_parser::parse_address_port_ignore_duplicates(
    1984              :       std::string address
    1985              :     , std::string port_str
    1986              :     , addr_range::vector_t & result
    1987              :     , bool ipv6)
    1988              : {
    1989           98 :     parse_address_port(address, port_str, result, ipv6);
    1990           98 :     if(result.size() > 1)
    1991              :     {
    1992              :         // the following works only on a single set of addresses
    1993              :         // (i.e. if you called parse_address_port() multiple times,
    1994              :         // then it will not properly reduce all the equivalent IPs)
    1995              :         //
    1996              :         // note that since this is used for ranges, if we return multiple
    1997              :         // addresses (as in 192.168.1.1 and 192.168.3.1) then the range
    1998              :         // definition fails anyway, so the fact that we do not properly
    1999              :         // reduce the second set of addresses is not relevant here
    2000              :         //
    2001            4 :         addr first(result[0].get_from());
    2002           12 :         while(result.size() > 1)
    2003              :         {
    2004            8 :             addr next(result[1].get_from());
    2005            8 :             next.set_protocol(first.get_protocol());
    2006            8 :             if(first != next)
    2007              :             {
    2008              :                        // I'm not sure of how to get two IPs in a locally running unit test...
    2009              :                 break; // LCOV_EXCL_LINE
    2010              :             }
    2011            8 :             result.erase(result.begin() + 1);
    2012            8 :         }
    2013            4 :     }
    2014           98 : }
    2015              : 
    2016              : 
    2017              : /** \brief Parse a mask.
    2018              :  *
    2019              :  * If the input string is a decimal number, then use that as the
    2020              :  * number of bits to clear.
    2021              :  *
    2022              :  * If the mask is not just one decimal number, try to convert it
    2023              :  * as an address.
    2024              :  *
    2025              :  * If the string is neither a decimal number nor a valid IP address
    2026              :  * then the parser adds an error string to the f_error variable.
    2027              :  *
    2028              :  * The \p is_ipv4 flag is used to know whether the size of the CIDR is
    2029              :  * 0 to 128 or 0 to 32 and fix the mask accordingly.
    2030              :  *
    2031              :  * \bug
    2032              :  * Note that this flag* is bogus when the input is a domain name and lookup
    2033              :  * are allowed. This is because in this case the address may be IPv4 or
    2034              :  * IPv6, which means the mask would be bogus anyway. So I think we are fine.
    2035              :  * The bug comes from the user in this case.
    2036              :  *
    2037              :  * \param[in] mask  The mask to transform to binary.
    2038              :  * \param[in,out] cidr  The address to which the mask will be added.
    2039              :  * \param[in] is_ipv4  Whether the address was parsed as an IPv4 (true) or
    2040              :  * an IPv6 (false).
    2041              :  */
    2042          590 : void addr_parser::parse_mask(
    2043              :       std::string const & mask
    2044              :     , addr & cidr
    2045              :     , bool const is_ipv4)
    2046              : {
    2047              :     // no mask?
    2048              :     //
    2049          590 :     if(mask.empty())
    2050              :     {
    2051              :         // however, the algorithm below expects that 'mask' is not
    2052              :         // empty (otherwise we get the case of 0 even though it
    2053              :         // may not be correct.)
    2054              :         //
    2055           41 :         return;
    2056              :     }
    2057              : 
    2058              :     // the mask may be a decimal number or an address, if just one number
    2059              :     // then it's not an address, so test that first
    2060              :     //
    2061          549 :     std::uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
    2062              : 
    2063              :     // convert the mask to an integer, if possible
    2064              :     //
    2065          549 :     int mask_count(0);
    2066              :     {
    2067          549 :         std::string m(mask);
    2068         1620 :         for(char const * s(m.c_str()); *s != '\0'; ++s)
    2069              :         {
    2070         1325 :             if(*s >= '0' && *s <= '9')
    2071              :             {
    2072         1086 :                 mask_count = mask_count * 10 + *s - '0';
    2073         1086 :                 if(mask_count > 10000)
    2074              :                 {
    2075           30 :                     emit_error("Mask size too large ("
    2076           30 :                              + mask
    2077           60 :                              + ", expected a maximum of 128).");
    2078           15 :                     return;
    2079              :                 }
    2080              :             }
    2081              :             else
    2082              :             {
    2083          239 :                 mask_count = -1;
    2084          239 :                 break;
    2085              :             }
    2086              :         }
    2087          549 :     }
    2088              : 
    2089              :     // the conversion to an integer worked if mask_count != -1
    2090              :     //
    2091          534 :     if(mask_count != -1)
    2092              :     {
    2093          295 :         if(is_ipv4)
    2094              :         {
    2095           52 :             if(mask_count > 32)
    2096              :             {
    2097           10 :                 emit_error("Unsupported mask size ("
    2098           10 :                          + std::to_string(mask_count)
    2099           20 :                          + ", expected 32 at the most for an IPv4).");
    2100            5 :                 return;
    2101              :             }
    2102           47 :             mask_count = 32 - mask_count;
    2103              :         }
    2104              :         else
    2105              :         {
    2106          243 :             if(mask_count > 128)
    2107              :             {
    2108           10 :                 emit_error("Unsupported mask size ("
    2109           10 :                          + std::to_string(mask_count)
    2110           20 :                          + ", expected 128 at the most for an IPv6).");
    2111            5 :                 return;
    2112              :             }
    2113          238 :             mask_count = 128 - mask_count;
    2114              :         }
    2115              : 
    2116              :         // clear a few bits at the bottom of mask_bits
    2117              :         //
    2118          285 :         int idx(15);
    2119         1934 :         for(; mask_count > 8; mask_count -= 8, --idx)
    2120              :         {
    2121         1649 :             mask_bits[idx] = 0;
    2122              :         }
    2123          285 :         mask_bits[idx] = 255 << mask_count;
    2124              :     }
    2125              :     else //if(mask_count < 0)
    2126              :     {
    2127          239 :         if(!get_allow(allow_t::ALLOW_ADDRESS_MASK))
    2128              :         {
    2129          102 :             emit_error("Address like mask not allowed (/"
    2130          102 :                      + mask
    2131          204 :                      + "), try with a simple number instead.");
    2132           51 :             return;
    2133              :         }
    2134              : 
    2135              :         // prepare hints for the the getaddrinfo() function
    2136              :         //
    2137          188 :         addrinfo hints = {};
    2138          188 :         hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
    2139          188 :         hints.ai_family = AF_UNSPEC;
    2140              : 
    2141          188 :         switch(cidr.get_protocol())
    2142              :         {
    2143           92 :         case IPPROTO_TCP:
    2144           92 :             hints.ai_socktype = SOCK_STREAM;
    2145           92 :             hints.ai_protocol = IPPROTO_TCP;
    2146           92 :             break;
    2147              : 
    2148           92 :         case IPPROTO_UDP:
    2149           92 :             hints.ai_socktype = SOCK_DGRAM;
    2150           92 :             hints.ai_protocol = IPPROTO_UDP;
    2151           92 :             break;
    2152              : 
    2153              :         }
    2154              : 
    2155          188 :         std::string const port_str(std::to_string(cidr.get_port()));
    2156              : 
    2157              :         // if the mask is an IPv6, then it has to have the '[...]'
    2158          188 :         std::string m(mask);
    2159          188 :         if(is_ipv4)
    2160              :         {
    2161           99 :             if(mask[0] == '[')
    2162              :             {
    2163            3 :                 emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
    2164            1 :                 return;
    2165              :             }
    2166              :         }
    2167              :         else //if(!is_ipv4)
    2168              :         {
    2169           89 :             if(mask[0] != '[')
    2170              :             {
    2171            3 :                 emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
    2172            1 :                 return;
    2173              :             }
    2174           88 :             if(mask.back() != ']')
    2175              :             {
    2176            1 :                 emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
    2177            1 :                 return;
    2178              :             }
    2179              : 
    2180              :             // note that we know that mask.length() >= 2 here since
    2181              :             // we at least have a '[' and ']'
    2182              :             //
    2183           87 :             m = mask.substr(1, mask.length() - 2);
    2184           87 :             if(m.empty())
    2185              :             {
    2186              :                 // an empty mask is valid, it just means keep the default
    2187              :                 // (getaddrinfo() fails on an empty string)
    2188              :                 //
    2189            1 :                 return;
    2190              :             }
    2191              :         }
    2192              : 
    2193              :         // if negative, we may have a full address here, so call the
    2194              :         // getaddrinfo() on this other string
    2195              :         //
    2196          184 :         addrinfo * masklist(nullptr);
    2197          184 :         errno = 0;
    2198          184 :         int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
    2199          184 :         if(r != 0)
    2200              :         {
    2201              :             // break on invalid addresses
    2202              :             //
    2203           15 :             int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
    2204           30 :             emit_error("Invalid mask in \"/"
    2205           30 :                      + mask
    2206           60 :                      + "\", error "
    2207           60 :                      + std::to_string(r)
    2208           60 :                      + " -- "
    2209           60 :                      + gai_strerror(r)
    2210           60 :                      + " (errno: "
    2211           60 :                      + std::to_string(e)
    2212           60 :                      + " -- "
    2213           60 :                      + strerror(e)
    2214           60 :                      + ").");
    2215           15 :             return;
    2216              :         }
    2217          169 :         std::shared_ptr<addrinfo> mask_ai(masklist, addrinfo_deleter);
    2218              : 
    2219          169 :         if(is_ipv4)
    2220              :         {
    2221           88 :             if(masklist->ai_family != AF_INET)
    2222              :             {
    2223              :                 // this one happens when the user does not put the '[...]'
    2224              :                 // around an IPv6 address
    2225              :                 //
    2226            3 :                 emit_error("Incompatible address between the address and"
    2227              :                           " mask address (first was an IPv4 second an IPv6).");
    2228            1 :                 return;
    2229              :             }
    2230           87 :             if(masklist->ai_addrlen != sizeof(sockaddr_in))
    2231              :             {
    2232              :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
    2233              :                         + std::to_string(masklist->ai_addrlen)          // LCOV_EXCL_LINE
    2234              :                         + ", expected"                                  // LCOV_EXCL_LINE
    2235              :                         + std::to_string(sizeof(sockaddr_in))           // LCOV_EXCL_LINE
    2236              :                         + ").");                                        // LCOV_EXCL_LINE
    2237              :                 return;                                                 // LCOV_EXCL_LINE
    2238              :             }
    2239           87 :             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
    2240              :         }
    2241              :         else //if(!is_ipv4)
    2242              :         {
    2243           81 :             if(masklist->ai_family != AF_INET6)
    2244              :             {
    2245              :                 // this one happens if the user puts the '[...]'
    2246              :                 // around an IPv4 address
    2247              :                 //
    2248            3 :                 emit_error("Incompatible address between the address"
    2249              :                           " and mask address (first was an IPv6 second an IPv4).");
    2250            1 :                 return;
    2251              :             }
    2252           80 :             if(masklist->ai_addrlen != sizeof(sockaddr_in6))
    2253              :             {
    2254              :                 emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
    2255              :                          + std::to_string(masklist->ai_addrlen)         // LCOV_EXCL_LINE
    2256              :                          + ", expected "                                // LCOV_EXCL_LINE
    2257              :                          + std::to_string(sizeof(sockaddr_in6))         // LCOV_EXCL_LINE
    2258              :                          + ").");                                       // LCOV_EXCL_LINE
    2259              :                 return;                                                 // LCOV_EXCL_LINE
    2260              :             }
    2261           80 :             memcpy(mask_bits, &reinterpret_cast<sockaddr_in6 *>(masklist->ai_addr)->sin6_addr.s6_addr, 16);
    2262              :         }
    2263              :     }
    2264              : 
    2265          452 :     cidr.set_mask(mask_bits);
    2266              : }
    2267              : 
    2268              : 
    2269              : /** \brief Transform a string into an `addr` object.
    2270              :  *
    2271              :  * This function converts the string \p a in an IP address saved in
    2272              :  * the returned addr object or throws an error if the conversion
    2273              :  * fails.
    2274              :  *
    2275              :  * The \p default_address parameter string can be set to an address
    2276              :  * which is returned if the input in \p a does not include an
    2277              :  * address such as in ":123".
    2278              :  *
    2279              :  * The \p port parameter can be specified or set to -1. If -1, then
    2280              :  * there is no default port. Either way, the port can be defined in
    2281              :  * \p a.
    2282              :  *
    2283              :  * The protocol can be specified, as a string. For example, you can
    2284              :  * use "tcp". The default is no specific protocol which means any
    2285              :  * type of IP address can be returned. Note that if more than one
    2286              :  * result is returned when the protocol was not specified, the
    2287              :  * results will be filtered to only keep the address that uses the
    2288              :  * TCP protocol. If as a result we have a single address, then that
    2289              :  * result gets returned.
    2290              :  *
    2291              :  * \note
    2292              :  * This function does not allow for address or port ranges. It is
    2293              :  * expected to return exactly one address. You can allow a \p mask
    2294              :  * by setting that parameter to true.
    2295              :  *
    2296              :  * \exception addr_invalid_argument
    2297              :  * If the parsed address is not returning a valid `addr` object, then
    2298              :  * this function fails by throwing an error. If you would prefer to
    2299              :  * handle the error mechanism, you want to create your own addr_parser
    2300              :  * and then call the addr_parser::parse() function. This will allow
    2301              :  * you to get error messages instead of an exception.
    2302              :  *
    2303              :  * \param[in] a  The address string to be converted.
    2304              :  * \param[in] default_addrress  The default address or an empty string.
    2305              :  * \param[in] default_port  The default port or -1
    2306              :  * \param[in] protocol  The protocol the address has to be of, or the
    2307              :  *                      empty string to allow any protocol.
    2308              :  * \param[in] mask  Whether to allow a mask (true) or not (false).
    2309              :  *
    2310              :  * \return The address converted in an `addr` object.
    2311              :  *
    2312              :  * \sa addr_parser::parse()
    2313              :  */
    2314           15 : addr string_to_addr(
    2315              :           std::string const & a
    2316              :         , std::string const & default_address
    2317              :         , int default_port
    2318              :         , std::string const & protocol
    2319              :         , bool mask)
    2320              : {
    2321           15 :     addr_parser p;
    2322              : 
    2323           15 :     if(!default_address.empty())
    2324              :     {
    2325           10 :         p.set_default_address(default_address);
    2326              :     }
    2327              : 
    2328           15 :     if(default_port != -1)
    2329              :     {
    2330            8 :         p.set_default_port(default_port);
    2331              :     }
    2332              : 
    2333           15 :     if(!protocol.empty())
    2334              :     {
    2335            7 :         p.set_protocol(protocol);
    2336              :     }
    2337              : 
    2338           15 :     p.set_allow(allow_t::ALLOW_MASK, mask);
    2339              : 
    2340           15 :     addr_range::vector_t result(p.parse(a));
    2341              : 
    2342           15 :     if(result.size() != 1)
    2343              :     {
    2344              :         // when the protocol is not specified, this happens like all the
    2345              :         // time, we search for an entry with protocol TCP by default
    2346              :         // because in most cases that's what people want
    2347              :         //
    2348            9 :         if(protocol.empty())
    2349              :         {
    2350           40 :             result.erase(
    2351           16 :                       std::remove_if(
    2352              :                           result.begin()
    2353              :                         , result.end()
    2354           21 :                         , [](auto const it)
    2355              :                         {
    2356           21 :                             return it.has_from() && it.get_from().get_protocol() != IPPROTO_TCP;
    2357              :                         })
    2358           16 :                     , result.end());
    2359              :         }
    2360            9 :         if(result.size() != 1)
    2361              :         {
    2362              :             // an invalid protocol is caught by the set_protocol()
    2363              :             // function, but a totally invalid address or domain name
    2364              :             // will get us here with an empty list (result.empty() == true)
    2365              :             //
    2366              :             throw addr_invalid_argument(
    2367              :                       "the address \""
    2368            4 :                     + a
    2369            8 :                     + "\" could not be converted to a single address in string_to_addr(), found "
    2370            8 :                     + std::to_string(result.size())
    2371            6 :                     + " entries instead.");
    2372              :         }
    2373              :     }
    2374              : 
    2375              :     // at the moment, we can only get a "from" so the following exceptions
    2376              :     // just cannot happen, which is why we have an LCOV_EXCL_LINE
    2377              :     //
    2378           13 :     if(result[0].has_to()
    2379           13 :     || result[0].is_range())
    2380              :     {
    2381              :         throw addr_invalid_argument("string_to_addr() does not support ranges.");     // LCOV_EXCL_LINE
    2382              :     }
    2383              : 
    2384           13 :     if(!result[0].has_from())
    2385              :     {
    2386              :         throw addr_invalid_argument("string_to_addr() has no 'from' address.");       // LCOV_EXCL_LINE
    2387              :     }
    2388              : 
    2389           26 :     return result[0].get_from();
    2390           17 : }
    2391              : 
    2392              : 
    2393              : 
    2394              : }
    2395              : // namespace addr
    2396              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions