summaryrefslogtreecommitdiffstats
path: root/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c')
-rw-r--r--3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c196
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 */