/* This file is part of GNUnet. Copyright (C) 2004, 2005, 2006, 2015 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file util/os_network.c * @brief function to determine available network interfaces * @author Nils Durner * @author Heikki Lindholm * @author Jake Dust * @author LRN * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #define LOG(kind,...) GNUNET_log_from (kind, "util-os-network", __VA_ARGS__) #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-os-network", syscall, filename) #if ! (HAVE_GETIFADDRS && HAVE_FREEIFADDRS) && !MINGW /** * Try to enumerate all network interfaces using 'ifconfig'. * * @param proc the callback function * @param proc_cls closure for @a proc * @return #GNUNET_OK if it worked */ static int try_ifconfig (GNUNET_OS_NetworkInterfaceProcessor proc, void *proc_cls) { int i; char line[1024]; char *replace; const char *start; char ifc[12]; char addrstr[128]; char bcstr[128]; char netmaskstr[128]; FILE *f; int have_ifc; struct sockaddr_in a4; struct sockaddr_in6 a6; struct in_addr v4; struct in6_addr v6; struct sockaddr_in bcaddr; struct sockaddr_in netmask; struct sockaddr_in6 netmask6; struct sockaddr *pass_bcaddr; struct sockaddr *pass_netmask; int prefixlen; if (system ("ifconfig -a > /dev/null 2> /dev/null")) if (0 == system ("/sbin/ifconfig -a > /dev/null 2> /dev/null")) f = popen ("/sbin/ifconfig -a 2> /dev/null", "r"); else f = NULL; else f = popen ("ifconfig -a 2> /dev/null", "r"); if (! f) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "popen", "ifconfig"); return GNUNET_SYSERR; } have_ifc = GNUNET_NO; ifc[11] = '\0'; while (NULL != fgets (line, sizeof (line), f)) { if (strlen (line) == 0) { have_ifc = GNUNET_NO; continue; } if (!isspace (line[0])) { have_ifc = (1 == SSCANF (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO; /* would end with ':' on OSX, fix it! */ if (ifc[strlen (ifc) - 1] == ':') ifc[strlen (ifc) - 1] = '\0'; continue; } if (!have_ifc) continue; /* strange input, hope for the best */ /* make parsing of ipv6 addresses easier */ for (replace = line; *replace != '\0'; replace++) { if (*replace == '/') *replace = ' '; } prefixlen = -1; start = line; while (('\0' != *start) && (isspace (*start))) start++; /* Zero-out stack allocated values */ memset (addrstr, 0, 128); memset (netmaskstr, 0, 128); memset (bcstr, 0, 128); prefixlen = 0; if ( /* Linux */ (3 == SSCANF (start, "inet addr:%127s Bcast:%127s Mask:%127s", addrstr, bcstr, netmaskstr)) || (2 == SSCANF (start, "inet addr:%127s Mask:%127s", addrstr, netmaskstr)) || (2 == SSCANF (start, "inet6 addr:%127s %d", addrstr, &prefixlen)) || /* Solaris, OS X */ (1 == SSCANF (start, "inet %127s", addrstr)) || (1 == SSCANF (start, "inet6 %127s", addrstr))) { /* IPv4 */ if (1 == inet_pton (AF_INET, addrstr, &v4)) { memset (&a4, 0, sizeof (a4)); a4.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN a4.sin_len = (u_char) sizeof (struct sockaddr_in); #endif a4.sin_addr = v4; pass_bcaddr = NULL; pass_netmask = NULL; if (1 == inet_pton (AF_INET, bcstr, &v4)) { memset (&bcaddr, 0, sizeof (bcaddr)); bcaddr.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN bcaddr.sin_len = (u_char) sizeof (struct sockaddr_in); #endif bcaddr.sin_addr = v4; pass_bcaddr = (struct sockaddr *) &bcaddr; } if (1 == inet_pton (AF_INET, netmaskstr, &v4)) { memset (&netmask, 0, sizeof (netmask)); netmask.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN netmask.sin_len = (u_char) sizeof (struct sockaddr_in); #endif netmask.sin_addr = v4; pass_netmask = (struct sockaddr *) &netmask; } if (GNUNET_OK != proc (proc_cls, ifc, 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE), (const struct sockaddr *) &a4, pass_bcaddr, pass_netmask, sizeof (a4))) break; continue; } /* IPv6 */ if (1 == inet_pton (AF_INET6, addrstr, &v6)) { memset (&a6, 0, sizeof (a6)); a6.sin6_family = AF_INET6; #if HAVE_SOCKADDR_IN_SIN_LEN a6.sin6_len = (u_char) sizeof (struct sockaddr_in6); #endif a6.sin6_addr = v6; pass_netmask = NULL; if (prefixlen != -1) { memset (v6.s6_addr, 0, sizeof (v6.s6_addr)); for (i = 0; i < prefixlen; i++) { v6.s6_addr[i >> 3] |= 1 << (i & 7); } memset (&netmask6, 0, sizeof (netmask6)); netmask6.sin6_family = AF_INET6; #if HAVE_SOCKADDR_IN_SIN_LEN netmask6.sin6_len = (u_char) sizeof (struct sockaddr_in6); #endif netmask6.sin6_addr = v6; pass_netmask = (struct sockaddr *) &netmask6; } if (GNUNET_OK != proc (proc_cls, ifc, 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE), (const struct sockaddr *) &a6, NULL, pass_netmask, sizeof (a6))) break; continue; } } } pclose (f); return GNUNET_OK; } /** * Try to enumerate all network interfaces using 'ip'. * * @param proc the callback function * @param proc_cls closure for @a proc * @return #GNUNET_OK if it worked */ static int try_ip (GNUNET_OS_NetworkInterfaceProcessor proc, void *proc_cls) { char line[1024]; char *replace; char ifname[64]; char afstr[6]; char addrstr[128]; FILE *f; struct sockaddr_in a4; struct sockaddr_in6 a6; struct in_addr v4; struct in6_addr v6; struct sockaddr_in netmask; struct sockaddr_in6 netmask6; unsigned int i; unsigned int prefixlen; f = popen ("ip -o add 2> /dev/null", "r"); if (! f) { LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "popen", "ip"); return GNUNET_SYSERR; } while (NULL != fgets (line, sizeof (line), f)) { /* make parsing easier */ for (replace = line; *replace != '\0'; replace++) { if (*replace == '/') *replace = ' '; } /* Zero-out stack allocated values */ memset (ifname, 0, 64); memset (afstr, 0, 6); memset (addrstr, 0, 128); if (4 != SSCANF (line, "%*u: %63s %5s %127s %6u", ifname, afstr, addrstr, &prefixlen)) continue; /* IPv4 */ if ( (0 == strcasecmp ("inet", afstr)) && (1 == inet_pton (AF_INET, addrstr, &v4)) ) { memset (&a4, 0, sizeof (a4)); a4.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN a4.sin_len = (u_char) sizeof (struct sockaddr_in); #endif a4.sin_addr = v4; memset (&v4.s_addr, 0, sizeof (v4.s_addr)); for (i = 0; i < prefixlen; i++) v4.s_addr |= 1 << (i & 7); memset (&netmask, 0, sizeof (netmask)); netmask.sin_family = AF_INET; #if HAVE_SOCKADDR_IN_SIN_LEN netmask.sin_len = (u_char) sizeof (struct sockaddr_in); #endif netmask.sin_addr = v4; if (GNUNET_OK != proc (proc_cls, ifname, (0 == strcmp (ifname, GNUNET_DEFAULT_INTERFACE)), (const struct sockaddr *) &a4, NULL, (const struct sockaddr *) &netmask, sizeof (a4))) break; } /* IPv6 */ if ( (0 == strcasecmp ("inet6", afstr)) && (1 == inet_pton (AF_INET6, addrstr, &v6)) ) { memset (&a6, 0, sizeof (a6)); a6.sin6_family = AF_INET6; #if HAVE_SOCKADDR_IN_SIN_LEN a6.sin6_len = (u_char) sizeof (struct sockaddr_in6); #endif a6.sin6_addr = v6; memset (v6.s6_addr, 0, sizeof (v6.s6_addr)); for (i = 0; i < prefixlen; i++) v6.s6_addr[i >> 3] |= 1 << (i & 7); memset (&netmask6, 0, sizeof (netmask6)); netmask6.sin6_family = AF_INET6; #if HAVE_SOCKADDR_IN_SIN_LEN netmask6.sin6_len = (u_char) sizeof (struct sockaddr_in6); #endif netmask6.sin6_addr = v6; if (GNUNET_OK != proc (proc_cls, ifname, (0 == strcmp (ifname, GNUNET_DEFAULT_INTERFACE)), (const struct sockaddr *) &a6, NULL, (const struct sockaddr *) &netmask6, sizeof (a6))) break; } } pclose (f); return GNUNET_OK; } #endif /** * @brief Enumerate all network interfaces * * @param proc the callback function * @param proc_cls closure for @a proc */ void GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc, void *proc_cls) { #ifdef MINGW int r; int i; struct EnumNICs3_results *results = NULL; int results_count; r = EnumNICs3 (&results, &results_count); if (r != GNUNET_OK) return; for (i = 0; i < results_count; i++) { if (GNUNET_OK != proc (proc_cls, results[i].pretty_name, results[i].is_default, (const struct sockaddr *) &results[i].address, results[i]. flags & ENUMNICS3_BCAST_OK ? (const struct sockaddr *) &results[i].broadcast : NULL, results[i].flags & ENUMNICS3_MASK_OK ? (const struct sockaddr *) &results[i].mask : NULL, results[i].addr_size)) break; } EnumNICs3_free (results); return; #elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS struct ifaddrs *ifa_first; struct ifaddrs *ifa_ptr; socklen_t alen; if (getifaddrs (&ifa_first) == 0) { for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next) { if (ifa_ptr->ifa_name != NULL && ifa_ptr->ifa_addr != NULL && (ifa_ptr->ifa_flags & IFF_UP) != 0) { if ((ifa_ptr->ifa_addr->sa_family != AF_INET) && (ifa_ptr->ifa_addr->sa_family != AF_INET6)) continue; if (ifa_ptr->ifa_addr->sa_family == AF_INET) alen = sizeof (struct sockaddr_in); else alen = sizeof (struct sockaddr_in6); if (GNUNET_OK != proc (proc_cls, ifa_ptr->ifa_name, 0 == strcmp (ifa_ptr->ifa_name, GNUNET_DEFAULT_INTERFACE), ifa_ptr->ifa_addr, ifa_ptr->ifa_broadaddr, ifa_ptr->ifa_netmask, alen)) break; } } freeifaddrs (ifa_first); } #else if (GNUNET_OK == try_ip (proc, proc_cls)) return; if (GNUNET_OK == try_ifconfig (proc, proc_cls)) return; LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "Failed to enumerate network interfaces\n"); #endif } /* end of os_network.c */