LCOV - code coverage report
Current view: top level - src - addr.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 310 310 100.0 %
Date: 2018-05-30 23:58:02 Functions: 41 41 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-2018  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // https://snapwebsites.org/project/libaddr
       5             : //
       6             : // Permission is hereby granted, free of charge, to any person obtaining a
       7             : // copy of this software and associated documentation files (the
       8             : // "Software"), to deal in the Software without restriction, including
       9             : // without limitation the rights to use, copy, modify, merge, publish,
      10             : // distribute, sublicense, and/or sell copies of the Software, and to
      11             : // permit persons to whom the Software is furnished to do so, subject to
      12             : // the following conditions:
      13             : //
      14             : // The above copyright notice and this permission notice shall be included
      15             : // in all copies or substantial portions of the Software.
      16             : //
      17             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      18             : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      19             : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      20             : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      21             : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      22             : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      23             : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      24             : //
      25             : 
      26             : /** \file
      27             :  * \brief The implementation of the addr class.
      28             :  *
      29             :  * This file includes the implementation of the addr class. The one that
      30             :  * deals with low level classes.
      31             :  */
      32             : 
      33             : // self
      34             : //
      35             : #include "libaddr/addr.h"
      36             : #include "libaddr/addr_exceptions.h"
      37             : 
      38             : // C++ library
      39             : //
      40             : #include <sstream>
      41             : #include <iostream>
      42             : 
      43             : // C library
      44             : //
      45             : #include <ifaddrs.h>
      46             : #include <netdb.h>
      47             : 
      48             : 
      49             : 
      50             : /** \mainpage
      51             :  * \brief libaddr, a C++ library to handle network IP addresses in IPv4 and IPv6.
      52             :  *
      53             :  * ### Introduction
      54             :  *
      55             :  * This library is used to parse strings of IP addresses to lists of
      56             :  * binary IP addresses ready to be used by functions such as bind(),
      57             :  * send(), recv(), etc.
      58             :  *
      59             :  * The library supports multiple addresses separated by commas and/or
      60             :  * spaces, ports, and CIDR masks. It can check whether an address matches
      61             :  * another taking the mask in account. It can sort IPs numerically. It
      62             :  * can determine the type of an IP address (i.e. is it a local address,
      63             :  * a private address, a public address?)
      64             :  *
      65             :  * The library also has a function to read IP addresses from your
      66             :  * computer interfaces and return that list. Very practical to know
      67             :  * whether an IP address represents your computer or not.
      68             :  *
      69             :  * ### Usage
      70             :  *
      71             :  * The library is composed of three main classes:
      72             :  *
      73             :  * \li addr
      74             :  *
      75             :  * The address class holds one address, a port, a protocol and a few
      76             :  * other parts. This is what one uses to connect or listen with an
      77             :  * address.
      78             :  *
      79             :  * The address is kept by addr in an IPv6 address structure.
      80             :  *
      81             :  * By default the CIDR of the address is all 1s (i.e. no masking, all
      82             :  * bits considered important.) The mask is always 128 bits. If you are
      83             :  * dealing with IPv4, make sure that the first 12 bytes are set to 255.
      84             :  *
      85             :  * The class also offers a set of functions to transform the binary
      86             :  * address it is holding to a string.
      87             :  *
      88             :  * \li addr_range
      89             :  *
      90             :  * It is possible to define a range of addresses and ports. This class
      91             :  * holds a 'from' address and a 'to' address. By default neither is
      92             :  * defined. You have to call the set_from() and set_to() functions.
      93             :  *
      94             :  * The addr_range::vector_t is what the addr_parser returns after
      95             :  * parsing a string representing one of more addresses.
      96             :  *
      97             :  * \note
      98             :  * The range is functional, however, the parser does not yet support
      99             :  * parsing range of addresses and ports.
     100             :  *
     101             :  * \li addr_parser
     102             :  *
     103             :  * The parser is used to transform a string to an address.
     104             :  *
     105             :  * It supports many variations of its input, which are handled by
     106             :  * the 'allow' flags. The set_allow() and get_allow() functions can
     107             :  * be used to tweak the parser in supporting such and such feature.
     108             :  *
     109             :  * By default, the input is expected to be an address and a port
     110             :  * separated by a colon (i.e. `"1.2.3.4:1234"` in IPv4 and `"[::1]:1234"`
     111             :  * in IPv6.)
     112             :  *
     113             :  * ### Parser
     114             :  *
     115             :  * The parser supports the following syntax (ranges are not yet supported
     116             :  * and they do not appear in the following list):
     117             :  *
     118             :  * \code
     119             :  *    start: address_list
     120             :  *
     121             :  *    address_list: address_cidr
     122             :  *                | address_list address_list_separators address_cidr
     123             :  *
     124             :  *    address_list_separators: ' '
     125             :  *                           | ','
     126             :  *                           | address_list_separators address_list_separators
     127             :  *
     128             :  *    address_cidr: address_port
     129             :  *                | address_port '/' cidr
     130             :  *
     131             :  *    address_port: address
     132             :  *                | address ':' port
     133             :  *
     134             :  *    address: ipv4
     135             :  *           | ipv6
     136             :  *
     137             :  *    cidr: decimal_number
     138             :  *        | ipv4
     139             :  *        | ipv6
     140             :  *
     141             :  *    ipv4: decimal_number '.' decimal_number '.' decimal_number '.' decimal_number
     142             :  *
     143             :  *    ipv6: '[' hexadecimal_number_list ']'
     144             :  *
     145             :  *    port: decimal_number
     146             :  *
     147             :  *    hexadecimal_number_list: hexadecimal_number
     148             :  *                           | hexadecimal_number_list ':' hexadecimal_number
     149             :  *
     150             :  *    decimal_number: [0-9]+
     151             :  *
     152             :  *    hexadecimal_number: [0-9a-fA-F]+
     153             :  * \endcode
     154             :  *
     155             :  * When accepting multiple addresses separated by commas or spaces, the
     156             :  * number of commas and spaces separating two address is not significant.
     157             :  * The input string can also start or end with commas and spaces. The
     158             :  * following variable defines exactly two IP address:
     159             :  *
     160             :  * \code
     161             :  *       addresses=  ,1.2.3.4,   ,5.6.7.8,,
     162             :  * \endcode
     163             :  *
     164             :  * (note that the parser should not be passed the "addresses=" part.)
     165             :  */
     166             : 
     167             : 
     168             : /** \brief The libaddr classes are all defined in this namespace.
     169             :  *
     170             :  * The addr namespace includes all the addr classes.
     171             :  */
     172             : namespace addr
     173             : {
     174             : 
     175             : /*
     176             :  * Various sytem address structures
     177             : 
     178             : // Any address is 16 bytes or less
     179             : struct sockaddr {
     180             :    unsigned short    sa_family;    // address family, AF_xxx
     181             :    char              sa_data[14];  // 14 bytes of protocol address
     182             : };
     183             : 
     184             : struct sockaddr_storage {
     185             :     sa_family_t  ss_family;     // address family
     186             : 
     187             :     // all this is padding, implementation specific, ignore it:
     188             :     char      __ss_pad1[_SS_PAD1SIZE];
     189             :     int64_t   __ss_align;
     190             :     char      __ss_pad2[_SS_PAD2SIZE];
     191             : };
     192             : 
     193             : 
     194             : typedef uint32_t in_addr_t;   // or `__be32`
     195             : struct in_addr {
     196             :     in_addr_t        s_addr;
     197             : };
     198             : 
     199             : 
     200             : // IPv4
     201             : struct sockaddr_in {
     202             :     short            sin_family;   // e.g. AF_INET, AF_INET6
     203             :     unsigned short   sin_port;     // e.g. htons(3490)
     204             :     struct in_addr   sin_addr;     // see struct in_addr, below
     205             :     char             sin_zero[8];  // zero this if you want to
     206             : };
     207             : 
     208             : 
     209             : // IPv6
     210             : struct sockaddr_in6 {
     211             :     u_int16_t       sin6_family;   // address family, AF_INET6
     212             :     u_int16_t       sin6_port;     // port number, Network Byte Order
     213             :     u_int32_t       sin6_flowinfo; // IPv6 flow information
     214             :     struct in6_addr sin6_addr;     // IPv6 address
     215             :     u_int32_t       sin6_scope_id; // Scope ID
     216             : };
     217             : 
     218             : struct in6_addr
     219             :   {
     220             :     union
     221             :       {
     222             :         uint8_t __u6_addr8[16];
     223             : #ifdef __USE_MISC
     224             :         uint16_t __u6_addr16[8];
     225             :         uint32_t __u6_addr32[4];
     226             : #endif
     227             :       } __in6_u;
     228             : #define s6_addr                 __in6_u.__u6_addr8
     229             : #ifdef __USE_MISC
     230             : # define s6_addr16              __in6_u.__u6_addr16
     231             : # define s6_addr32              __in6_u.__u6_addr32
     232             : #endif
     233             :   };
     234             : 
     235             : 
     236             : */
     237             : 
     238             : 
     239             : /** \brief Details used by the addr class implementation.
     240             :  *
     241             :  * We have a function to check whether an address is part of
     242             :  * the interfaces of your computer. This check requires the
     243             :  * use of a `struct ifaddrs` and as such it requires to
     244             :  * delete that structure. We define a deleter for that
     245             :  * strucure here.
     246             :  */
     247             : namespace
     248             : {
     249             : 
     250             : /** \brief Delete an ifaddrs structure.
     251             :  *
     252             :  * This deleter is used to make sure all the ifaddrs get released when
     253             :  * an exception occurs or the function using such exists.
     254             :  *
     255             :  * \param[in] ia  The ifaddrs structure to free.
     256             :  */
     257          14 : void ifaddrs_deleter(struct ifaddrs * ia)
     258             : {
     259          14 :     freeifaddrs(ia);
     260          14 : }
     261             : 
     262             : 
     263             : }
     264             : // no name namespace
     265             : 
     266             : 
     267             : /** \brief Create an addr object that represents an ANY address.
     268             :  *
     269             :  * This function initializes the addr object with the ANY address.
     270             :  * The port is set to 0 and the protocol to TCP.
     271             :  *
     272             :  * It is strongly suggested that you change those parameters
     273             :  * before really using this address since a port of zero and
     274             :  * the protocol may be wrong.
     275             :  */
     276      264219 : addr::addr()
     277             : {
     278             :     // keep default protocol (TCP)
     279      264219 : }
     280             : 
     281             : 
     282             : /** \brief Create an addr object from a binary IPv4 address.
     283             :  *
     284             :  * This function initializes this addr object with the specified IPv4
     285             :  * address. The is_ipv4() function will return true.
     286             :  *
     287             :  * \param[in] in  The binary IPv4 address.
     288             :  */
     289       65937 : addr::addr(struct sockaddr_in const & in)
     290             : {
     291       65912 :     set_ipv4(in);
     292             :     // keep default protocol (TCP)
     293       65887 : }
     294             : 
     295             : 
     296             : /** \brief Create an addr object from a binary IPv6 address.
     297             :  *
     298             :  * This function initializes this addr object with the specified IPv6
     299             :  * address. The is_ipv4() function will return false.
     300             :  *
     301             :  * \param[in] in6  The binary IPv6 address.
     302             :  */
     303       65991 : addr::addr(struct sockaddr_in6 const & in6)
     304             : {
     305       65990 :     set_ipv6(in6);
     306             :     // keep default protocol (TCP)
     307       65989 : }
     308             : 
     309             : 
     310             : /** \brief Save an IPv4 in this addr object.
     311             :  *
     312             :  * This function saves the specified IPv4 in this addr object.
     313             :  *
     314             :  * Since we save the data in an IPv6 structure, it is kept in
     315             :  * the addr as an IPv4 mapped in an IPv6 address. It can still
     316             :  * be retrieved right back as an IPv4 with the get_ipv4() function.
     317             :  *
     318             :  * \param[in] in  The IPv4 address to save in this addr object.
     319             :  */
     320       66449 : void addr::set_ipv4(struct sockaddr_in const & in)
     321             : {
     322       66449 :     if(in.sin_family != AF_INET)
     323             :     {
     324             :         // although we convert the IPv4 to an IPv6 below, we really only
     325             :         // support AF_INET on entry
     326             :         //
     327          50 :         throw addr_invalid_argument_exception("addr::set_ipv4(): the input address does not represent an IPv4 address (family is not AF_INET).");
     328             :     }
     329             : 
     330             :     // reset the address first
     331       66399 :     memset(&f_address, 0, sizeof(f_address));
     332             : 
     333             :     // then transform the IPv4 to an IPv6
     334             :     //
     335             :     // Note: this is not an IPv6 per se, it is an IPv4 mapped within an
     336             :     //       IPv6 and your network anwway stack needs to support IPv4
     337             :     //       in order to use that IP...
     338             :     //
     339       66399 :     f_address.sin6_family = AF_INET6;
     340       66399 :     f_address.sin6_port = in.sin_port;
     341       66399 :     f_address.sin6_addr.s6_addr16[5] = 0xFFFF;
     342       66399 :     f_address.sin6_addr.s6_addr32[3] = in.sin_addr.s_addr;
     343             : 
     344       66399 :     address_changed();
     345       66399 : }
     346             : 
     347             : 
     348             : /** \brief Set the port of this address.
     349             :  *
     350             :  * This function changes the port of this address to \p port.
     351             :  *
     352             :  * \exception addr_invalid_argument_exception
     353             :  * This exception is raised whenever the \p port parameter is set to
     354             :  * an invalid number (negative or larger than 65535.)
     355             :  *
     356             :  * \param[in] port  The new port to save in this address.
     357             :  */
     358       65741 : void addr::set_port(int port)
     359             : {
     360       65741 :     if(port > 65535 
     361       65641 :     || port < 0)
     362             :     {
     363         200 :         throw addr_invalid_argument_exception("port to set_port() cannot be out of the allowed range [0..65535].");
     364             :     }
     365       65541 :     f_address.sin6_port = htons(port);
     366       65541 : }
     367             : 
     368             : 
     369             : /** \brief Change the protocol using a string.
     370             :  *
     371             :  * This function is used to change the current protocol defined in
     372             :  * this addr object.
     373             :  *
     374             :  * \exception addr_invalid_argument_exception
     375             :  * We currently support "tcp", "udp", and "ip". Any other protocol
     376             :  * name generates this exception.
     377             :  *
     378             :  * \param[in] protocol  The name of the protocol ("tcp", "udp", or "ip")
     379             :  */
     380           7 : void addr::set_protocol(char const * protocol)
     381             : {
     382           7 :     if(protocol == nullptr)
     383             :     {
     384           1 :         throw addr_invalid_argument_exception("protocol pointer to set_protocol() cannot be a nullptr.");
     385             :     }
     386             : 
     387           6 :     if(strcmp(protocol, "ip") == 0)
     388             :     {
     389           1 :         f_protocol = IPPROTO_IP;
     390             :     }
     391           5 :     else if(strcmp(protocol, "tcp") == 0)
     392             :     {
     393           1 :         f_protocol = IPPROTO_TCP;
     394             :     }
     395           4 :     else if(strcmp(protocol, "udp") == 0)
     396             :     {
     397           1 :         f_protocol = IPPROTO_UDP;
     398             :     }
     399             :     else
     400             :     {
     401             :         throw addr_invalid_argument_exception(
     402             :                           std::string("unknown protocol \"")
     403           6 :                         + protocol
     404           9 :                         + "\", expected \"tcp\" or \"udp\" (string).");
     405             :     }
     406             : 
     407           3 :     address_changed();
     408           3 : }
     409             : 
     410             : 
     411             : /** \brief Set the protocol numerically.
     412             :  *
     413             :  * This function sets the protocol from a number instead of a name.
     414             :  *
     415             :  * Note that we only support IPPROTO_TCP and IPPROTO_UDP for now.
     416             :  * Any other protocol will make this function raise an exception.
     417             :  *
     418             :  * \todo
     419             :  * We may want to support any protocol number at this level. If your
     420             :  * application is limited then it should verify the protocol and
     421             :  * make sure it supports it before using this address. At the same
     422             :  * time, the IP protocol is pretty much locked up with just TCP
     423             :  * and UDP these days (there is the IP protocol, but that's not
     424             :  * useful at our level.)
     425             :  *
     426             :  * \exception addr_invalid_argument_exception
     427             :  * This exception is raised if the specified protocol is not currently
     428             :  * supported by the addr implementation.
     429             :  *
     430             :  * \param[in] protocol  The new numeric protocol.
     431             :  */
     432      131960 : void addr::set_protocol(int protocol)
     433             : {
     434      131960 :     switch(protocol)
     435             :     {
     436             :     case IPPROTO_IP:
     437             :     case IPPROTO_TCP:
     438             :     case IPPROTO_UDP:
     439      131860 :         f_protocol = protocol;
     440      131860 :         break;
     441             : 
     442             :     default:
     443             :         throw addr_invalid_argument_exception(
     444             :                           "unknown protocol number "
     445         200 :                         + std::to_string(protocol)
     446         200 :                         + ", expected \"tcp\" ("
     447         400 :                         + std::to_string(static_cast<int>(IPPROTO_TCP))
     448         200 :                         + ") or \"udp\" ("
     449         400 :                         + std::to_string(static_cast<int>(IPPROTO_UDP))
     450         300 :                         + ") (numeric).");
     451             : 
     452             :     }
     453      131860 : }
     454             : 
     455             : 
     456             : /** \brief Set the mask.
     457             :  *
     458             :  * The input mask must be exactly 16 bytes. If you are dealing with an
     459             :  * IPv4, make sure the first 12 bytes are 255.
     460             :  *
     461             :  * \param[in] mask  The mask to save in this address.
     462             :  */
     463         427 : void addr::set_mask(uint8_t const * mask)
     464             : {
     465         427 :     memcpy(f_mask, mask, sizeof(f_mask));
     466         427 : }
     467             : 
     468             : 
     469             : /** \brief Apply the mask to the IP address.
     470             :  *
     471             :  * This function applies the mask to this address IP address. This means
     472             :  * the bits that are 0 in the mask will also be 0 in the address once
     473             :  * the function returns.
     474             :  *
     475             :  * This should be called if you are trying to canonicalize an IP/mask.
     476             :  */
     477           1 : void addr::apply_mask()
     478             : {
     479          17 :     for(int idx(0); idx < 16; ++idx)
     480             :     {
     481          16 :         f_address.sin6_addr.s6_addr[idx] &= f_mask[idx];
     482             :     }
     483           1 : }
     484             : 
     485             : 
     486             : /** \brief Get the mask.
     487             :  *
     488             :  * The output buffer for the mask must be at least 16 bytes. If you are
     489             :  * dealing with an IPv4, all the bytes are expected to be 255 except
     490             :  * the bottom 4 bytes (offset 12, 13, 14, 15).
     491             :  *
     492             :  * \param[out] mask  The buffer where the mask gets copied.
     493             :  */
     494         154 : void addr::get_mask(uint8_t * mask)
     495             : {
     496         154 :     memcpy(mask, f_mask, sizeof(f_mask));
     497         154 : }
     498             : 
     499             : 
     500             : /** \brief Check whether this address represents an IPv4 address.
     501             :  *
     502             :  * The IPv6 format supports embedding IPv4 addresses. This function
     503             :  * returns true if the embedded address is an IPv4. When this function
     504             :  * returns true, the get_ipv4() can be called. Otherwise, the get_ipv4()
     505             :  * function throws an exception.
     506             :  *
     507             :  * \return true if this address represents an IPv4 address.
     508             :  */
     509      527606 : bool addr::is_ipv4() const
     510             : {
     511      527606 :     return f_address.sin6_addr.s6_addr32[0] == 0
     512      329885 :         && f_address.sin6_addr.s6_addr32[1] == 0
     513      329881 :         && f_address.sin6_addr.s6_addr16[4] == 0
     514      857485 :         && f_address.sin6_addr.s6_addr16[5] == 0xFFFF;
     515             : }
     516             : 
     517             : 
     518             : /** \brief Retreive the IPv4 address.
     519             :  *
     520             :  * This function can be used to retrieve the IPv4 address of this addr
     521             :  * object. If the address is not an IPv4, then the function throws.
     522             :  *
     523             :  * \exception addr_invalid_structure_exception
     524             :  * This exception is raised if the address is not an IPv4 address.
     525             :  *
     526             :  * \param[out] in  The structure where the IPv4 Internet address gets saved.
     527             :  */
     528          35 : void addr::get_ipv4(struct sockaddr_in & in) const
     529             : {
     530          35 :     if(is_ipv4())
     531             :     {
     532             :         // this is an IPv4 mapped in an IPv6, "unmap" that IP
     533             :         //
     534          34 :         memset(&in, 0, sizeof(in));
     535          34 :         in.sin_family = AF_INET;
     536          34 :         in.sin_port = f_address.sin6_port;
     537          34 :         in.sin_addr.s_addr = f_address.sin6_addr.s6_addr32[3];
     538          68 :         return;
     539             :     }
     540             : 
     541           1 :     throw addr_invalid_state_exception("Not an IPv4 compatible address.");
     542             : }
     543             : 
     544             : 
     545             : /** \brief Save the specified IPv6 address in this addr object.
     546             :  *
     547             :  * This function saves the specified IPv6 address in this addr object.
     548             :  * The function does not check the validity of the address. It is
     549             :  * expected to be valid.
     550             :  *
     551             :  * The address may be an embedded IPv4 address.
     552             :  *
     553             :  * \param[in] in6  The source IPv6 to save in the addr object.
     554             :  */
     555       66117 : void addr::set_ipv6(struct sockaddr_in6 const & in6)
     556             : {
     557       66117 :     if(in6.sin6_family != AF_INET6)
     558             :     {
     559           2 :         throw addr_invalid_argument_exception("addr::set_ipv6(): the input address does not represent an IPv6 address (family is not AF_INET6).");
     560             :     }
     561       66115 :     memcpy(&f_address, &in6, sizeof(in6));
     562             : 
     563       66115 :     address_changed();
     564       66115 : }
     565             : 
     566             : 
     567             : /** \brief Retrieve a copy of this addr IP address.
     568             :  *
     569             :  * This function returns the current IP address saved in this
     570             :  * addr object. The IP may represent an IPv4 address in which
     571             :  * case the is_ipv4() returns true.
     572             :  *
     573             :  * \param[out] in6  The structure where the address gets saved.
     574             :  */
     575          21 : void addr::get_ipv6(struct sockaddr_in6 & in6) const
     576             : {
     577          21 :     memcpy(&in6, &f_address, sizeof(in6));
     578          21 : }
     579             : 
     580             : 
     581             : /** \brief Retrive the IPv4 as a string.
     582             :  *
     583             :  * This function returns a string representing the IP address
     584             :  * defined in this addr object.
     585             :  *
     586             :  * The \p mode parameter defines what gets output.
     587             :  *
     588             :  * \li ip_string_t::IP_STRING_ONLY -- only the IP address
     589             :  * \li ip_string_t::IP_STRING_PORT -- the IP and port
     590             :  * \li ip_string_t::IP_STRING_MASK -- the IP and mask
     591             :  * \li ip_string_t::IP_STRING_ALL -- the IP, port, and mask
     592             :  *
     593             :  * The ip_string_t::IP_STRING_BRACKET is viewed as
     594             :  * ip_string_t::IP_STRING_ONLY.
     595             :  *
     596             :  * The ip_string_t::IP_STRING_BRACKET_MASK is viewed as
     597             :  * ip_string_t::IP_STRING_MASK.
     598             :  *
     599             :  * \exception addr_invalid_state_exception
     600             :  * If the addr object does not currently represent an IPv4 then
     601             :  * this exception is raised.
     602             :  *
     603             :  * \param[in] mode  How the output string is to be built.
     604             :  */
     605      131703 : std::string addr::to_ipv4_string(string_ip_t mode) const
     606             : {
     607      131703 :     if(is_ipv4())
     608             :     {
     609             :         // this is an IPv4 mapped in an IPv6, "unmap" that IP
     610             :         // so the inet_ntop() can correctly generate an output IP
     611             :         //
     612             :         struct in_addr in;
     613      131697 :         memset(&in, 0, sizeof(in));
     614      131697 :         in.s_addr = f_address.sin6_addr.s6_addr32[3];
     615             :         char buf[INET_ADDRSTRLEN + 1];
     616      131697 :         if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
     617             :         {
     618      131697 :             if(mode != string_ip_t::STRING_IP_ONLY)
     619             :             {
     620        1152 :                 std::stringstream result;
     621         576 :                 result << buf;
     622         576 :                 if(mode == string_ip_t::STRING_IP_PORT
     623         266 :                 || mode == string_ip_t::STRING_IP_ALL)
     624             :                 {
     625         546 :                     result << ":";
     626         546 :                     result << ntohs(f_address.sin6_port);
     627             :                 }
     628         576 :                 if(mode == string_ip_t::STRING_IP_MASK
     629         566 :                 || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     630         556 :                 || mode == string_ip_t::STRING_IP_ALL)
     631             :                 {
     632         256 :                     memset(&in, 0, sizeof(in));
     633         256 :                     in.s_addr = htonl((f_mask[12] << 24) | (f_mask[13] << 16) | (f_mask[14] << 8) | f_mask[15]);
     634         256 :                     if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
     635             :                     {
     636         256 :                         result << "/";
     637         256 :                         result << buf; // TODO: convert to simple number if possible
     638             :                     }
     639             :                 }
     640         576 :                 return result.str();
     641             :             }
     642      131121 :             return std::string(buf);
     643             :         }
     644             :         // IPv4 should never fail converting the address unless the
     645             :         // buffer was too small...
     646             :     }
     647             : 
     648           6 :     throw addr_invalid_state_exception("Not an IPv4 compatible address.");
     649             : }
     650             : 
     651             : 
     652             : /** \brief Convert the addr object to a string.
     653             :  *
     654             :  * This function converts the addr object to a canonicalized string.
     655             :  * This can be used to compare two IPv6 together as strings, although
     656             :  * it is probably better to compare them using the < and == operators.
     657             :  *
     658             :  * By default the function returns with the IPv6 address defined
     659             :  * between square bracket, so the output of this function can be
     660             :  * used as the input of the set_addr_port() function. You may
     661             :  * also request the address without the brackets.
     662             :  *
     663             :  * \exception addr_invalid_state_exception
     664             :  * If the binary IP address cannot be converted to ASCII, this exception
     665             :  * is raised.
     666             :  *
     667             :  * \param[in] mode  How the output string is to be built.
     668             :  *
     669             :  * \return The addr object converted to an IPv6 address.
     670             :  */
     671      263054 : std::string addr::to_ipv6_string(string_ip_t mode) const
     672             : {
     673             :     char buf[INET6_ADDRSTRLEN + 1];
     674      263054 :     if(inet_ntop(AF_INET6, &f_address.sin6_addr, buf, sizeof(buf)) != nullptr)
     675             :     {
     676             :         bool const include_brackets(mode == string_ip_t::STRING_IP_BRACKETS
     677      197502 :                                  || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     678      197490 :                                  || mode == string_ip_t::STRING_IP_PORT // port requires us to add brackets
     679      329260 :                                  || mode == string_ip_t::STRING_IP_ALL);
     680             : 
     681      526108 :         std::stringstream result;
     682             : 
     683             :         // always insert the IP, even if ANY or "BROADCAST"
     684             :         //
     685      263054 :         if(include_brackets)
     686             :         {
     687      197476 :             result << "[";
     688             :         }
     689      263054 :         result << buf;
     690      263054 :         if(include_brackets)
     691             :         {
     692      197476 :             result << "]";
     693             :         }
     694             : 
     695             :         // got a port?
     696             :         //
     697      263054 :         if(mode == string_ip_t::STRING_IP_PORT
     698      131770 :         || mode == string_ip_t::STRING_IP_ALL)
     699             :         {
     700      131912 :             result << ":";
     701      131912 :             result << ntohs(f_address.sin6_port);
     702             :         }
     703             : 
     704             :         // got a mask?
     705             :         //
     706      263054 :         if(mode == string_ip_t::STRING_IP_MASK
     707      263042 :         || mode == string_ip_t::STRING_IP_BRACKETS_MASK
     708      263030 :         || mode == string_ip_t::STRING_IP_ALL)
     709             :         {
     710         652 :             if(inet_ntop(AF_INET6, f_mask, buf, sizeof(buf)) != nullptr)
     711             :             {
     712         652 :                 result << "/";
     713         652 :                 if(include_brackets)
     714             :                 {
     715         640 :                     result << "[";
     716             :                 }
     717         652 :                 result << buf; // TODO: convert to simple number if possible
     718         652 :                 if(include_brackets)
     719             :                 {
     720         640 :                     result << "]";
     721             :                 }
     722             :             }
     723             :         }
     724             : 
     725      526108 :         return result.str();
     726             :     }
     727             : 
     728             :     throw addr_invalid_state_exception("The address from this addr could not be converted to a valid canonicalized IPv6 address.");  // LCOV_EXCL_LINE
     729             : }
     730             : 
     731             : 
     732             : /** \brief Return the address as IPv4 or IPv6.
     733             :  *
     734             :  * Depending on whether the address represents an IPv4 or an IPv6,
     735             :  * this function returns the corresponding address. Since the format
     736             :  * of both types of addresses can always be distinguished, it poses
     737             :  * no concerns.
     738             :  *
     739             :  * \exception 
     740             :  * If include_brackets is false and include_port is true, this
     741             :  * exception is raised because we cannot furfill the request.
     742             :  *
     743             :  * \param[in] mode  How the output string is to be built.
     744             :  *
     745             :  * \return The addr object converted to an IPv4 or an IPv6 address.
     746             :  */
     747      131774 : std::string addr::to_ipv4or6_string(string_ip_t mode) const
     748             : {
     749      131774 :     return is_ipv4() ? to_ipv4_string(mode)
     750      131774 :                      : to_ipv6_string(mode);
     751             : }
     752             : 
     753             : 
     754             : /** \brief Determine the type of network this IP represents.
     755             :  *
     756             :  * The IP address may represent various type of networks. This
     757             :  * function returns that type.
     758             :  *
     759             :  * The function checks the address either as IPv4 when is_ipv4()
     760             :  * returns true, otherwise as IPv6.
     761             :  *
     762             :  * See:
     763             :  *
     764             :  * \li https://en.wikipedia.org/wiki/Reserved_IP_addresses
     765             :  * \li https://tools.ietf.org/html/rfc3330
     766             :  * \li https://tools.ietf.org/html/rfc5735 (IPv4)
     767             :  * \li https://tools.ietf.org/html/rfc5156 (IPv6)
     768             :  *
     769             :  * \return One of the possible network types as defined in the
     770             :  *         network_type_t enumeration.
     771             :  */
     772      131773 : addr::network_type_t addr::get_network_type() const
     773             : {
     774      131773 :     if(f_private_network_defined == network_type_t::NETWORK_TYPE_UNDEFINED)
     775             :     {
     776      131614 :         f_private_network_defined = network_type_t::NETWORK_TYPE_UNKNOWN;
     777             : 
     778      131614 :         if(is_ipv4())
     779             :         {
     780             :             // get the address in host order
     781             :             //
     782             :             // we can use a simple mask + compare to know whether it is
     783             :             // this or that once in host order
     784             :             //
     785       65886 :             uint32_t const host_ip(ntohl(f_address.sin6_addr.s6_addr32[3]));
     786             : 
     787       65886 :             if((host_ip & 0xFF000000) == 0x0A000000         // 10.0.0.0/8
     788       65873 :             || (host_ip & 0xFFF00000) == 0xAC100000         // 172.16.0.0/12
     789       65753 :             || (host_ip & 0xFFFF0000) == 0xC0A80000)        // 192.168.0.0/16
     790             :             {
     791       65682 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
     792             :             }
     793         204 :             else if((host_ip & 0xFFC00000) == 0x64400000)   // 100.64.0.0/10
     794             :             {
     795          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_CARRIER;
     796             :             }
     797         194 :             else if((host_ip & 0xFFFF0000) == 0xA9FE0000)   // 169.254.0.0/16
     798             :             {
     799          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
     800             :             }
     801         184 :             else if((host_ip & 0xF0000000) == 0xE0000000)   // 224.0.0.0/4
     802             :             {
     803             :                 // there are many sub-groups on this one which are probably
     804             :                 // still in use...
     805             :                 //
     806          10 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
     807             :             }
     808         174 :             else if((host_ip & 0xFF000000) == 0x7F000000)   // 127.0.0.0/8
     809             :             {
     810          13 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK; // i.e. localhost
     811             :             }
     812         161 :             else if(host_ip == 0x00000000)
     813             :             {
     814           1 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_ANY; // i.e. 0.0.0.0
     815             :             }
     816             :         }
     817             :         else //if(is_ipv6()) -- if not IPv4, we have an IPv6
     818             :         {
     819             :             // for IPv6 it was simplified by using a prefix for
     820             :             // all types; really way easier than IPv4
     821             :             //
     822       65728 :             if(f_address.sin6_addr.s6_addr32[0] == 0      // ::
     823          34 :             && f_address.sin6_addr.s6_addr32[1] == 0
     824          30 :             && f_address.sin6_addr.s6_addr32[2] == 0
     825          26 :             && f_address.sin6_addr.s6_addr32[3] == 0)
     826             :             {
     827             :                 // this is the "any" IP address
     828           2 :                 f_private_network_defined = network_type_t::NETWORK_TYPE_ANY;
     829             :             }
     830             :             else
     831             :             {
     832       65726 :                 uint16_t const prefix(ntohs(f_address.sin6_addr.s6_addr16[0]));
     833             : 
     834       65726 :                 if((prefix & 0xFF00) == 0xFD00)                 // fd00::/8
     835             :                 {
     836          10 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
     837             :                 }
     838       65716 :                 else if((prefix & 0xFFC0) == 0xFE80    // fe80::/10
     839       65706 :                      || (prefix & 0xFF0F) == 0xFF02)   // ffx2::/16
     840             :                 {
     841         120 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
     842             :                 }
     843       65596 :                 else if((prefix & 0xFF0F) == 0xFF01    // ffx1::/16
     844          50 :                      || (f_address.sin6_addr.s6_addr32[0] == 0      // ::1
     845          32 :                       && f_address.sin6_addr.s6_addr32[1] == 0
     846          28 :                       && f_address.sin6_addr.s6_addr32[2] == 0
     847          24 :                       && f_address.sin6_addr.s6_addr16[6] == 0
     848          22 :                       && f_address.sin6_addr.s6_addr16[7] == htons(1)))
     849             :                 {
     850       65566 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK;
     851             :                 }
     852          30 :                 else if((prefix & 0xFF00) == 0xFF00)   // ff00::/8
     853             :                 {
     854             :                     // this one must be after the link-local and loopback networks
     855          10 :                     f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
     856             :                 }
     857             :             }
     858             :         }
     859             :     }
     860             : 
     861      131773 :     return f_private_network_defined;
     862             : }
     863             : 
     864             : 
     865             : /** \brief Get the network type string
     866             :  *
     867             :  * Translate the network type into a string, which can be really useful
     868             :  * to log that information.
     869             :  *
     870             :  * Note that PUBLIC is the same as UNKNOWN, this function returns
     871             :  * "Unknown" in that case, though.
     872             :  *
     873             :  * \return The string representing the type of network.
     874             :  */
     875         159 : std::string addr::get_network_type_string() const
     876             : {
     877         159 :     std::string name;
     878         159 :     switch( get_network_type() )
     879             :     {
     880             :     case addr::network_type_t::NETWORK_TYPE_UNDEFINED  : name = "Undefined";  break; // LCOV_EXCL_LINE -- get_network_type() defines it...
     881          40 :     case addr::network_type_t::NETWORK_TYPE_PRIVATE    : name = "Private";    break;
     882          10 :     case addr::network_type_t::NETWORK_TYPE_CARRIER    : name = "Carrier";    break;
     883          30 :     case addr::network_type_t::NETWORK_TYPE_LINK_LOCAL : name = "Local Link"; break;
     884          20 :     case addr::network_type_t::NETWORK_TYPE_MULTICAST  : name = "Multicast";  break;
     885          40 :     case addr::network_type_t::NETWORK_TYPE_LOOPBACK   : name = "Loopback";   break;
     886           3 :     case addr::network_type_t::NETWORK_TYPE_ANY        : name = "Any";        break;
     887          16 :     case addr::network_type_t::NETWORK_TYPE_UNKNOWN    : name = "Unknown";    break; // == NETWORK_TYPE_PUBLIC
     888             :     }
     889         159 :     return name;
     890             : }
     891             : 
     892             : 
     893             : /** \brief Retrieve the interface name.
     894             :  *
     895             :  * This function retrieves the name of the interface of the address.
     896             :  * This is set using the get_local_addresses() static method.
     897             :  *
     898             :  * \return The name of the interface this address is assigned to.
     899             :  */
     900           1 : std::string addr::get_iface_name() const
     901             : {
     902           1 :     return f_iface_name;
     903             : }
     904             : 
     905             : 
     906             : /** \brief Create a socket from the IP address held by this addr object.
     907             :  *
     908             :  * This function creates a socket that corresponds to the addr object
     909             :  * definitions, it takes the protocol and family information in account.
     910             :  *
     911             :  * The flags can be used to add one or more of the following flags:
     912             :  *
     913             :  * \li SOCKET_FLAG_NONBLOCK -- create socket as non-block
     914             :  * \li SOCKET_FLAG_CLOEXEC -- close socket on an execv()
     915             :  * \li SOCKET_FLAG_REUSE -- for TCP socket, mark the address as immediately
     916             :  * reusable, ignored for UDP; only useful for server (bind + listen after
     917             :  * this call)
     918             :  *
     919             :  * \note
     920             :  * The IP protocol is viewed as TCP in this function.
     921             :  *
     922             :  * \warning
     923             :  * This class does not hold the socket created by this function.
     924             :  *
     925             :  * \todo
     926             :  * Move this to our libsnapnetwork once we create that separate library.
     927             :  * Probably within a form of low level socket class.
     928             :  *
     929             :  * \param[in] flags  A set of socket flags to use when creating the socket.
     930             :  * \param[in] reuse_address  Set the reuse address flag.
     931             :  *
     932             :  * \return The socket file descriptor or -1 on errors.
     933             :  */
     934           6 : int addr::create_socket(socket_flag_t flags) const
     935             : {
     936             :     int const sock_flags(
     937           6 :               ((flags & SOCKET_FLAG_CLOEXEC)  != 0 ? SOCK_CLOEXEC  : 0)
     938           6 :             | ((flags & SOCKET_FLAG_NONBLOCK) != 0 ? SOCK_NONBLOCK : 0));
     939           6 :     int const family(is_ipv4() ? AF_INET : AF_INET6);
     940             : 
     941           6 :     switch(f_protocol)
     942             :     {
     943             :     case IPPROTO_IP: // interpret as TCP...
     944             :     case IPPROTO_TCP:
     945             :         {
     946           4 :             int s(socket(family, SOCK_STREAM | sock_flags, IPPROTO_TCP));
     947             : 
     948           4 :             if(s >= 0
     949           4 :             && (flags & SOCKET_FLAG_REUSE) != 0)
     950             :             {
     951             :                 // set the "reuse that address immediately" flag, we totally
     952             :                 // ignore errors on that one
     953             :                 //
     954           2 :                 int optval(1);
     955           2 :                 socklen_t const optlen(sizeof(optval));
     956           2 :                 static_cast<void>(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, optlen));
     957             :             }
     958           4 :             return s;
     959             :         }
     960             : 
     961             :     case IPPROTO_UDP:
     962           2 :         return socket(family, SOCK_DGRAM | sock_flags, IPPROTO_UDP);
     963             : 
     964             :     default:
     965             :         // this should never happen since we control the f_protocol field
     966             :         //
     967             :         return -1;      // LCOV_EXCL_LINE
     968             : 
     969             :     }
     970             : }
     971             : 
     972             : 
     973             : /** \brief Connect the specified socket to this IP address.
     974             :  *
     975             :  * When you create a TCP client, you can connect to a server. This
     976             :  * is done by using the connect() function which makes use of the
     977             :  * address to connect to the server.
     978             :  *
     979             :  * This function makes sure to select the correct connect() function
     980             :  * depending on whether this IP address is an IPv4 or an IPv6 address
     981             :  * (although we could always try with the IPv6 structure, it may or
     982             :  * may not work properly on all systems, so for now we use the
     983             :  * distinction.)
     984             :  *
     985             :  * \todo
     986             :  * Move this to our libsnapnetwork once we create that separate library.
     987             :  * Probably within a form of low level socket class.
     988             :  *
     989             :  * \param[in] s  The socket to connect to the address.
     990             :  *
     991             :  * \return 0 if the bind() succeeded, -1 on errors
     992             :  */
     993           4 : int addr::connect(int s) const
     994             : {
     995             :     // only TCP can connect, UDP binds and sends only
     996             :     //
     997           4 :     switch(f_protocol)
     998             :     {
     999             :     case IPPROTO_IP: // interpret as TCP...
    1000             :     case IPPROTO_TCP:
    1001           2 :         if(is_ipv4())
    1002             :         {
    1003             :             // this would most certainly work using the IPv6 address
    1004             :             // as in the else part, but to be sure, we use the IPv4
    1005             :             // as specified in the address (there could be other reasons
    1006             :             // than just your OS for this to fail if using IPv6.)
    1007             :             //
    1008             :             // IMPORTANT NOTE: also the family is used in the socket()
    1009             :             //                 call above and must match the address here...
    1010             :             //
    1011             :             sockaddr_in ipv4;
    1012           1 :             get_ipv4(ipv4);
    1013           1 :             return ::connect(s, reinterpret_cast<sockaddr const *>(&ipv4), sizeof(ipv4));
    1014             :         }
    1015             :         else
    1016             :         {
    1017           1 :             return ::connect(s, reinterpret_cast<sockaddr const *>(&f_address), sizeof(struct sockaddr_in6));
    1018             :         }
    1019             :         break;
    1020             : 
    1021             :     }
    1022             : 
    1023           2 :     return -1;
    1024             : }
    1025             : 
    1026             : 
    1027             : /** \brief Create a server with this socket listening on this IP address.
    1028             :  *
    1029             :  * This function will bind the socket \p s to the address defined in
    1030             :  * this addr object. This creates a server listening on that IP address.
    1031             :  *
    1032             :  * If the IP address is 127.0.0.1, then only local processes can connect
    1033             :  * to that server. If the IP address is 0.0.0.0, then anyone can connect
    1034             :  * to the server.
    1035             :  *
    1036             :  * This function works for TCP and UDP servers.
    1037             :  *
    1038             :  * If the IP address represents an IPv4 addressm then the bind() is done
    1039             :  * with an IPv4 address and not the IPv6 as it is stored.
    1040             :  *
    1041             :  * \todo
    1042             :  * Move this to our libsnapnetwork once we create that separate library.
    1043             :  * Probably within a form of low level socket class.
    1044             :  *
    1045             :  * \param[in] s  The socket to bind to this address.
    1046             :  *
    1047             :  * \return 0 if the bind() succeeded, -1 on errors
    1048             :  */
    1049           2 : int addr::bind(int s) const
    1050             : {
    1051           2 :     if(is_ipv4())
    1052             :     {
    1053             :         sockaddr_in ipv4;
    1054           1 :         get_ipv4(ipv4);
    1055           1 :         return ::bind(s, reinterpret_cast<sockaddr const *>(&ipv4), sizeof(ipv4));
    1056             :     }
    1057             :     else
    1058             :     {
    1059           1 :         return ::bind(s, reinterpret_cast<sockaddr const *>(&f_address), sizeof(struct sockaddr_in6));
    1060             :     }
    1061             : }
    1062             : 
    1063             : 
    1064             : /** \brief Initializes this addr object from a socket information.
    1065             :  *
    1066             :  * When you connect to a server or a clients connect to your server, the
    1067             :  * socket defines two IP addresses and ports: one on your side and one on
    1068             :  * the other side.
    1069             :  *
    1070             :  * The other side is called the _peer name_.
    1071             :  *
    1072             :  * You side is called the _socket name_ (i.e. the IP address of your computer,
    1073             :  * representing the interface used to perform that connection.)
    1074             :  *
    1075             :  * If you call this function with \p peer set to false then you get the
    1076             :  * address and port from your side. If you set \p peer to true,
    1077             :  * you get the other side address and port details.
    1078             :  *
    1079             :  * \todo
    1080             :  * Move this to our libsnapnetwork once we create that separate library.
    1081             :  * Probably within a form of low level socket class.
    1082             :  *
    1083             :  * \param[in] s  The socket from which you want to retrieve peer information.
    1084             :  * \param[in] peer  Whether to retrieve the peer or socket name.
    1085             :  */
    1086          14 : void addr::set_from_socket(int s, bool peer)
    1087             : {
    1088             :     // make sure the socket is defined and well
    1089             :     //
    1090          14 :     if(s < 0)
    1091             :     {
    1092           2 :         throw addr_invalid_argument_exception("addr::set_from_socket(): the socket cannot be a negative number.");
    1093             :     }
    1094             : 
    1095          12 :     struct sockaddr_storage address = sockaddr_storage();
    1096          12 :     socklen_t length(sizeof(address));
    1097             :     int r;
    1098          12 :     if(peer)
    1099             :     {
    1100             :         // this retrieves the information from the other side
    1101             :         //
    1102           6 :         r = getpeername(s, reinterpret_cast<struct sockaddr *>(&address), &length);
    1103             :     }
    1104             :     else
    1105             :     {
    1106             :         // retrieve the local socket information
    1107             :         //
    1108           6 :         r = getsockname(s, reinterpret_cast<struct sockaddr *>(&address), &length);
    1109             :     }
    1110          12 :     if(r != 0)
    1111             :     {
    1112           5 :         int const e(errno);
    1113             :         throw addr_io_exception(
    1114             :                   std::string("addr::set_from_socket(): ")
    1115          10 :                 + (peer ? "getpeername()" : "getsockname()")
    1116          10 :                 + " failed to retrieve IP address details (errno: "
    1117          20 :                 + std::to_string(e)
    1118          10 :                 + ", "
    1119          15 :                 + strerror(e)
    1120          15 :                 + ").");
    1121             :     }
    1122             : 
    1123           7 :     switch(address.ss_family)
    1124             :     {
    1125             :     case AF_INET:
    1126           3 :         set_ipv4(reinterpret_cast<struct sockaddr_in &>(address));
    1127           3 :         break;
    1128             : 
    1129             :     case AF_INET6:
    1130           3 :         set_ipv6(reinterpret_cast<struct sockaddr_in6 &>(address));
    1131           3 :         break;
    1132             : 
    1133             :     default:
    1134             :         throw addr_invalid_state_exception(
    1135             :                   std::string("addr::set_from_socket(): ")
    1136           2 :                 + (peer ? "getpeername()" : "getsockname()")
    1137           3 :                 + " returned a type of address, which is not understood, i.e. not AF_INET or AF_INET6.");
    1138             : 
    1139             :     }
    1140           6 : }
    1141             : 
    1142             : 
    1143             : /** \brief Transform the IP into a domain name.
    1144             :  *
    1145             :  * This function transforms the IP address in this `addr` object in a
    1146             :  * name such as "snap.website".
    1147             :  *
    1148             :  * \note
    1149             :  * The function does not cache the result because it is rarely used (at least
    1150             :  * at this time). So you should cache the result and avoid calling this
    1151             :  * function more than once as the process can be very slow.
    1152             :  *
    1153             :  * \todo
    1154             :  * Speed enhancement can be achieved by using getaddrinfo_a(). That would
    1155             :  * work with a vector of addr objects.
    1156             :  *
    1157             :  * \return The domain name. If not available, an empty string.
    1158             :  */
    1159           7 : std::string addr::get_name() const
    1160             : {
    1161             :     char host[NI_MAXHOST];
    1162             : 
    1163           7 :     int flags(NI_NAMEREQD);
    1164           7 :     if(f_protocol == IPPROTO_UDP)
    1165             :     {
    1166           4 :         flags |= NI_DGRAM;
    1167             :     }
    1168             : 
    1169             :     // TODO: test with the NI_IDN* flags and make sure we know what we get
    1170             :     //       (i.e. we want UTF-8 as a result)
    1171             :     //
    1172           7 :     int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), host, sizeof(host), nullptr, 0, flags));
    1173             : 
    1174             :     // return value is 0, then it worked
    1175             :     //
    1176           7 :     return r == 0 ? host : std::string();
    1177             : }
    1178             : 
    1179             : 
    1180             : /** \brief Transform the port into a service name.
    1181             :  *
    1182             :  * This function transforms the port in this `addr` object in a
    1183             :  * name such as "http".
    1184             :  *
    1185             :  * \note
    1186             :  * The function does not cache the result because it is rarely used (at least
    1187             :  * at this time). So you should cache the result and avoid calling this
    1188             :  * function more than once as the process is somewhat slow.
    1189             :  *
    1190             :  * \warning
    1191             :  * The getnameinfo() will return a string with a number if it does not
    1192             :  * know the server (i.e. this is the equivalent to std::to_string() of
    1193             :  * the port.) For port 0, the function always returns an empty string.
    1194             :  *
    1195             :  * \return The service name. If not available, an empty string.
    1196             :  */
    1197           5 : std::string addr::get_service() const
    1198             : {
    1199           5 :     if(f_address.sin6_port == 0)
    1200             :     {
    1201           1 :         return std::string();
    1202             :     }
    1203             : 
    1204             :     char service[NI_MAXSERV];
    1205             : 
    1206           4 :     int flags(NI_NAMEREQD);
    1207           4 :     if(f_protocol == IPPROTO_UDP)
    1208             :     {
    1209           2 :         flags |= NI_DGRAM;
    1210             :     }
    1211           4 :     int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), nullptr, 0, service, sizeof(service), flags));
    1212             : 
    1213             :     // return value is 0, then it worked
    1214             :     //
    1215             :     return r == 0 ? service
    1216           4 :                   : std::string();
    1217             : }
    1218             : 
    1219             : 
    1220             : /** \brief Retrieve the port.
    1221             :  *
    1222             :  * This function retrieves the port of the IP address in host order.
    1223             :  *
    1224             :  * \return The port defined along this address.
    1225             :  */
    1226      197592 : int addr::get_port() const
    1227             : {
    1228      197592 :     return ntohs(f_address.sin6_port);
    1229             : }
    1230             : 
    1231             : 
    1232             : /** \brief Retrieve the protocol.
    1233             :  *
    1234             :  * This function retrieves the protocol as specified on the
    1235             :  * set_addr_port() function or corresponding constructor.
    1236             :  *
    1237             :  * You may change the protocol with the set_protocol() function.
    1238             :  *
    1239             :  * \return protocol such as IPPROTO_TCP or IPPROTO_UDP.
    1240             :  */
    1241      132072 : int addr::get_protocol() const
    1242             : {
    1243      132072 :     return f_protocol;
    1244             : }
    1245             : 
    1246             : 
    1247             : /** \brief Check whether an IP matches a CIDR.
    1248             :  *
    1249             :  * When an IP address is defined along a mask, it can match a set of
    1250             :  * other IP addresses. This function can be used to see whether
    1251             :  * \p ip matches \p this IP address and mask.
    1252             :  *
    1253             :  * So in other words, the mask of `this` addr object is used to mask
    1254             :  * both, `this` and `p` before comparing the masked result.
    1255             :  *
    1256             :  * \warning
    1257             :  * This function only checks the IP address. It totally ignores the
    1258             :  * port, family, protocol and other peripheral details.
    1259             :  *
    1260             :  * \param[in] ip  The address to match against this IP/mask CIDR.
    1261             :  *
    1262             :  * \return true if \p ip is a match.
    1263             :  */
    1264          22 : bool addr::match(addr const & ip) const
    1265             : {
    1266         332 :     for(int idx(0); idx < 16; ++idx)
    1267             :     {
    1268         320 :         if((f_address.sin6_addr.s6_addr[idx] & f_mask[idx]) != (ip.f_address.sin6_addr.s6_addr[idx] & f_mask[idx]))
    1269             :         {
    1270          10 :             return false;
    1271             :         }
    1272             :     }
    1273             : 
    1274          12 :     return true;
    1275             : }
    1276             : 
    1277             : 
    1278             : /** \brief Check whether two addresses are equal.
    1279             :  *
    1280             :  * This function compares the left hand side (this) and the right
    1281             :  * hand side (rhs) for equality. If both represent the same IP
    1282             :  * address, then the function returns true.
    1283             :  *
    1284             :  * \warning
    1285             :  * The function only compares the address itself. The family, port,
    1286             :  * flow info, scope identifier, protocol are all ignored.
    1287             :  *
    1288             :  * \return true if \p this is equal to \p rhs.
    1289             :  */
    1290          35 : bool addr::operator == (addr const & rhs) const
    1291             : {
    1292          35 :     return f_address.sin6_addr == rhs.f_address.sin6_addr;
    1293             : }
    1294             : 
    1295             : 
    1296             : /** \brief Check whether two addresses are not equal.
    1297             :  *
    1298             :  * This function compares the left hand side (this) and the right
    1299             :  * hand side (rhs) for inequality. If both represent the same IP
    1300             :  * address, then the function returns false.
    1301             :  *
    1302             :  * \warning
    1303             :  * The function only compares the address itself. The family, port,
    1304             :  * flow info, scope identifier, protocol are all ignored.
    1305             :  *
    1306             :  * \return true if \p this is not equal to \p rhs.
    1307             :  */
    1308           9 : bool addr::operator != (addr const & rhs) const
    1309             : {
    1310           9 :     return f_address.sin6_addr != rhs.f_address.sin6_addr;
    1311             : }
    1312             : 
    1313             : 
    1314             : /** \brief Compare two addresses to know which one is smaller.
    1315             :  *
    1316             :  * This function compares the left hand side (this) and the right
    1317             :  * hand side (rhs) to know which one is the smallest. If both
    1318             :  * are equal or the left hand side is larger than the right hand
    1319             :  * side, then it returns false, otherwise it returns true.
    1320             :  *
    1321             :  * \warning
    1322             :  * The function only compares the address itself. The family, port,
    1323             :  * flow info, scope identifier, protocol are all ignored.
    1324             :  *
    1325             :  * \return true if \p this is smaller than \p rhs.
    1326             :  */
    1327           7 : bool addr::operator < (addr const & rhs) const
    1328             : {
    1329           7 :     return f_address.sin6_addr < rhs.f_address.sin6_addr;
    1330             : }
    1331             : 
    1332             : 
    1333             : /** \brief Compare two addresses to know which one is smaller or equal.
    1334             :  *
    1335             :  * This function compares the left hand side (this) and the right
    1336             :  * hand side (rhs) to know whether the left hand side is smaller or
    1337             :  * equal to thr right handside.
    1338             :  *
    1339             :  * \warning
    1340             :  * The function only compares the address itself. The family, port,
    1341             :  * flow info, scope identifier, protocol are all ignored.
    1342             :  *
    1343             :  * \return true if \p this is smaller than \p rhs.
    1344             :  */
    1345         685 : bool addr::operator <= (addr const & rhs) const
    1346             : {
    1347         685 :     return f_address.sin6_addr <= rhs.f_address.sin6_addr;
    1348             : }
    1349             : 
    1350             : 
    1351             : /** \brief Compare two addresses to know which one is smaller.
    1352             :  *
    1353             :  * This function compares the left hand side (this) and the right
    1354             :  * hand side (rhs) to know which one is the smallest. If both
    1355             :  * are equal or the left hand side is larger than the right hand
    1356             :  * side, then it returns false, otherwise it returns true.
    1357             :  *
    1358             :  * \warning
    1359             :  * The function only compares the address itself. The family, port,
    1360             :  * flow info, scope identifier, protocol are all ignored.
    1361             :  *
    1362             :  * \return true if \p this is smaller than \p rhs.
    1363             :  */
    1364          27 : bool addr::operator > (addr const & rhs) const
    1365             : {
    1366          27 :     return f_address.sin6_addr > rhs.f_address.sin6_addr;
    1367             : }
    1368             : 
    1369             : 
    1370             : /** \brief Compare two addresses to know which one is smaller.
    1371             :  *
    1372             :  * This function compares the left hand side (this) and the right
    1373             :  * hand side (rhs) to know which one is the smallest. If both
    1374             :  * are equal or the left hand side is larger than the right hand
    1375             :  * side, then it returns false, otherwise it returns true.
    1376             :  *
    1377             :  * \warning
    1378             :  * The function only compares the address itself. The family, port,
    1379             :  * flow info, scope identifier, protocol are all ignored.
    1380             :  *
    1381             :  * \return true if \p this is smaller than \p rhs.
    1382             :  */
    1383         290 : bool addr::operator >= (addr const & rhs) const
    1384             : {
    1385         290 :     return f_address.sin6_addr >= rhs.f_address.sin6_addr;
    1386             : }
    1387             : 
    1388             : 
    1389             : /** \brief Mark that the address changed.
    1390             :  *
    1391             :  * This functions makes sure that some of the parameters being cached
    1392             :  * get reset in such a way that checking the cache will again return
    1393             :  * the correct answer.
    1394             :  *
    1395             :  * \sa get_network_type()
    1396             :  */
    1397      132517 : void addr::address_changed()
    1398             : {
    1399      132517 :     f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
    1400      132517 : }
    1401             : 
    1402             : 
    1403             : /** \brief Return a list of local addresses on this machine.
    1404             :  *
    1405             :  * Peruse the list of available interfaces, and return any detected
    1406             :  * ip addresses in a vector.
    1407             :  *
    1408             :  * \return A vector of all the local interface IP addresses.
    1409             :  */
    1410           1 : addr::vector_t addr::get_local_addresses()
    1411             : {
    1412             :     // get the list of interface addresses
    1413             :     //
    1414           1 :     struct ifaddrs * ifa_start(nullptr);
    1415           1 :     if(getifaddrs(&ifa_start) != 0)
    1416             :     {
    1417             :         // TODO: Should this throw, or just return an empty list quietly?
    1418             :         //
    1419             :         return vector_t(); // LCOV_EXCL_LINE
    1420             :     }
    1421             : 
    1422           2 :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
    1423             : 
    1424           2 :     vector_t addr_list;
    1425          17 :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
    1426             :     {
    1427          16 :         if( ifa->ifa_addr == nullptr )
    1428             :         {
    1429           6 :             continue;
    1430             :         }
    1431             : 
    1432          26 :         addr the_address;
    1433             : 
    1434          16 :         the_address.f_iface_name = ifa->ifa_name;
    1435          16 :         uint16_t const family( ifa->ifa_addr->sa_family );
    1436          16 :         if( family == AF_INET )
    1437             :         {
    1438           6 :             the_address.set_ipv4( *(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)) );
    1439             :         }
    1440          10 :         else if( family == AF_INET6 )
    1441             :         {
    1442           4 :             the_address.set_ipv6( *(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)) );
    1443             :         }
    1444             :         else
    1445             :         {
    1446             :             // TODO: can we just ignore unexpected address families?
    1447             :             //throw addr_invalid_structure_exception( "Unknown address family!" );
    1448           6 :             continue;
    1449             :         }
    1450             : 
    1451          10 :         addr_list.push_back( the_address );
    1452             :     }
    1453             : 
    1454           1 :     return addr_list;
    1455             : }
    1456             : 
    1457             : 
    1458             : /** \brief Check whether this address represents this computer.
    1459             :  *
    1460             :  * This function reads the addresses as given to us by the getifaddrs()
    1461             :  * function. This is a system function that returns a complete list of
    1462             :  * all the addresses this computer is managing / represents. In other
    1463             :  * words, a list of address that other computers can use to connect
    1464             :  * to this computer (assuming proper firewall, of course.)
    1465             :  *
    1466             :  * \warning
    1467             :  * The list of addresses from getifaddrs() is not being cached. So you
    1468             :  * probably do not want to call this function in a loop. That being
    1469             :  * said, I still would imagine that retrieving that list is fast.
    1470             :  *
    1471             :  * \todo
    1472             :  * We need to apply the mask to make this work properly. This is why
    1473             :  * the current implementation fails big time (used by snapcommunicator.cpp).
    1474             :  *
    1475             :  * \return a computer_interface_address_t enumeration: error, true, or
    1476             :  *         false at this time; on error errno should be set to represent
    1477             :  *         what the error was.
    1478             :  */
    1479          13 : addr::computer_interface_address_t addr::is_computer_interface_address() const
    1480             : {
    1481             :     // TBD: maybe we could cache the ifaddrs for a small amount of time
    1482             :     //      (i.e. 1 minute) so additional calls within that time
    1483             :     //      can go even faster?
    1484             :     //
    1485             : 
    1486             :     // get the list of interface addresses
    1487             :     //
    1488          13 :     struct ifaddrs * ifa_start(nullptr);
    1489          13 :     if(getifaddrs(&ifa_start) != 0)
    1490             :     {
    1491             :         return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
    1492             :     }
    1493          26 :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
    1494             : 
    1495          13 :     bool const ipv4(is_ipv4());
    1496          13 :     uint16_t const family(ipv4 ? AF_INET : AF_INET6);
    1497         207 :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
    1498             :     {
    1499         196 :         if(ifa->ifa_addr != nullptr
    1500         196 :         && ifa->ifa_addr->sa_family == family)
    1501             :         {
    1502          66 :             if(ipv4)
    1503             :             {
    1504             :                 // the interface address structure is a 'struct sockaddr_in'
    1505             :                 //
    1506         122 :                 if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
    1507          61 :                             f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
    1508             :                             sizeof(struct in_addr)) == 0)
    1509             :                 {
    1510           1 :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
    1511             :                 }
    1512             :             }
    1513             :             else
    1514             :             {
    1515             :                 // the interface address structure is a 'struct sockaddr_in6'
    1516             :                 //
    1517           5 :                 if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
    1518             :                             &f_address.sin6_addr,
    1519             :                             sizeof(f_address.sin6_addr)) == 0)
    1520             :                 {
    1521           1 :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
    1522             :                 }
    1523             :             }
    1524             :         }
    1525             :     }
    1526             : 
    1527          11 :     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
    1528             : }
    1529             : 
    1530             : 
    1531             : 
    1532             : 
    1533           6 : }
    1534             : // addr namespace
    1535             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12