diff options
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c')
-rw-r--r-- | 3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c new file mode 100644 index 0000000..0765c9f --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c @@ -0,0 +1,196 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * portable function to return the nodes IP address. + * + * 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> +#ifndef _WIN32 +# include <netdb.h> +#endif +#include <impl/i18n.h> +#include <impl/framework.h> + + +//#define GETNODEADDR_DEBUG + + +/* globals */ + +static const char* pgm_family_string (const sa_family_t); + + +/* return node primary address on multi-address family interfaces. + * + * returns TRUE on success, returns FALSE on failure. + */ + +bool +pgm_if_getnodeaddr ( + const sa_family_t family, /* requested address family, AF_INET, AF_INET6, or AF_UNSPEC */ + struct sockaddr* restrict addr, + const socklen_t cnt, /* size of address pointed to by addr */ + pgm_error_t** restrict error + ) +{ + pgm_return_val_if_fail (AF_INET == family || AF_INET6 == family || AF_UNSPEC == family, FALSE); + pgm_return_val_if_fail (NULL != addr, FALSE); + if (AF_INET == family || AF_UNSPEC == family) + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in), FALSE); + else + pgm_return_val_if_fail (cnt >= sizeof(struct sockaddr_in6), FALSE); + + pgm_debug ("pgm_if_getnodeaddr (family:%s addr:%p cnt:%d error:%p)", + pgm_family_string (family), (const void*)addr, cnt, (const void*)error); + + char hostname[NI_MAXHOST + 1]; + struct hostent* he; + + if (0 != gethostname (hostname, sizeof(hostname))) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_errno (errno), + _("Resolving hostname: %s"), +#ifndef _WIN32 + strerror (errno) +#else + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + addr->sa_family = family; + struct addrinfo hints = { + .ai_family = family, + .ai_socktype = SOCK_STREAM, /* not really */ + .ai_protocol = IPPROTO_TCP, /* not really */ + .ai_flags = AI_ADDRCONFIG, + }, *res; + + int e = getaddrinfo (hostname, NULL, &hints, &res); + if (0 == e) { + const socklen_t addrlen = res->ai_addrlen; + memcpy (addr, res->ai_addr, addrlen); + freeaddrinfo (res); + return TRUE; + } else if (EAI_NONAME != e) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_eai_errno (e, errno), + _("Resolving hostname address: %s"), + gai_strerror (e)); + return FALSE; + } else if (AF_UNSPEC == family) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONAME, + _("Resolving hostname address family.")); + return FALSE; + } + +/* Common case a dual stack host has incorrect IPv6 configuration, i.e. + * hostname is only IPv4 and despite one or more IPv6 addresses. Workaround + * for this case is to resolve the IPv4 hostname, find the matching interface + * and from that interface find an active IPv6 address taking global scope as + * preference over link scoped addresses. + */ + he = gethostbyname (hostname); + if (NULL == he) { + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + pgm_error_from_h_errno (h_errno), +#ifndef _WIN32 + _("Resolving IPv4 hostname address: %s"), + hstrerror (h_errno) +#else + _("Resolving IPv4 hostname address: %s"), + pgm_wsastrerror (WSAGetLastError()) +#endif + ); + return FALSE; + } + + struct pgm_ifaddrs_t *ifap, *ifa, *ifa6; + if (!pgm_getifaddrs (&ifap, error)) { + pgm_prefix_error (error, + _("Enumerating network interfaces: ")); + return FALSE; + } + +/* hunt for IPv4 interface */ + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (NULL == ifa->ifa_addr || + AF_INET != ifa->ifa_addr->sa_family) + continue; + if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == ((struct in_addr*)(he->h_addr_list[0]))->s_addr) + { + goto ipv4_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv4 network interface.")); + return FALSE; +ipv4_found: + +/* hunt for IPv6 interface */ + for (ifa6 = ifap; ifa6; ifa6 = ifa6->ifa_next) + { + if (AF_INET6 != ifa6->ifa_addr->sa_family) + continue; + if (0 == strcmp (ifa->ifa_name, ifa6->ifa_name)) + { + goto ipv6_found; + } + } + pgm_freeifaddrs (ifap); + pgm_set_error (error, + PGM_ERROR_DOMAIN_IF, + PGM_ERROR_NONET, + _("Discovering primary IPv6 network interface.")); + return FALSE; +ipv6_found: + + memcpy (addr, ifa6->ifa_addr, pgm_sockaddr_len (ifa6->ifa_addr)); + pgm_freeifaddrs (ifap); + return TRUE; +} + +static +const char* +pgm_family_string ( + const sa_family_t family + ) +{ + const char* c; + + switch (family) { + case AF_UNSPEC: c = "AF_UNSPEC"; break; + case AF_INET: c = "AF_INET"; break; + case AF_INET6: c = "AF_INET6"; break; + default: c = "(unknown)"; break; + } + + return c; +} + +/* eof */ |