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

Generated by: LCOV version 1.12