LCOV - code coverage report
Current view: top level - src - iface.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 63 63 100.0 %
Date: 2018-06-08 23:44:40 Functions: 12 12 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/iface.h"
      36             : 
      37             : // C++ library
      38             : //
      39             : #include <iostream>
      40             : 
      41             : // C library
      42             : //
      43             : #include <ifaddrs.h>
      44             : #include <net/if.h>
      45             : 
      46             : 
      47             : 
      48             : namespace addr
      49             : {
      50             : 
      51             : 
      52             : /** \brief Details used by the addr class implementation.
      53             :  *
      54             :  * We have a function to check whether an address is part of
      55             :  * the interfaces of your computer. This check requires the
      56             :  * use of a `struct ifaddrs` and as such it requires to
      57             :  * delete that structure. We define a deleter for that
      58             :  * strucure here.
      59             :  */
      60             : namespace
      61             : {
      62             : 
      63             : /** \brief Delete an ifaddrs structure.
      64             :  *
      65             :  * This deleter is used to make sure all the ifaddrs get released when
      66             :  * an exception occurs or the function using such exists.
      67             :  *
      68             :  * \param[in] ia  The ifaddrs structure to free.
      69             :  */
      70          14 : void ifaddrs_deleter(struct ifaddrs * ia)
      71             : {
      72          14 :     freeifaddrs(ia);
      73          14 : }
      74             : 
      75             : 
      76             : }
      77             : // no name namespace
      78             : 
      79             : 
      80             : 
      81             : /** \brief Return a list of local addresses on this machine.
      82             :  *
      83             :  * Peruse the list of available interfaces, and return any detected
      84             :  * ip addresses in a vector.
      85             :  *
      86             :  * These addresses include:
      87             :  *
      88             :  * \li A mask whenever available (very likely if the interface is up).
      89             :  * \li A name you can retrieve with get_iface_name()
      90             :  * \li A set of flags defining the current status of the network interface
      91             :  *     (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
      92             :  *
      93             :  * \return A vector of all the local interface IP addresses.
      94             :  */
      95          14 : iface::vector_t iface::get_local_addresses()
      96             : {
      97             :     // get the list of interface addresses
      98             :     //
      99          14 :     struct ifaddrs * ifa_start(nullptr);
     100          14 :     if(getifaddrs(&ifa_start) != 0)
     101             :     {
     102             :         // TODO: Should this throw, or just return an empty list quietly?
     103             :         //
     104             :         return iface::vector_t(); // LCOV_EXCL_LINE
     105             :     }
     106             : 
     107          28 :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
     108             : 
     109             :     uint8_t mask[16];
     110          28 :     iface::vector_t iface_list;
     111         238 :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
     112             :     {
     113             :         // the documentation says there may be no address at all
     114             :         // skip such entries at the moment
     115             :         //
     116         224 :         if(ifa->ifa_addr == nullptr)
     117             :         {
     118          84 :             continue;
     119             :         }
     120             : 
     121             :         // initialize an interface
     122         364 :         iface the_interface;
     123             : 
     124             :         // copy the name and flags as is
     125             :         //
     126             :         // TBD: can the ifa_name even be a null pointer?
     127             :         //
     128         224 :         the_interface.f_name = ifa->ifa_name;
     129         224 :         the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
     130             : 
     131             :         // get the family to know how to treat the address
     132             :         //
     133             :         // when an interface has an IPv4 and an IPv6, there are two entries in
     134             :         // the list, both with the same name
     135             :         //
     136         224 :         uint16_t const family(ifa->ifa_addr->sa_family);
     137             : 
     138         224 :         switch(family)
     139             :         {
     140             :         case AF_INET:
     141          84 :             the_interface.f_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)));
     142             : 
     143          84 :             if((ifa->ifa_flags & IFF_BROADCAST) != 0
     144          70 :             && ifa->ifa_broadaddr != nullptr)
     145             :             {
     146          70 :                 the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_broadaddr)));
     147             :             }
     148          84 :             if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
     149             :             && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
     150             :             {
     151             :                 the_interface.f_destination_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
     152             :             }
     153             : 
     154             :             // if present, add the mask as well
     155             :             //
     156          84 :             if(ifa->ifa_netmask != nullptr)
     157             :             {
     158             :                 // for the IPv4 mask, we have to break it down in such a
     159             :                 // way as to make it IPv6 compatible
     160             :                 //
     161          84 :                 memset(mask, 255, 12);
     162          84 :                 mask[12] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
     163          84 :                 mask[13] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
     164          84 :                 mask[14] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >>  8;
     165          84 :                 mask[15] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >>  0;
     166          84 :                 the_interface.f_address.set_mask(mask);
     167             :             }
     168          84 :             break;
     169             : 
     170             :         case AF_INET6:
     171          56 :             the_interface.f_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)));
     172             : 
     173          56 :             if((ifa->ifa_flags & IFF_BROADCAST) != 0
     174          42 :             && ifa->ifa_broadaddr != nullptr)
     175             :             {
     176             :                 the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_broadaddr)));  // LCOV_EXCL_LINE
     177             :             }
     178          56 :             if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
     179             :             && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
     180             :             {
     181             :                 the_interface.f_destination_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
     182             :             }
     183             : 
     184             :             // if present, add the mask as well
     185             :             //
     186          56 :             if(ifa->ifa_netmask != nullptr)
     187             :             {
     188          56 :                 the_interface.f_address.set_mask(reinterpret_cast<uint8_t *>(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask)->sin6_addr));
     189             :             }
     190          56 :             break;
     191             : 
     192             :         default:
     193             :             // TODO: can we just ignore unexpected address families?
     194             :             //throw addr_invalid_structure_exception( "Unknown address family!" );
     195          84 :             continue;
     196             : 
     197             :         }
     198             : 
     199         140 :         iface_list.push_back(the_interface);
     200             :     }
     201             : 
     202          14 :     return iface_list;
     203             : }
     204             : 
     205             : 
     206             : /** \brief Get the interface name.
     207             :  *
     208             :  * This function returns the name of the interface such as 'eth0' or 'p4p1'.
     209             :  *
     210             :  * The name is used in a few places such as the ioctl() function with the
     211             :  * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
     212             :  * (i.e. to let users select which interface to connect to.)
     213             :  *
     214             :  * \return The interface name.
     215             :  */
     216          10 : std::string iface::get_name() const
     217             : {
     218          10 :     return f_name;
     219             : }
     220             : 
     221             : 
     222             : /** \brief Get the interface setup flags.
     223             :  *
     224             :  * This function returns a set of flags defined on that interface. The flags
     225             :  * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
     226             :  * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
     227             :  *
     228             :  * One flag of interest is the IFF_UP flag. This means the interface is
     229             :  * active (even if not actually in use.)
     230             :  *
     231             :  * \return The interface flags.
     232             :  */
     233          30 : unsigned int iface::get_flags() const
     234             : {   
     235          30 :     return f_flags;
     236             : }
     237             : 
     238             : 
     239             : /** \brief Get this interface address.
     240             :  *
     241             :  * This function returns the address of the interface. This address is very
     242             :  * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
     243             :  *
     244             :  * The address may be an IPv4 or an IPv6 address.
     245             :  *
     246             :  * \return The address of the interface.
     247             :  */
     248         128 : addr const & iface::get_address() const
     249             : {
     250         128 :     return f_address;
     251             : }
     252             : 
     253             : 
     254             : /** \brief Get the broadcast address.
     255             :  *
     256             :  * This function returns a constant reference to the broadcast address
     257             :  * of this interface. The address is always available in this class. It
     258             :  * will be set to the ANY address if it was not defined. Note, however,
     259             :  * that even though the ANY address is not a valid broadcast address,
     260             :  * you should call the has_broadcast_address() function to know whether
     261             :  * this address is indeed defined.
     262             :  *
     263             :  * \return The broadcast address of this interface.
     264             :  */
     265          10 : addr const & iface::get_broadcast_address() const
     266             : {
     267          10 :     return f_broadcast_address;
     268             : }
     269             : 
     270             : 
     271             : /** \brief Get the destination address.
     272             :  *
     273             :  * This function returns a constant reference to the destination address
     274             :  * of this interface. The address is always available in this class. It
     275             :  * will be set to the ANY address if it was not defined. Note, however,
     276             :  * that the ANY address is a valid destination address (i.e. default
     277             :  * route).
     278             :  *
     279             :  * To know whether the destination address is defined in that interface,
     280             :  * make sure to call the has_destination_address() function first.
     281             :  *
     282             :  * \return The destination address of this interface.
     283             :  */
     284          10 : addr const & iface::get_destination_address() const
     285             : {
     286          10 :     return f_destination_address;
     287             : }
     288             : 
     289             : 
     290             : /** \brief Check whether a broadcast address.
     291             :  *
     292             :  * The broadcast address is not present on all interfaces. When it is, this
     293             :  * function returns true.
     294             :  *
     295             :  * Note that you can always call the get_broadcast_address(), but if
     296             :  * undefined it will appear as a default address (NETWORK_TYPE_ANY)
     297             :  * which you can't distinguish from a valid address although a
     298             :  * the NETWORK_TYPE_ANY is not a valid address for a broacast
     299             :  * address.
     300             :  *
     301             :  * \note
     302             :  * When a broadcast address is defined on an interface, then there can't
     303             :  * be a destination address.
     304             :  *
     305             :  * \return true if a broadcast address is defined.
     306             :  */
     307          20 : bool iface::has_broadcast_address() const
     308             : {
     309          20 :     return (f_flags & IFF_BROADCAST) != 0;
     310             : }
     311             : 
     312             : 
     313             : /** \brief Check whether this interface defines a destination address.
     314             :  *
     315             :  * This function returns true if this interface defined a destination
     316             :  * address. Either way you can call the get_destination_address()
     317             :  * function, however, the address will be of type NETWORK_TYPE_ANY
     318             :  * which does not tell you whether it was defined or not.
     319             :  *
     320             :  * Ethernet and the local interfaces all define a destination address.
     321             :  *
     322             :  * \note
     323             :  * The destination address is not assigned any specific mask (all
     324             :  * are ff or 255).
     325             :  *
     326             :  * \note
     327             :  * When there is a destination address defined on an interface, then
     328             :  * there can't be a broadcast address.
     329             :  *
     330             :  * \return true when that interface defined a destination address.
     331             :  */
     332          20 : bool iface::has_destination_address() const
     333             : {
     334          20 :     return (f_flags & IFF_POINTOPOINT) != 0;
     335             : }
     336             : 
     337             : 
     338             : /** \brief Search for the interface corresponding to this address.
     339             :  *
     340             :  * Peruse the list of available interfaces and return the one that matches
     341             :  * this address if any, otherwise return a null pointer.
     342             :  *
     343             :  * Say you create an addr object with the IP address "127.0.0.1" and then
     344             :  * call this function. You will get a pointer to the "lo" interface and
     345             :  * can check the validity of the flags (i.e. is the interface UP, can it
     346             :  * BROADCAST or MULTICAST your UDP packets, etc.)
     347             :  *
     348             :  * If the address is a remote address, then this function returns a null
     349             :  * pointer.
     350             :  *
     351             :  * \note
     352             :  * This function replaces the addr::is_computer_interface_address() function.
     353             :  * If this function returns a non-null pointer when allow_default_destination
     354             :  * set to false, then you've got the same result plus you have access to all
     355             :  * the available information from that interface.
     356             :  *
     357             :  * \param[in] allow_default_destination  If true and this address doesn't
     358             :  *            match any of the interfaces, use the one interface with its
     359             :  *            destination set to 0.0.0.0 or equivalent.
     360             :  *
     361             :  * \return A pointer to an interface IP address.
     362             :  */
     363          13 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
     364             : {
     365          26 :     iface::vector_t interfaces(iface::get_local_addresses());
     366             : 
     367          26 :     iface::pointer_t default_iface;
     368         129 :     for(auto i : interfaces)
     369             :     {
     370         118 :         if(i.get_address().match(a))
     371             :         {
     372           2 :             return iface::pointer_t(new iface(i));
     373             :         }
     374             :         // if there is a default, keep a copy in case we do not find a
     375             :         // local address while looking (and only if the user requested
     376             :         // such, which is the default)
     377             :         //
     378         116 :         if(allow_default_destination
     379         116 :         && i.get_destination_address().is_default())
     380             :         {
     381             :             default_iface.reset(new iface(i));  // LCOV_EXCL_LINE
     382             :         }
     383             :     }
     384             : 
     385          11 :     return default_iface;
     386             : }
     387             : 
     388             : 
     389             : #if 0
     390             : /** \brief Check whether this address represents this computer.
     391             :  *
     392             :  * This function reads the addresses as given to us by the getifaddrs()
     393             :  * function. This is a system function that returns a complete list of
     394             :  * all the addresses this computer is managing / represents. In other
     395             :  * words, a list of address that other computers can use to connect
     396             :  * to this computer (assuming proper firewall, of course.)
     397             :  *
     398             :  * \warning
     399             :  * The list of addresses from getifaddrs() is not being cached. So you
     400             :  * probably do not want to call this function in a loop. That being
     401             :  * said, I still would imagine that retrieving that list is fast.
     402             :  *
     403             :  * \todo
     404             :  * We need to apply the mask to make this work properly. This is why
     405             :  * the current implementation fails big time (used by snapcommunicator.cpp).
     406             :  *
     407             :  * \return a computer_interface_address_t enumeration: error, true, or
     408             :  *         false at this time; on error errno should be set to represent
     409             :  *         what the error was.
     410             :  */
     411             : 
     412             : // replaced by with a pointer_t == nullptr if there was no match,
     413             : // although make sure to set allow_default_destination to false
     414             : //
     415             : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
     416             : 
     417             : bool is_computer_interface_address(addr const & a)
     418             : {
     419             :     iface::vector_t interfaces(iface::get_local_addresses());
     420             : 
     421             :     for(auto i : interfaces)
     422             :     {
     423             :     }
     424             : 
     425             : 
     426             :     // TBD: maybe we could cache the ifaddrs for a small amount of time
     427             :     //      (i.e. 1 minute) so additional calls within that time
     428             :     //      can go even faster?
     429             :     //
     430             : 
     431             :     // get the list of interface addresses
     432             :     //
     433             :     struct ifaddrs * ifa_start(nullptr);
     434             :     if(getifaddrs(&ifa_start) != 0)
     435             :     {
     436             :         return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
     437             :     }
     438             :     std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
     439             : 
     440             :     bool const ipv4(a.is_ipv4());
     441             :     uint16_t const family(ipv4 ? AF_INET : AF_INET6);
     442             :     for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
     443             :     {
     444             :         if(ifa->ifa_addr != nullptr
     445             :         && ifa->ifa_addr->sa_family == family)
     446             :         {
     447             :             if(ipv4)
     448             :             {
     449             :                 // the interface address structure is a 'struct sockaddr_in'
     450             :                 //
     451             :                 if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
     452             :                             f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
     453             :                             sizeof(struct in_addr)) == 0)
     454             :                 {
     455             :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
     456             :                 }
     457             :             }
     458             :             else
     459             :             {
     460             :                 // the interface address structure is a 'struct sockaddr_in6'
     461             :                 //
     462             :                 if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
     463             :                             &f_address.sin6_addr,
     464             :                             sizeof(f_address.sin6_addr)) == 0)
     465             :                 {
     466             :                     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
     467             :                 }
     468             :             }
     469             :         }
     470             :     }
     471             : 
     472             :     return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
     473             : }
     474             : #endif
     475             : 
     476             : 
     477           6 : }
     478             : // addr namespace
     479             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12