diff options
Diffstat (limited to '3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c')
-rw-r--r-- | 3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c new file mode 100644 index 0000000..9db1d86 --- /dev/null +++ b/3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c @@ -0,0 +1,840 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable getifaddrs implementation. + * + * Copyright (c) 2006-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <errno.h> +#ifdef CONFIG_HAVE_GETIFADDRS +# include <sys/types.h> +# include <ifaddrs.h> +#endif +#if defined( sun ) +# include <sys/sockio.h> +#endif +#if defined( _WIN32 ) +# include <ws2tcpip.h> +# include <iphlpapi.h> +#endif +#include <impl/i18n.h> +#include <impl/framework.h> + + +//#define GETIFADDRS_DEBUG + +/* locals */ +struct _pgm_ifaddrs_t +{ + struct pgm_ifaddrs_t _ifa; + char _name[IF_NAMESIZE]; + struct sockaddr_storage _addr; + struct sockaddr_storage _netmask; +}; + +/* Number of attempts to try enumerating the interface list */ +#define MAX_TRIES 3 +#define DEFAULT_BUFFER_SIZE 4096 + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +#ifdef SIOCGLIFCONF +static +bool +_pgm_getlifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + struct lifnum lifn; +again: + lifn.lifn_family = AF_INET; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + unsigned if_count = lifn.lifn_count; + pgm_debug ("ioctl(AF_INET, SIOCGLIFNUM) = %d", lifn.lifn_count); + +/* nb: Sun and Apple code likes to pad the interface count here in case interfaces + * are coming up between calls, + */ + lifn.lifn_count += 4; + +/* process all interfaces with family-agnostic ioctl, unfortunately it still + * must be called on each family socket despite what if_tcp(7P) says. + */ + struct lifconf lifc, lifc6; + lifc.lifc_family = AF_INET; + lifc.lifc_flags = 0; + lifc.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc.lifc_buf = alloca (lifc.lifc_len); + if (-1 == ioctl (sock, SIOCGLIFCONF, &lifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET, SIOCGLIFCONF) = %d (%d)", lifc.lifc_len, (int)(lifc.lifc_len / sizeof(struct lifreq))); + +/* repeat everything for IPv6 */ + lifn.lifn_family = AF_INET6; + lifn.lifn_flags = 0; + if (-1 == ioctl (sock6, SIOCGLIFNUM, &lifn)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFNUM failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += lifn.lifn_count; + pgm_debug ("ioctl(AF_INET6, SIOCGLIFNUM) = %d", lifn.lifn_count); + + lifn.lifn_count += 4; + + lifc6.lifc_family = AF_INET6; + lifc6.lifc_flags = 0; + lifc6.lifc_len = lifn.lifn_count * sizeof(struct lifreq); + lifc6.lifc_buf = alloca (lifc6.lifc_len); + if (-1 == ioctl (sock6, SIOCGLIFCONF, &lifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGLIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + pgm_debug ("ioctl(AF_INET6, SIOCGLIFCONF) = %d (%d)", lifc6.lifc_len, (int)(lifc6.lifc_len / sizeof(struct lifreq))); + + unsigned nlifr = (lifc.lifc_len + lifc6.lifc_len) / sizeof(struct lifreq); + if (nlifr > if_count) + goto again; + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = calloc (nlifr, sizeof (struct _pgm_ifaddrs_t)); + pgm_assert (NULL != ifa); + + struct _pgm_ifaddrs_t* ift = ifa; + struct lifreq* lifr = lifc.lifc_req; + struct lifreq* lifr_end = (struct lifreq *)&lifc.lifc_buf[lifc.lifc_len]; + + pgm_assert (IF_NAMESIZE >= LIFNAMSIZ); + + while (lifr < lifr_end) + { +/* name */ + pgm_debug ("AF_INET/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, LIFNAMSIZ); + ift->_ifa.ifa_name[LIFNAMSIZ - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGLIFNETMASK, lifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + if (lifr < lifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +/* repeat everything for IPv6 */ + lifr = lifc6.lifc_req; + lifr_end = (struct lifreq *)&lifc6.lifc_buf[lifc6.lifc_len]; + + while (lifr < lifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name: %s", lifr->lifr_name ? lifr->lifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, lifr->lifr_name, sizeof(lifr->lifr_name)); + ift->_ifa.ifa_name[sizeof(lifr->lifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGLIFFLAGS, lifr)) { + ift->_ifa.ifa_flags = lifr->lifr_flags; + } else { + pgm_warn (_("SIOCGLIFFLAGS failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock6, SIOCGLIFADDR, lifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); + } else { + pgm_warn (_("SIOCGLIFADDR failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + +/* netmask */ + if (ioctl (sock6, SIOCGLIFNETMASK, lifr) != -1) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_netmask, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &lifr->lifr_addr, pgm_sockaddr_len((struct sockaddr*)&lifr->lifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGLIFNETMASK failed on interface %s%s%s"), + lifr->lifr_name ? "\"" : "", lifr->lifr_name ? lifr->lifr_name : "(null)", lifr->lifr_name ? "\"" : ""); + } + + ++lifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCGLIFCONF */ + +#ifdef SIOCGIFCONF +static +bool +_pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == sock) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv4 datagram socket: %s"), + strerror (errno)); + return FALSE; + } + +/* process IPv4 interfaces */ + char buf[ DEFAULT_BUFFER_SIZE ]; + struct ifconf ifc; + ifc.ifc_buf = buf; + ifc.ifc_len = sizeof(buf); + if (-1 == ioctl (sock, SIOCGIFCONF, &ifc)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv4 socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + int if_count = ifc.ifc_len / sizeof(struct ifreq); + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* process IPv6 interfaces */ + const int sock6 = socket (AF_INET6, SOCK_DGRAM, 0); + if (-1 == sock6) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Opening IPv6 datagram socket: %s"), + strerror (errno)); + close (sock); + return FALSE; + } + + char buf6[1024]; + struct ifconf ifc6; + ifc6.ifc_buf = buf6; + ifc6.ifc_len = sizeof(buf6); + if (-1 == ioctl (sock6, SIOCGIFCONF, &ifc6)) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("SIOCGIFCONF failed on IPv6 socket: %s"), + strerror (errno)); + close (sock); + close (sock6); + return FALSE; + } + if_count += ifc6.ifc_len / sizeof(struct ifreq); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + +/* alloc a contiguous block for entire list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, if_count); + struct _pgm_ifaddrs_t* ift = ifa; + struct ifreq *ifr = ifc.ifc_req; + struct ifreq *ifr_end = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + pgm_assert (IF_NAMESIZE >= sizeof(ifr->ifr_name)); + + while (ifr < ifr_end) + { +/* name */ + pgm_debug ("AF_INET/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address */ + if (-1 != ioctl (sock, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + if (ifr < ifr_end) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + +# ifdef CONFIG_HAVE_IPV6_SIOCGIFADDR +/* repeat everything for IPv6 */ + ifr = ifc6.ifc_req; + ifr_end = (struct ifreq *)&ifc6.ifc_buf[ifc6.ifc_len]; + + while (ifr < ifr_end) + { + if (ift != ifa) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + +/* name */ + pgm_debug ("AF_INET6/name:%s", ifr->ifr_name ? ifr->ifr_name : "(null)"); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + ift->_ifa.ifa_name[sizeof(ifr->ifr_name) - 1] = 0; + +/* flags */ + if (-1 != ioctl (sock6, SIOCGIFFLAGS, ifr)) { + ift->_ifa.ifa_flags = ifr->ifr_flags; + } else { + pgm_warn (_("SIOCGIFFLAGS failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* address, note this does not work on Linux as struct ifreq is too small for an IPv6 address */ + if (-1 != ioctl (sock6, SIOCGIFADDR, ifr)) { + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); + } else { + pgm_warn (_("SIOCGIFADDR failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + +/* netmask */ + if (-1 != ioctl (sock6, SIOCGIFNETMASK, ifr)) { + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; +# ifdef CONFIG_HAVE_IFR_NETMASK + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_netmask, pgm_sockaddr_len(&ifr->ifr_netmask)); +# else + memcpy (ift->_ifa.ifa_netmask, &ifr->ifr_addr, pgm_sockaddr_len(&ifr->ifr_addr)); +# endif + } else { + pgm_warn (_("SIOCGIFNETMASK failed on interface %s%s%s"), + ifr->ifr_name ? "\"" : "", ifr->ifr_name ? ifr->ifr_name : "(null)", ifr->ifr_name ? "\"" : ""); + } + + ++ifr; + } + + if (-1 == close (sock6)) + pgm_warn (_("Closing IPv6 socket failed: %s"), strerror(errno)); +# endif /* CONFIG_HAVE_IPV6_SIOCGIFADDR */ + + if (-1 == close (sock)) + pgm_warn (_("Closing IPv4 socket failed: %s"), strerror(errno)); + + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* SIOCLIFCONF */ + +#if defined(_WIN32) +static inline +void* +_pgm_heap_alloc ( + const size_t n_bytes + ) +{ +# ifdef CONFIG_USE_HEAPALLOC +/* Does not appear very safe with re-entrant calls on XP */ + return HeapAlloc (GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, n_bytes); +# else + return pgm_malloc (n_bytes); +# endif +} + +static inline +void +_pgm_heap_free ( + void* mem + ) +{ +# ifdef CONFIG_USE_HEAPALLOC + HeapFree (GetProcessHeap(), 0, mem); +# else + pgm_free (mem); +# endif +} + +/* NB: IP_ADAPTER_INFO size varies size due to sizeof (time_t), the API assumes + * 4-byte datatype whilst compiler uses an 8-byte datatype. Size can be forced + * with -D_USE_32BIT_TIME_T with side effects to everything else. + */ + +static +bool +_pgm_getadaptersinfo ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwRet; + ULONG ulOutBufLen = DEFAULT_BUFFER_SIZE; + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_INFO buffer length %lu bytes.", ulOutBufLen); + pAdapterInfo = (IP_ADAPTER_INFO*)_pgm_heap_alloc (ulOutBufLen); + dwRet = GetAdaptersInfo (pAdapterInfo, &ulOutBufLen); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterInfo); + pAdapterInfo = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: /* NO_ERROR */ + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersInfo repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersInfo failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterInfo) + _pgm_heap_free (pAdapterInfo); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + ++n; + } + } + + pgm_debug ("GetAdaptersInfo() discovered %d interfaces.", n); + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (pAdapter = pAdapterInfo; + pAdapter; + pAdapter = pAdapter->Next) + { + for (IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + pIPAddr; + pIPAddr = pIPAddr->Next) + { +/* skip null adapters */ + if (strlen (pIPAddr->IpAddress.String) == 0) + continue; + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpAddress.String, ift->_ifa.ifa_addr)); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu", + pAdapter->AdapterName, pAdapter->Index); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, pAdapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags: assume up, broadcast and multicast */ + ift->_ifa.ifa_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; + if (pAdapter->Type == MIB_IF_TYPE_LOOPBACK) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + pgm_assert (pgm_sockaddr_pton (pIPAddr->IpMask.String, ift->_ifa.ifa_netmask)); + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterInfo) + free (pAdapterInfo); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} + +static +bool +_pgm_getadaptersaddresses ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + DWORD dwSize = DEFAULT_BUFFER_SIZE, dwRet; + IP_ADAPTER_ADDRESSES *pAdapterAddresses = NULL, *adapter; + +/* loop to handle interfaces coming online causing a buffer overflow + * between first call to list buffer length and second call to enumerate. + */ + for (unsigned i = MAX_TRIES; i; i--) + { + pgm_debug ("IP_ADAPTER_ADDRESSES buffer length %lu bytes.", dwSize); + pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_pgm_heap_alloc (dwSize); + dwRet = GetAdaptersAddresses (AF_UNSPEC, + GAA_FLAG_INCLUDE_PREFIX | + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_MULTICAST, + NULL, + pAdapterAddresses, + &dwSize); + if (ERROR_BUFFER_OVERFLOW == dwRet) { + _pgm_heap_free (pAdapterAddresses); + pAdapterAddresses = NULL; + } else { + break; + } + } + + switch (dwRet) { + case ERROR_SUCCESS: + break; + case ERROR_BUFFER_OVERFLOW: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NOBUFS, + _("GetAdaptersAddresses repeatedly failed with ERROR_BUFFER_OVERFLOW.")); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + default: + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_win_errno (dwRet), + _("GetAdaptersAddresses failed: %s"), + pgm_adapter_strerror (dwRet)); + if (pAdapterAddresses) + free (pAdapterAddresses); + return FALSE; + } + +/* count valid adapters */ + int n = 0, k = 0; + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + + ++n; + } + } + +/* contiguous block for adapter list */ + struct _pgm_ifaddrs_t* ifa = pgm_new0 (struct _pgm_ifaddrs_t, n); + struct _pgm_ifaddrs_t* ift = ifa; + +/* now populate list */ + for (adapter = pAdapterAddresses; + adapter; + adapter = adapter->Next) + { + int unicastIndex = 0; + for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; + unicast; + unicast = unicast->Next, ++unicastIndex) + { +/* ensure IP adapter */ + if (AF_INET != unicast->Address.lpSockaddr->sa_family && + AF_INET6 != unicast->Address.lpSockaddr->sa_family) + { + continue; + } + +/* address */ + ift->_ifa.ifa_addr = (void*)&ift->_addr; + memcpy (ift->_ifa.ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength); + +/* name */ + pgm_debug ("name:%s IPv4 index:%lu IPv6 index:%lu", + adapter->AdapterName, adapter->IfIndex, adapter->Ipv6IfIndex); + ift->_ifa.ifa_name = ift->_name; + strncpy (ift->_ifa.ifa_name, adapter->AdapterName, IF_NAMESIZE); + ift->_ifa.ifa_name[IF_NAMESIZE - 1] = 0; + +/* flags */ + ift->_ifa.ifa_flags = 0; + if (IfOperStatusUp == adapter->OperStatus) + ift->_ifa.ifa_flags |= IFF_UP; + if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) + ift->_ifa.ifa_flags |= IFF_LOOPBACK; + if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST)) + ift->_ifa.ifa_flags |= IFF_MULTICAST; + +/* netmask */ + ift->_ifa.ifa_netmask = (void*)&ift->_netmask; + +/* pre-Vista must hunt for matching prefix in linked list, otherwise use OnLinkPrefixLength */ + int prefixIndex = 0; + ULONG prefixLength = 0; + for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; + prefix; + prefix = prefix->Next, ++prefixIndex) + { + if (prefixIndex == unicastIndex) { + prefixLength = prefix->PrefixLength; + break; + } + } + +/* map prefix to netmask */ + ift->_ifa.ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family; + switch (unicast->Address.lpSockaddr->sa_family) { + case AF_INET: + if (0 == prefixLength) { + pgm_warn (_("IPv4 adapter %s prefix length is 0, overriding to 32."), adapter->AdapterName); + prefixLength = 32; + } + ((struct sockaddr_in*)ift->_ifa.ifa_netmask)->sin_addr.s_addr = htonl( 0xffffffffU << ( 32 - prefixLength ) ); + break; + + case AF_INET6: + if (0 == prefixLength) { + pgm_warn (_("IPv6 adapter %s prefix length is 0, overriding to 128."), adapter->AdapterName); + prefixLength = 128; + } + for (ULONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) + { + ((struct sockaddr_in6*)ift->_ifa.ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU ); + } + break; + } + +/* next */ + if (k++ < (n - 1)) { + ift->_ifa.ifa_next = (struct pgm_ifaddrs_t*)(ift + 1); + ift = (struct _pgm_ifaddrs_t*)(ift->_ifa.ifa_next); + } + } + } + + if (pAdapterAddresses) + free (pAdapterAddresses); + *ifap = (struct pgm_ifaddrs_t*)ifa; + return TRUE; +} +#endif /* _WIN32 */ + +/* returns TRUE on success setting ifap to a linked list of system interfaces, + * returns FALSE on failure and sets error appropriately. + */ + +bool +pgm_getifaddrs ( + struct pgm_ifaddrs_t** restrict ifap, + pgm_error_t** restrict error + ) +{ + pgm_assert (NULL != ifap); + + pgm_debug ("pgm_getifaddrs (ifap:%p error:%p)", + (void*)ifap, (void*)error); + +#ifdef CONFIG_HAVE_GETIFADDRS + const int e = getifaddrs ((struct ifaddrs**)ifap); + if (-1 == e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("getifaddrs failed: %s"), + strerror (errno)); + return FALSE; + } + return TRUE; +#elif defined(CONFIG_TARGET_WINE) + return _pgm_getadaptersinfo (ifap, error); +#elif defined(_WIN32) + return _pgm_getadaptersaddresses (ifap, error); +#elif defined(SIOCGLIFCONF) + return _pgm_getlifaddrs (ifap, error); +#elif defined(SIOCGIFCONF) + return _pgm_getifaddrs (ifap, error); +#else +# error "Unsupported interface enumeration on this platform." +#endif /* !CONFIG_HAVE_GETIFADDRS */ +} + +void +pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + pgm_return_if_fail (NULL != ifa); + +#ifdef CONFIG_HAVE_GETIFADDRS + freeifaddrs ((struct ifaddrs*)ifa); +#else + pgm_free (ifa); +#endif +} + +/* eof */ |