LCOV - code coverage report
Current view: top level - src - addr.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 260 260 100.0 %
Date: 2017-01-22 02:53:10 Functions: 37 37 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Network Address -- classes functions to ease handling IP addresses
       2             : // Copyright (C) 2012-2017  Made to Order Software Corp.
       3             : //
       4             : // http://snapwebsites.org/project/libaddr
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : // self
      21             : //
      22             : #include "libaddr/addr.h"
      23             : 
      24             : // C++ lib
      25             : //
      26             : #include <algorithm>
      27             : #include <sstream>
      28             : #include <iostream>
      29             : 
      30             : // C lib
      31             : //
      32             : #include <ifaddrs.h>
      33             : #include <netdb.h>
      34             : 
      35             : 
      36             : 
      37             : /** \mainpage
      38             :  * \brief libaddr, a C++ library to handle network IP addresses in IPv4 and IPv6.
      39             :  *
      40             :  * ### Introduction
      41             :  *
      42             :  * This library is used to parse strings of IP addresses to lists of
      43             :  * binary IP addresses ready to be used by functions such as bind(),
      44             :  * send(), recv(), etc.
      45             :  *
      46             :  * The library supports multiple addresses separated by commas and/or
      47             :  * spaces, ports, and CIDR masks. It can check whether an address matches
      48             :  * another taking the mask in account. It can sort IPs numerically. It
      49             :  * can determine the type of an IP address (i.e. is it a local address,
      50             :  * a private address, a public address?)
      51             :  *
      52             :  * The library also has a function to read IP addresses from your
      53             :  * computer interfaces and return that list. Very practical to know
      54             :  * whether an IP address represents your computer or not.
      55             :  *
      56             :  * ### Usage
      57             :  *
      58             :  * The library is composed of three main classes:
      59             :  *
      60             :  * \li addr
      61             :  *
      62             :  * The address class holds one address, a port, a protocol and a few
      63             :  * other parts. This is what one uses to connect or listen with an
      64             :  * address.
      65             :  *
      66             :  * The address is kept by addr in an IPv6 address structure.
      67             :  *
      68             :  * By default the CIDR of the address is all 1s (i.e. no masking, all
      69             :  * bits considered important.) The mask is always 128 bits. If you are
      70             :  * dealing with IPv4, make sure that the first 12 bytes are set to 255.
      71             :  *
      72             :  * The class also offers a set of functions to transform the binary
      73             :  * address it is holding to a string.
      74             :  *
      75             :  * \li addr_range
      76             :  *
      77             :  * It is possible to define a range of addresses and ports. This class
      78             :  * holds a 'from' address and a 'to' address. By default neither is
      79             :  * defined. You have to call the set_from() and set_to() functions.
      80             :  *
      81             :  * The addr_range::vector_t is what the addr_parser returns after
      82             :  * parsing a string representing one of more addresses.
      83             :  *
      84             :  * \note
      85             :  * The range is functional, however, the parser does not yet support
      86             :  * parsing range of addresses and ports.
      87             :  *
      88             :  * \li addr_parser
      89             :  *
      90             :  * The parser is used to transform a string to an address.
      91             :  *
      92             :  * It supports many variations of its input, which are handled by
      93             :  * the 'allow' flags. The set_allow() and get_allow() functions can
      94             :  * be used to tweak the parser in supporting such and such feature.
      95             :  *
      96             :  * By default, the input is expected to be an address and a port
      97             :  * separated by a colon (i.e. `"1.2.3.4:1234"` in IPv4 and `"[::1]:1234"`
      98             :  * in IPv6.)
      99             :  *
     100             :  * ### Parser
     101             :  *
     102             :  * The parser supports the following syntax (ranges are not yet supported
     103             :  * and they do not appear in the following list):
     104             :  *
     105             :  * \code
     106             :  *    start: address_list
     107             :  *
     108             :  *    address_list: address_cidr
     109             :  *                | address_list address_list_separators address_cidr
     110             :  *
     111             :  *    address_list_separators: ' '
     112             :  *                           | ','
     113             :  *                           | address_list_separators address_list_separators
     114             :  *
     115             :  *    address_cidr: address_port
     116             :  *                | address_port '/' cidr
     117             :  *
     118             :  *    address_port: address
     119             :  *                | address ':' port
     120             :  *
     121             :  *    address: ipv4
     122             :  *           | ipv6
     123             :  *
     124             :  *    cidr: decimal_number
     125             :  *        | ipv4
     126             :  *        | ipv6
     127             :  *
     128             :  *    ipv4: decimal_number '.' decimal_number '.' decimal_number '.' decimal_number
     129             :  *
     130             :  *    ipv6: '[' hexadecimal_number_list ']'
     131             :  *
     132             :  *    port: decimal_number
     133             :  *
     134             :  *    hexadecimal_number_list: hexadecimal_number
     135             :  *                           | hexadecimal_number_list ':' hexadecimal_number
     136             :  *
     137             :  *    decimal_number: [0-9]+
     138             :  *
     139             :  *    hexadecimal_number: [0-9a-fA-F]+
     140             :  * \endcode
     141             :  *
     142             :  * When accepting multiple addresses separated by commas or spaces, the
     143             :  * number of commas and spaces separating two address is not significant.
     144             :  * The input string can also start or end with commas and spaces. The
     145             :  * following variable defines exactly two IP address:
     146             :  *
     147             :  * \code
     148             :  *       addresses=  ,1.2.3.4,   ,5.6.7.8,,
     149             :  * \endcode
     150             :  *
     151             :  * (note that the parser should not be passed the "addresses=" part.)
     152             :  */
     153             : 
     154             : 
     155             : namespace addr
     156             : {
     157             : 
     158             : /*
     159             :  * Various sytem address structures
     160             : 
     161             : struct sockaddr {
     162             :    unsigned short    sa_family;    // address family, AF_xxx
     163             :    char              sa_data[14];  // 14 bytes of protocol address
     164             : };
     165             : 
     166             : 
     167             : struct sockaddr_in {
     168             :     short            sin_family;   // e.g. AF_INET, AF_INET6
     169             :     unsigned short   sin_port;     // e.g. htons(3490)
     170             :     struct in_addr   sin_addr;     // see struct in_addr, below
     171             :     char             sin_zero[8];  // zero this if you want to
     172             : };
     173             : 
     174             : 
     175             : struct sockaddr_in6 {
     176             :     u_int16_t       sin6_family;   // address family, AF_INET6
     177             :     u_int16_t       sin6_port;     // port number, Network Byte Order
     178             :     u_int32_t       sin6_flowinfo; // IPv6 flow information
     179             :     struct in6_addr sin6_addr;     // IPv6 address
     180             :     u_int32_t       sin6_scope_id; // Scope ID
     181             : };
     182             : 
     183             : struct in6_addr
     184             :   {
     185             :     union
     186             :       {
     187             :         uint8_t __u6_addr8[16];
     188             : #ifdef __USE_MISC
     189             :         uint16_t __u6_addr16[8];
     190             :         uint32_t __u6_addr32[4];
     191             : #endif
     192             :       } __in6_u;
     193             : #define s6_addr                 __in6_u.__u6_addr8
     194             : #ifdef __USE_MISC
     195             : # define s6_addr16              __in6_u.__u6_addr16
     196             : # define s6_addr32              __in6_u.__u6_addr32
     197             : #endif
     198             :   };
     199             : 
     200             : struct in_addr {
     201             :         __be32  s_addr;
     202             : };
     203             : 
     204             : struct sockaddr_storage {
     205             :     sa_family_t  ss_family;     // address family
     206             : 
     207             :     // all this is padding, implementation specific, ignore it:
     208             :     char      __ss_pad1[_SS_PAD1SIZE];
     209             :     int64_t   __ss_align;
     210             :     char      __ss_pad2[_SS_PAD2SIZE];
     211             : };
     212             : 
     213             : */
     214             : 
     215             : 
     216             : namespace
     217             : {
     218             : 
     219             : /** \brief Delete an ifaddrs structure.
     220             :  *
     221             :  * This deleter is used to make sure all the ifaddrs get released when
     222             :  * an exception occurs or the function using such exists.
     223             :  *
     224             :  * \param[in] ia  The ifaddrs structure to free.
     225             :  */
     226          14 : void ifaddrs_deleter(struct ifaddrs * ia)
     227             : {
     228          14 :     freeifaddrs(ia);
     229          14 : }
     230             : 
     231             : 
     232             : }
     233             : // no name namespace
     234             : 
     235             : 
     236             : /** \brief Create an addr object that represents an ANY address.
     237             :  *
     238             :  * This function initializes the addr object with the ANY address.
     239             :  * The port is set to 0 and the protocol to TCP.
     240             :  *
     241             :  * It is strongly suggested that you change those parameters
     242             :  * before really using this address since a port of zero and
     243             :  * the protocol may be wrong.
     244             :  */
     245      264127 : addr::addr()
     246             : {
     247             :     // keep default protocol (TCP)
     248      264127 : }
     249             : 
     250             : 
     251             : /** \brief Create an addr object from a binary IPv4 address.
     252             :  *
     253             :  * This function initializes this addr object with the specified IPv4
     254             :  * address. The is_ipv4() function will return true.
     255             :  *
     256             :  * \param[in] in  The binary IPv4 address.
     257             :  */
     258       65904 : addr::addr(struct sockaddr_in const & in)
     259             : {
     260       65879 :     set_ipv4(in);
     261             :     // keep default protocol (TCP)
     262       65854 : }
     263             : 
     264             : 
     265             : /** \brief Create an addr object from a binary IPv6 address.
     266             :  *
     267             :  * This function initializes this addr object with the specified IPv6
     268             :  * address. The is_ipv4() function will return false.
     269             :  *
     270             :  * \param[in] in6  The binary IPv6 address.
     271             :  */
     272       65984 : addr::addr(struct sockaddr_in6 const & in6)
     273             : {
     274       65983 :     set_ipv6(in6);
     275             :     // keep default protocol (TCP)
     276       65982 : }
     277             : 
     278             : 
     279             : /** \brief Save an IPv4 in this addr object.
     280             :  *
     281             :  * This function saves the specified IPv4 in this addr object.
     282             :  *
     283             :  * Since we save the data in an IPv6 structure, it is kept in
     284             :  * the addr as an IPv4 mapped in an IPv6 address. It can still
     285             :  * be retrieved right back as an IPv4 with the get_ipv4() function.
     286             :  *
     287             :  * \param[in] in  The IPv4 address to save in this addr object.
     288             :  */
     289       66412 : void addr::set_ipv4(struct sockaddr_in const & in)
     290             : {
     291       66412 :     if(in.sin_family != AF_INET)
     292             :     {
     293             :         // although we convert the IPv4 to an IPv6 below, we really only
     294             :         // support AF_INET on entry
     295             :         //
     296          50 :         throw addr_invalid_argument_exception("addr::set_ipv4(): the input address does not represent an IPv4 address (family is not AF_INET).");
     297             :     }
     298             : 
     299             :     // reset the address first
     300       66362 :     memset(&f_address, 0, sizeof(f_address));
     301             : 
     302             :     // then transform the IPv4 to an IPv6
     303             :     //
     304             :     // Note: this is not an IPv6 per se, it is an IPv4 mapped within an
     305             :     //       IPv6 and your network anwway stack needs to support IPv4
     306             :     //       in order to use that IP...
     307             :     //
     308       66362 :     f_address.sin6_family = AF_INET6;
     309       66362 :     f_address.sin6_port = in.sin_port;
     310       66362 :     f_address.sin6_addr.s6_addr16[5] = 0xFFFF;
     311       66362 :     f_address.sin6_addr.s6_addr32[3] = in.sin_addr.s_addr;
     312             : 
     313       66362 :     address_changed();
     314       66362 : }
     315             : 
     316             : 
     317             : /** \brief Set the port of this address.
     318             :  *
     319             :  * This function changes the port of this address to \p port.
     320             :  *
     321             :  * \exception addr_invalid_argument_exception
     322             :  * This exception is raised whenever the \p port parameter is set to
     323             :  * an invalid number (negative or larger than 65535.)
     324             :  *
     325             :  * \param[in] port  The new port to save in this address.
     326             :  */
     327       65741 : void addr::set_port(int port)
     328             : {
     329       65741 :     if(port > 65535 
     330       65641 :     || port < 0)
     331             :     {
     332         200 :         throw addr_invalid_argument_exception("port to set_port() cannot be out of the allowed range [0..65535].");
     333             :     }
     334       65541 :     f_address.sin6_port = htons(port);
     335       65541 : }
     336             : 
     337             : 
     338             : /** \brief Change the protocol using a string.
     339             :  *
     340             :  * This function is used to change the current protocol defined in
     341             :  * this addr object.
     342             :  *
     343             :  * \exception addr_invalid_argument_exception
     344             :  * We currently support "tcp", "udp", and "ip". Any other protocol
     345             :  * name generates this exception.
     346             :  *
     347             :  * \param[in] protocol  The name of the protocol ("tcp", "udp", or "ip")
     348             :  */
     349           7 : void addr::set_protocol(char const * protocol)
     350             : {
     351           7 :     if(protocol == nullptr)
     352             :     {
     353           1 :         throw addr_invalid_argument_exception("protocol pointer to set_protocol() cannot be a nullptr.");
     354             :     }
     355             : 
     356           6 :     if(strcmp(protocol, "ip") == 0)
     357             :     {
     358           1 :         f_protocol = IPPROTO_IP;
     359             :     }
     360           5 :     else if(strcmp(protocol, "tcp") == 0)
     361             :     {
     362           1 :         f_protocol = IPPROTO_TCP;
     363             :     }
     364           4 :     else if(strcmp(protocol, "udp") == 0)
     365             :     {
     366           1 :         f_protocol = IPPROTO_UDP;
     367             :     }
     368             :     else
     369             :     {
     370             :         throw addr_invalid_argument_exception(
     371             :                           std::string("unknown protocol \"")
     372           6 :                         + protocol
     373           9 :                         + "\", expected \"tcp\" or \"udp\" (string).");
     374             :     }
     375             : 
     376           3 :     address_changed();
     377           3 : }
     378             : 
     379             : 
     380             : /** \brief Set the protocol numerically.
     381             :  *
     382             :  * This function sets the protocol from a number instead of a name.
     383             :  *
     384             :  * Note that we only support IPPROTO_TCP and IPPROTO_UDP for now.
     385             :  * Any other protocol will make this function raise an exception.
     386             :  *
     387             :  * \todo
     388             :  * We may want to support any protocol number at this level. If your
     389             :  * application is limited then it should verify the protocol and
     390             :  * make sure it supports it before using this address. At the same
     391             :  * time, the IP protocol is pretty much locked up with just TCP
     392             :  * and UDP these days (there is the IP protocol, but that's not
     393             :  * useful at our level.)
     394             :  *
     395             :  * \exception addr_invalid_argument_exception
     396             :  * This exception is raised if the specified protocol is not currently
     397             :  * supported by the addr implementation.
     398             :  *
     399             :  * \param[in] protocol  The new numeric protocol.
     400             :  */
     401      131919 : void addr::set_protocol(int protocol)
     402             : {
     403      131919 :     switch(protocol)
     404             :     {
     405             :     case IPPROTO_IP:
     406             :     case IPPROTO_TCP:
     407             :     case IPPROTO_UDP:
     408      131819 :         f_protocol = protocol;
     409      131819 :         break;
     410             : 
     411             :     default:
     412             :         throw addr_invalid_argument_exception(
     413             :                           "unknown protocol number "
     414         200 :                         + std::to_string(protocol)
     415         200 :                         + ", expected \"tcp\" ("
     416         400 :                         + std::to_string(static_cast<int>(IPPROTO_TCP))
     417         200 :                         + ") or \"udp\" ("
     418         400 :                         + std::to_string(static_cast<int>(IPPROTO_UDP))
     419         300 :                         + ") (numeric).");
     420             : 
     421             :     }
     422      131819 : }
     423             : 
     424             : 
     425             : /** \brief Set the mask.
     426             :  *
     427             :  * The input mask must be exactly 16 bytes. If you are dealing with an
     428             :  * IPv4, make sure the first 12 bytes are 255.
     429             :  *
     430             :  * \param[in] mask  The mask to save in this address.
     431             :  */
     432         425 : void addr::set_mask(uint8_t const * mask)
     433             : {
     434         425 :     memcpy(f_mask, mask, sizeof(f_mask));
     435         425 : }
     436             : 
     437             : 
     438             : /** \brief Apply the mask to the IP address.
     439             :  *
     440             :  * This function applies the mask to this address IP address. This means
     441             :  * the bits that are 0 in the mask will also be 0 in the address once
     442             :  * the function returns.
     443             :  *
     444             :  * This should be called if you are trying to canonicalize an IP/mask.
     445             :  */
     446           1 : void addr::apply_mask()
     447             : {
     448          17 :     for(int idx(0); idx < 16; ++idx)
     449             :     {
     450          16 :         f_address.sin6_addr.s6_addr[idx] &= f_mask[idx];
     451             :     }
     452           1 : }
     453             : 
     454             : 
     455             : /** \brief Get the mask.
     456             :  *
     457             :  * The output buffer for the mask must be at least 16 bytes. If you are
     458             :  * dealing with an IPv4, all the bytes are expected to be 255 except
     459             :  * the bottom 4 bytes (offset 12, 13, 14, 15).
     460             :  *
     461             :  * \param[out] mask  The buffer where the mask gets copied.
     462             :  */
     463         142 : void addr::get_mask(uint8_t * mask)
     464             : {
     465         142 :     memcpy(mask, f_mask, sizeof(f_mask));
     466         142 : }
     467             : 
     468             : 
     469             : /** \brief Check whether this address represents an IPv4 address.
     470             :  *
     471             :  * The IPv6 format supports embedding IPv4 addresses. This function
     472             :  * returns true if the embedded address is an IPv4. When this function
     473             :  * returns true, the get_ipv4() can be called. Otherwise, the get_ipv4()
     474             :  * function throws an error.
     475             :  *
     476             :  * \return true if this address represents an IPv4 address.
     477             :  */
     478      527545 : bool addr::is_ipv4() const
     479             : {
     480      527545 :     return f_address.sin6_addr.s6_addr32[0] == 0
     481      329824 :         && f_address.sin6_addr.s6_addr32[1] == 0
     482      329820 :         && f_address.sin6_addr.s6_addr16[4] == 0
     483      857363 :         && f_address.sin6_addr.s6_addr16[5] == 0xFFFF;
     484             : }
     485             : 
     486             : 
     487             : /** \brief Retreive the IPv4 address.
     488             :  *
     489             :  * This function can be used to retrieve the IPv4 address of this addr
     490             :  * object. If the address is not an IPv4, then the function throws.
     491             :  *
     492             :  * \exception addr_invalid_structure_exception
     493             :  * This exception is raised if the address is not an IPv4 address.
     494             :  *
     495             :  * \param[out] in  The structure where the IPv4 Internet address gets saved.
     496             :  */
     497          21 : void addr::get_ipv4(struct sockaddr_in & in) const
     498             : {
     499          21 :     if(is_ipv4())
     500             :     {
     501             :         // this is an IPv4 mapped in an IPv6, "unmap" that IP
     502             :         //
     503          20 :         memset(&in, 0, sizeof(in));
     504          20 :         in.sin_family = AF_INET;
     505          20 :         in.sin_port = f_address.sin6_port;
     506          20 :         in.sin_addr.s_addr = f_address.sin6_addr.s6_addr32[3];
     507          40 :         return;
     508             :     }
     509             : 
     510           1 :     throw addr_invalid_state_exception("Not an IPv4 compatible address.");
     511             : }
     512             : 
     513             : 
     514             : /** \brief Save the specified IPv6 address in this addr object.
     515             :  *
     516             :  * This function saves the specified IPv6 address in this addr object.
     517             :  * The function does not check the validity of the address. It is
     518             :  * expected to be valid.
     519             :  *
     520             :  * The address may be an embedded IPv4 address.
     521             :  *
     522             :  * \param[in] in6  The source IPv6 to save in the addr object.
     523             :  */
     524       66107 : void addr::set_ipv6(struct sockaddr_in6 const & in6)
     525             : {
     526       66107 :     if(in6.sin6_family != AF_INET6)
     527             :     {
     528           2 :         throw addr_invalid_argument_exception("addr::set_ipv6(): the input address does not represent an IPv6 address (family is not AF_INET6).");
     529             :     }
     530       66105 :     memcpy(&f_address, &in6, sizeof(in6));
     531             : 
     532       66105 :     address_changed();
     533       66105 : }
     534             : 
     535             : 
     536             : /** \brief Retrieve a copy of this addr IP address.
     537             :  *
     538             :  * This function returns the current IP address saved in this
     539             :  * addr object. The IP may represent an IPv4 address in which
     540             :  * case the is_ipv4() returns true.
     541             :  *
     542             :  * \param[out] in6  The structure where the address gets saved.
     543             :  */
     544          21 : void addr::get_ipv6(struct sockaddr_in6 & in6) const
     545             : {
     546          21 :     memcpy(&in6, &f_address, sizeof(in6));
     547          21 : }
     548             : 
     549             : 
     550             : /** \brief Retrive the IPv4 as a string.
     551             :  *
     552             :  * This function returns a string representing the IP address
     553             :  * defined in this addr object.
     554             :  *
     555             :  * The \p mode parameter defines what gets output.
     556             :  *
     557             :  * \li ip_string_t::IP_STRING_ONLY -- only the IP address
     558             :  * \li ip_string_t::IP_STRING_PORT -- the IP and port
     559             :  * \li ip_string_t::IP_STRING_MASK -- the IP and mask
     560             :  * \li ip_string_t::IP_STRING_ALL -- the IP, port, and mask
     561             :  *
     562             :  * The ip_string_t::IP_STRING_BRACKET is viewed as
     563             :  * ip_string_t::IP_STRING_ONLY.
     564             :  *
     565             :  * The ip_string_t::IP_STRING_BRACKET_MASK is viewed as
     566             :  * ip_string_t::IP_STRING_MASK.
     567             :  *
     568             :  * \exception addr_invalid_state_exception
     569             :  * If the addr object does not currently represent an IPv4 then
     570             :  * this exception is raised.
     571             :  *
     572             :  * \param[in] mode  How the output string is to be built.
     573             :  */
     574      131697 : std::string addr::to_ipv4_string(string_ip_t mode) const
     575             : {
     576      131697 :     if(is_ipv4())
     577             :     {
     578             :         // this is an IPv4 mapped in an IPv6, "unmap" that IP
     579             :         // so the inet_ntop() can correctly generate an output IP
     580             :         //
     581             :         struct in_addr in;
     582      131691 :         memset(&in, 0, sizeof(in));
     583      131691 :         in.s_addr = f_address.sin6_addr.s6_addr32[3];
     584             :         char buf[INET_ADDRSTRLEN + 1];
     585      131691 :         if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
     586             :         {
     587      131691 :             if(mode != string_ip_t::STRING_IP_ONLY)
     588             :             {
     589        1152 :                 std::stringstream result;
     590         576 :                 result << buf;
     591         576 :                 if(mode == string_ip_t::STRING_IP_PORT
     592         266 :                 || mode == string_ip_t::STRING_IP_ALL)
     593             :                 {
     594         546 :                     result << ":";
     595         546 :                     result << ntohs(f_address.sin6_port);
     596             :                 }
     597         576 :                 if(mode == string_ip_t::STRING_IP_MASK
     598         566 :                 || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     599         556 :                 || mode == string_ip_t::STRING_IP_ALL)
     600             :                 {
     601         256 :                     memset(&in, 0, sizeof(in));
     602         256 :                     in.s_addr = htonl((f_mask[12] << 24) | (f_mask[13] << 16) | (f_mask[14] << 8) | f_mask[15]);
     603         256 :                     if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
     604             :                     {
     605         256 :                         result << "/";
     606         256 :                         result << buf; // TODO: convert to simple number if possible
     607             :                     }
     608             :                 }
     609         576 :                 return result.str();
     610             :             }
     611      131115 :             return std::string(buf);
     612             :         }
     613             :         // IPv4 should never fail converting the address unless the
     614             :         // buffer was too small...
     615             :     }
     616             : 
     617           6 :     throw addr_invalid_state_exception("Not an IPv4 compatible address.");
     618             : }
     619             : 
     620             : 
     621             : /** \brief Convert the addr object to a string.
     622             :  *
     623             :  * This function converts the addr object to a canonicalized string.
     624             :  * This can be used to compare two IPv6 together as strings, although
     625             :  * it is probably better to compare them using the < and == operators.
     626             :  *
     627             :  * By default the function returns with the IPv6 address defined
     628             :  * between square bracket, so the output of this function can be
     629             :  * used as the input of the set_addr_port() function. You may
     630             :  * also request the address without the brackets.
     631             :  *
     632             :  * \exception addr_invalid_argument_exception
     633             :  * If the binary IP address cannot be converted to ASCII, this exception
     634             :  * is raised.
     635             :  *
     636             :  * \param[in] mode  How the output string is to be built.
     637             :  *
     638             :  * \return The addr object converted to an IPv6 address.
     639             :  */
     640      263044 : std::string addr::to_ipv6_string(string_ip_t mode) const
     641             : {
     642             :     char buf[INET6_ADDRSTRLEN + 1];
     643      263044 :     if(inet_ntop(AF_INET6, &f_address.sin6_addr, buf, sizeof(buf)) != nullptr)
     644             :     {
     645             :         bool const include_brackets(mode == string_ip_t::STRING_IP_BRACKETS
     646      197492 :                                  || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     647      197480 :                                  || mode == string_ip_t::STRING_IP_PORT // port requires us to add brackets
     648      329240 :                                  || mode == string_ip_t::STRING_IP_ALL);
     649             : 
     650      526088 :         std::stringstream result;
     651             : 
     652             :         // always insert the IP, even if ANY or "BROADCAST"
     653             :         //
     654      263044 :         if(include_brackets)
     655             :         {
     656      197476 :             result << "[";
     657             :         }
     658      263044 :         result << buf;
     659      263044 :         if(include_brackets)
     660             :         {
     661      197476 :             result << "]";
     662             :         }
     663             : 
     664             :         // got a port?
     665             :         //
     666      263044 :         if(mode == string_ip_t::STRING_IP_PORT
     667      131760 :         || mode == string_ip_t::STRING_IP_ALL)
     668             :         {
     669      131912 :             result << ":";
     670      131912 :             result << ntohs(f_address.sin6_port);
     671             :         }
     672             : 
     673             :         // got a mask?
     674             :         //
     675      263044 :         if(mode == string_ip_t::STRING_IP_MASK
     676      263032 :         || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     677      263020 :         || mode == string_ip_t::STRING_IP_ALL)
     678             :         {
     679         652 :             if(inet_ntop(AF_INET6, f_mask, buf, sizeof(buf)) != nullptr)
     680             :             {
     681         652 :                 result << "/";
     682         652 :                 if(include_brackets)
     683             :                 {
     684         640 :                     result << "[";
     685             :                 }
     686         652 :                 result << buf; // TODO: convert to simple number if possible
     687         652 :                 if(include_brackets)
     688             :                 {
     689         640 :                     result << "]";
     690             :                 }
     691             :             }
     692             :         }
     693             : 
     694      526088 :         return result.str();
     695             :     }
     696             : 
     697             :     throw addr_invalid_argument_exception("The address from this addr could not be converted to a valid canonicalized IPv6 address.");  // LCOV_EXCL_LINE
     698             : }
     699             : 
     700             : 
     701             : /** \brief Return the address as IPv4 or IPv6.
     702             :  *
     703             :  * Depending on whether the address represents an IPv4 or an IPv6,
     704             :  * this function returns the corresponding address. Since the format
     705             :  * of both types of addresses can always be distinguished, it poses
     706             :  * no concerns.
     707             :  *
     708             :  * \exception 
     709             :  * If include_brackets is false and include_port is true, this
     710             :  * exception is raised because we cannot furfill the request.
     711             :  *
     712             :  * \param[in] mode  How the output string is to be built.
     713             :  *
     714             :  * \return The addr object converted to an IPv4 or an IPv6 address.
     715             :  */
     716      131766 : std::string addr::to_ipv4or6_string(string_ip_t mode) const
     717             : {
     718      131766 :     return is_ipv4() ? to_ipv4_string(mode)
     719      131766 :                      : to_ipv6_string(mode);
     720             : }
     721             : 
     722             : 
     723             : /** \brief Determine the type of network this IP represents.
     724             :  *
     725             :  * The IP address may represent various type of networks. This
     726             :  * function returns that type.
     727             :  *
     728             :  * The function checks the address either as IPv4 when is_ipv4()
     729             :  * returns true, otherwise as IPv6.
     730             :  *
     731             :  * See:
     732             :  *
     733             :  * \li https://en.wikipedia.org/wiki/Reserved_IP_addresses
     734             :  * \li https://tools.ietf.org/html/rfc3330
     735             :  * \li https://tools.ietf.org/html/rfc5735 (IPv4)
     736             :  * \li https://tools.ietf.org/html/rfc5156 (IPv6)
     737             :  *
     738             :  * \return One of the possible network types as defined in the
     739             :  *         network_type_t enumeration.
     740             :  */
     741      131773 : addr::network_type_t addr::get_network_type() const
     742             : {
     743      131773 :     if(f_private_network_defined == network_type_t::NETWORK_TYPE_UNDEFINED)
     744             :     {
     745      131614 :         f_private_network_defined = network_type_t::NETWORK_TYPE_UNKNOWN;
     746             : 
     747      131614 :         if(is_ipv4())
     748             :         {
     749             :             // get the address in host order
     750             :             //
     751             :             // we can use a simple mask + compare to know whether it is
     752             :             // this or that once in host order
     753             :             //
     754       65886 :             uint32_t const host_ip(ntohl(f_address.sin6_addr.s6_addr32[3]));
     755             : 
     756       65886 :             if((host_ip & 0xFF000000) == 0x0A000000         // 10.0.0.0/8
     757       65873 :             || (host_ip & 0xFFF00000) == 0xAC100000         // 172.16.0.0/12
     758       65753 :             || (host_ip & 0xFFFF0000) == 0xC0A80000)        // 192.168.0.0/16
     759             :             {
     760       65682 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
     761             :             }
     762         204 :             else if((host_ip & 0xFFC00000) == 0x64400000)   // 100.64.0.0/10
     763             :             {
     764          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_CARRIER;
     765             :             }
     766         194 :             else if((host_ip & 0xFFFF0000) == 0xA9FE0000)   // 169.254.0.0/16
     767             :             {
     768          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
     769             :             }
     770         184 :             else if((host_ip & 0xF0000000) == 0xE0000000)   // 224.0.0.0/4
     771             :             {
     772             :                 // there are many sub-groups on this one which are probably
     773             :                 // still in use...
     774             :                 //
     775          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
     776             :             }
     777         174 :             else if((host_ip & 0xFF000000) == 0x7F000000)   // 127.0.0.0/8
     778             :             {
     779          13 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK; // i.e. localhost
     780             :             }
     781         161 :             else if(host_ip == 0x00000000)
     782             :             {
     783           1 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_ANY; // i.e. 0.0.0.0
     784             :             }
     785             :         }
     786             :         else //if(is_ipv6()) -- if not IPv4, we have an IPv6
     787             :         {
     788             :             // for IPv6 it was simplified by using a prefix for
     789             :             // all types; really way easier than IPv4
     790             :             //
     791       65728 :             if(f_address.sin6_addr.s6_addr32[0] == 0      // ::
     792          34 :             && f_address.sin6_addr.s6_addr32[1] == 0
     793          30 :             && f_address.sin6_addr.s6_addr32[2] == 0
     794          26 :             && f_address.sin6_addr.s6_addr32[3] == 0)
     795             :             {
     796             :                 // this is the "any" IP address
     797           2 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_ANY;
     798             :             }
     799             :             else
     800             :             {
     801       65726 :                 uint16_t const prefix(ntohs(f_address.sin6_addr.s6_addr16[0]));
     802             : 
     803       65726 :                 if((prefix & 0xFF00) == 0xFD00)                 // fd00::/8
     804             :                 {
     805          10 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
     806             :                 }
     807       65716 :                 else if((prefix & 0xFFC0) == 0xFE80    // fe80::/10
     808       65706 :                      || (prefix & 0xFF0F) == 0xFF02)   // ffx2::/16
     809             :                 {
     810         120 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
     811             :                 }
     812       65596 :                 else if((prefix & 0xFF0F) == 0xFF01    // ffx1::/16
     813          50 :                      || (f_address.sin6_addr.s6_addr32[0] == 0      // ::1
     814          32 :                       && f_address.sin6_addr.s6_addr32[1] == 0
     815          28 :                       && f_address.sin6_addr.s6_addr32[2] == 0
     816          24 :                       && f_address.sin6_addr.s6_addr16[6] == 0
     817          22 :                       && f_address.sin6_addr.s6_addr16[7] == htons(1)))
     818             :                 {
     819       65566 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK;
     820             :                 }
     821          30 :                 else if((prefix & 0xFF00) == 0xFF00)   // ff00::/8
     822             :                 {
     823             :                     // this one must be after the link-local and loopback networks
     824          10 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
     825             :                 }
     826             :             }
     827             :         }
     828             :     }
     829             : 
     830      131773 :     return f_private_network_defined;
     831             : }
     832             : 
     833             : 
     834             : /** \brief Get the network type string
     835             :  *
     836             :  * Translate the network type into a string, which can be really useful
     837             :  * to log that information.
     838             :  *
     839             :  * Note that PUBLIC is the same as UNKNOWN, this function returns
     840             :  * "Unknown" in that case, though.
     841             :  *
     842             :  * \return The string representing the type of network.
     843             :  */
     844         159 : std::string addr::get_network_type_string() const
     845             : {
     846         159 :     std::string name;
     847         159 :     switch( get_network_type() )
     848             :     {
     849             :     case addr::network_type_t::NETWORK_TYPE_UNDEFINED  : name= "Undefined";  break; // LCOV_EXCL_LINE -- get_network_type() defines it...
     850          40 :     case addr::network_type_t::NETWORK_TYPE_PRIVATE    : name= "Private";    break;
     851          10 :     case addr::network_type_t::NETWORK_TYPE_CARRIER    : name= "Carrier";    break;
     852          30 :     case addr::network_type_t::NETWORK_TYPE_LINK_LOCAL : name= "Local Link"; break;
     853          20 :     case addr::network_type_t::NETWORK_TYPE_MULTICAST  : name= "Multicast";  break;
     854          40 :     case addr::network_type_t::NETWORK_TYPE_LOOPBACK   : name= "Loopback";   break;
     855           3 :     case addr::network_type_t::NETWORK_TYPE_ANY        : name= "Any";        break;
     856          16 :     case addr::network_type_t::NETWORK_TYPE_UNKNOWN    : name= "Unknown";    break; // == NETWORK_TYPE_PUBLIC
     857             :     }
     858         159 :     return name;
     859             : }
     860             : 
     861             : 
     862             : /** \brief Retrieve the interface name
     863             :  *
     864             :  * This function retrieves the name of the interface of the address.
     865             :  * This is set using the get_local_addresses() static method.
     866             :  */
     867           1 : std::string addr::get_iface_name() const
     868             : {
     869           1 :     return f_iface_name;
     870             : }
     871             : 
     872             : 
     873             : /** \brief Transform the IP into a domain name.
     874             :  *
     875             :  * This function transforms the IP address in this `addr` object in a
     876             :  * name such as "snap.website".
     877             :  *
     878             :  * \note
     879             :  * The function does not cache the result because it is rarely used (at least
     880             :  * at this time). So you should cache the result and avoid calling this
     881             :  * function more than once as the process can be very slow.
     882             :  *
     883             :  * \todo
     884             :  * Speed enhancement can be achieved by using getaddrinfo_a(). That would
     885             :  * work with a vector of addr objects.
     886             :  *
     887             :  * \return The domain name. If not available, an empty string.
     888             :  */
     889           7 : std::string addr::get_name() const
     890             : {
     891             :     char host[NI_MAXHOST];
     892             : 
     893           7 :     int flags(NI_NAMEREQD);
     894           7 :     if(f_protocol == IPPROTO_UDP)
     895             :     {
     896           4 :         flags |= NI_DGRAM;
     897             :     }
     898             : 
     899             :     // TODO: test with the NI_IDN* flags and make sure we know what we get
     900             :     //       (i.e. we want UTF-8 as a result)
     901             :     //
     902           7 :     int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), host, sizeof(host), nullptr, 0, flags));
     903             : 
     904             :     // return value is 0, then it worked
     905             :     //
     906           7 :     return r == 0 ? host : std::string();
     907             : }
     908             : 
     909             : 
     910             : /** \brief Transform the port into a service name.
     911             :  *
     912             :  * This function transforms the port in this `addr` object in a
     913             :  * name such as "http".
     914             :  *
     915             :  * \note
     916             :  * The function does not cache the result because it is rarely used (at least
     917             :  * at this time). So you should cache the result and avoid calling this
     918             :  * function more than once as the process is somewhat slow.
     919             :  *
     920             :  * \warning
     921             :  * The getnameinfo() will return a string with a number if it does not
     922             :  * know the server (i.e. this is the equivalent to std::to_string() of
     923             :  * the port.) For port 0, the function always returns an empty string.
     924             :  *
     925             :  * \return The service name. If not available, an empty string.
     926             :  */
     927           5 : std::string addr::get_service() const
     928             : {
     929           5 :     if(f_address.sin6_port == 0)
     930             :     {
     931           1 :         return std::string();
     932             :     }
     933             : 
     934             :     char service[NI_MAXSERV];
     935             : 
     936           4 :     int flags(NI_NAMEREQD);
     937           4 :     if(f_protocol == IPPROTO_UDP)
     938             :     {
     939           2 :         flags |= NI_DGRAM;
     940             :     }
     941           4 :     int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), nullptr, 0, service, sizeof(service), flags));
     942             : 
     943             :     // return value is 0, then it worked
     944             :     //
     945             :     return r == 0 ? service
     946           4 :                   : std::string();
     947             : }
     948             : 
     949             : 
     950             : /** \brief Retrieve the port.
     951             :  *
     952             :  * This function retrieves the port of the IP address in host order.
     953             :  *
     954             :  * \return The port defined along this address.
     955             :  */
     956      197584 : int addr::get_port() const
     957             : {
     958      197584 :     return ntohs(f_address.sin6_port);
     959             : }
     960             : 
     961             : 
     962             : /** \brief Retrieve the protocol.
     963             :  *
     964             :  * This function retrieves the protocol as specified on the
     965             :  * set_addr_port() function or corresponding constructor.
     966             :  *
     967             :  * You may change the protocol with the set_protocol() function.
     968             :  *
     969             :  * \return protocol such as IPPROTO_TCP or IPPROTO_UDP.
     970             :  */
     971      132051 : int addr::get_protocol() const
     972             : {
     973      132051 :     return f_protocol;
     974             : }
     975             : 
     976             : 
     977             : /** \brief Check whether an IP matches a CIDR.
     978             :  *
     979             :  * When an IP address is defined along a mask, it can match a set of
     980             :  * other IP addresses. This function can be used to see whether
     981             :  * \p ip matches \p this IP address and mask.
     982             :  *
     983             :  * \warning
     984             :  * This function only checks the IP address. It totally ignores the
     985             :  * port, family, protocol and other peripheral details.
     986             :  *
     987             :  * \param[in] ip  The address to match against this IP/mask CIDR.
     988             :  *
     989             :  * \return true if \p ip is a match.
     990             :  */
     991          10 : bool addr::match(addr const & ip) const
     992             : {
     993         166 :     for(int idx(0); idx < 16; ++idx)
     994             :     {
     995         158 :         if((f_address.sin6_addr.s6_addr[idx] & f_mask[idx]) != (ip.f_address.sin6_addr.s6_addr[idx] & f_mask[idx]))
     996             :         {
     997           2 :             return false;
     998             :         }
     999             :     }
    1000             : 
    1001           8 :     return true;
    1002             : }
    1003             : 
    1004             : 
    1005             : /** \brief Check whether two addresses are equal.
    1006             :  *
    1007             :  * This function compares the left hand side (this) and the right
    1008             :  * hand side (rhs) for equality. If both represent the same IP
    1009             :  * address, then the function returns true.
    1010             :  *
    1011             :  * \warning
    1012             :  * The function only compares the address itself. The family, port,
    1013             :  * flow info, scope identifier, protocol are all ignored.
    1014             :  *
    1015             :  * \return true if \p this is equal to \p rhs.
    1016             :  */
    1017          35 : bool addr::operator == (addr const & rhs) const
    1018             : {
    1019          35 :     return f_address.sin6_addr == rhs.f_address.sin6_addr;
    1020             : }
    1021             : 
    1022             : 
    1023             : /** \brief Check whether two addresses are not equal.
    1024             :  *
    1025             :  * This function compares the left hand side (this) and the right
    1026             :  * hand side (rhs) for inequality. If both represent the same IP
    1027             :  * address, then the function returns false.
    1028             :  *
    1029             :  * \warning
    1030             :  * The function only compares the address itself. The family, port,
    1031             :  * flow info, scope identifier, protocol are all ignored.
    1032             :  *
    1033             :  * \return true if \p this is not equal to \p rhs.
    1034             :  */
    1035           9 : bool addr::operator != (addr const & rhs) const
    1036             : {
    1037           9 :     return f_address.sin6_addr != rhs.f_address.sin6_addr;
    1038             : }
    1039             : 
    1040             : 
    1041             : /** \brief Compare two addresses to know which one is smaller.
    1042             :  *
    1043             :  * This function compares the left hand side (this) and the right
    1044             :  * hand side (rhs) to know which one is the smallest. If both
    1045             :  * are equal or the left hand side is larger than the right hand
    1046             :  * side, then it returns false, otherwise it returns true.
    1047             :  *
    1048             :  * \warning
    1049             :  * The function only compares the address itself. The family, port,
    1050             :  * flow info, scope identifier, protocol are all ignored.
    1051             :  *
    1052             :  * \return true if \p this is smaller than \p rhs.
    1053             :  */
    1054           7 : bool addr::operator < (addr const & rhs) const
    1055             : {
    1056           7 :     return f_address.sin6_addr < rhs.f_address.sin6_addr;
    1057             : }
    1058             : 
    1059             : 
    1060             : /** \brief Compare two addresses to know which one is smaller or equal.
    1061             :  *
    1062             :  * This function compares the left hand side (this) and the right
    1063             :  * hand side (rhs) to know whether the left hand side is smaller or
    1064             :  * equal to thr right handside.
    1065             :  *
    1066             :  * \warning
    1067             :  * The function only compares the address itself. The family, port,
    1068             :  * flow info, scope identifier, protocol are all ignored.
    1069             :  *
    1070             :  * \return true if \p this is smaller than \p rhs.
    1071             :  */
    1072         668 : bool addr::operator <= (addr const & rhs) const
    1073             : {
    1074         668 :     return f_address.sin6_addr <= rhs.f_address.sin6_addr;
    1075             : }
    1076             : 
    1077             : 
    1078             : /** \brief Compare two addresses to know which one is smaller.
    1079             :  *
    1080             :  * This function compares the left hand side (this) and the right
    1081             :  * hand side (rhs) to know which one is the smallest. If both
    1082             :  * are equal or the left hand side is larger than the right hand
    1083             :  * side, then it returns false, otherwise it returns true.
    1084             :  *
    1085             :  * \warning
    1086             :  * The function only compares the address itself. The family, port,
    1087             :  * flow info, scope identifier, protocol are all ignored.
    1088             :  *
    1089             :  * \return true if \p this is smaller than \p rhs.
    1090             :  */
    1091          15 : bool addr::operator > (addr const & rhs) const
    1092             : {
    1093          15 :     return f_address.sin6_addr > rhs.f_address.sin6_addr;
    1094             : }
    1095             : 
    1096             : 
    1097             : /** \brief Compare two addresses to know which one is smaller.
    1098             :  *
    1099             :  * This function compares the left hand side (this) and the right
    1100             :  * hand side (rhs) to know which one is the smallest. If both
    1101             :  * are equal or the left hand side is larger than the right hand
    1102             :  * side, then it returns false, otherwise it returns true.
    1103             :  *
    1104             :  * \warning
    1105             :  * The function only compares the address itself. The family, port,
    1106             :  * flow info, scope identifier, protocol are all ignored.
    1107             :  *
    1108             :  * \return true if \p this is smaller than \p rhs.
    1109             :  */
    1110         281 : bool addr::operator >= (addr const & rhs) const
    1111             : {
    1112         281 :     return f_address.sin6_addr >= rhs.f_address.sin6_addr;
    1113             : }
    1114             : 
    1115             : 
    1116             : /** \brief Mark that the address changed.
    1117             :  *
    1118             :  * This functions makes sure that some of the parameters being cached
    1119             :  * get reset in such a way that checking the cache will again return
    1120             :  * the correct answer.
    1121             :  *
    1122             :  * \sa get_network_type()
    1123             :  */
    1124      132470 : void addr::address_changed()
    1125             : {
    1126      132470 :     f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
    1127      132470 : }
    1128             : 
    1129             : 
    1130             : /** \brief Return a list of local addresses on this machine.
    1131             :  *
    1132             :  * Peruse the list of available interfaces, and return any detected ip addresses
    1133             :  * in a vector.
    1134             :  */
    1135           1 : addr::vector_t addr::get_local_addresses()
    1136             : {
    1137             :     // get the list of interface addresses
    1138             :     //
    1139           1 :     struct ifaddrs * ifa_start(nullptr);
    1140           1 :     if(getifaddrs(&ifa_start) != 0)
    1141             :     {
    1142             :         // TODO: Should this throw, or just return an empty list quietly?
    1143             :         //
    1144             :         return vector_t(); // LCOV_EXCL_LINE
    1145             :     }
    1146             : 
    1147           2 :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
    1148             : 
    1149           2 :     vector_t addr_list;
    1150          16 :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
    1151             :     {
    1152          21 :         if( ifa->ifa_addr == nullptr ) continue;
    1153             : 
    1154          24 :         addr the_address;
    1155             : 
    1156          15 :         the_address.f_iface_name = ifa->ifa_name;
    1157          15 :         uint16_t const family( ifa->ifa_addr->sa_family );
    1158          15 :         if( family == AF_INET )
    1159             :         {
    1160           5 :             the_address.set_ipv4( *(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)) );
    1161             :         }
    1162          10 :         else if( family == AF_INET6 )
    1163             :         {
    1164           4 :             the_address.set_ipv6( *(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)) );
    1165             :         }
    1166             :         else
    1167             :         {
    1168             :             // TODO: can we just ignore invalid addresses?
    1169             :             //throw addr_invalid_structure_exception( "Unknown address family!" );
    1170           6 :             continue;
    1171             :         }
    1172             : 
    1173           9 :         addr_list.push_back( the_address );
    1174             :     }
    1175             : 
    1176           1 :     return addr_list;
    1177             : }
    1178             : 
    1179             : 
    1180             : /** \brief Check whether this address represents this computer.
    1181             :  *
    1182             :  * This function reads the addresses as given to us by the getifaddrs()
    1183             :  * function. This is a system function that returns a complete list of
    1184             :  * all the addresses this computer is managing / represents. In other
    1185             :  * words, a list of address that other computers can use to connect
    1186             :  * to this computer (assuming proper firewall, of course.)
    1187             :  *
    1188             :  * \warning
    1189             :  * The list of addresses from getifaddrs() is not being cached. So you
    1190             :  * probably do not want to call this function in a loop. That being
    1191             :  * said, I still would imagine that retrieving that list is fast.
    1192             :  *
    1193             :  * \return a computer_interface_address_t enumeration: error, true, or
    1194             :  *         false at this time; on error errno should be set to represent
    1195             :  *         what the error was.
    1196             :  */
    1197          13 : addr::computer_interface_address_t addr::is_computer_interface_address() const
    1198             : {
    1199             :     // TBD: maybe we could cache the ifaddrs for a small amount of time
    1200             :     //      (i.e. 1 minute) so additional calls within that time
    1201             :     //      can go even faster?
    1202             :     //
    1203             : 
    1204             :     // get the list of interface addresses
    1205             :     //
    1206          13 :     struct ifaddrs * ifa_start(nullptr);
    1207          13 :     if(getifaddrs(&ifa_start) != 0)
    1208             :     {
    1209             :         return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
    1210             :     }
    1211          26 :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
    1212             : 
    1213          13 :     bool const ipv4(is_ipv4());
    1214          13 :     uint16_t const family(ipv4 ? AF_INET : AF_INET6);
    1215         195 :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
    1216             :     {
    1217         184 :         if(ifa->ifa_addr != nullptr
    1218         184 :         && ifa->ifa_addr->sa_family == family)
    1219             :         {
    1220          56 :             if(ipv4)
    1221             :             {
    1222             :                 // the interface address structure is a 'struct sockaddr_in'
    1223             :                 //
    1224         102 :                 if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
    1225          51 :                             f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
    1226             :                             sizeof(struct in_addr)) == 0)
    1227             :                 {
    1228           1 :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
    1229             :                 }
    1230             :             }
    1231             :             else
    1232             :             {
    1233             :                 // the interface address structure is a 'struct sockaddr_in6'
    1234             :                 //
    1235           5 :                 if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr, &f_address.sin6_addr, sizeof(f_address.sin6_addr)) == 0)
    1236             :                 {
    1237           1 :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
    1238             :                 }
    1239             :             }
    1240             :         }
    1241             :     }
    1242             : 
    1243          11 :     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
    1244             : }
    1245             : 
    1246             : 
    1247             : 
    1248             : 
    1249             : 
    1250             : 
    1251             : 
    1252             : 
    1253             : 
    1254             : 
    1255             : 
    1256             : 
    1257             : 
    1258             : 
    1259             : 
    1260             : 
    1261           6 : }
    1262             : // snap_addr namespace
    1263             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12