summaryrefslogblamecommitdiffstats
path: root/3rdparty/openpgm-svn-r1135/pgm/getnodeaddr.c
blob: 0765c9f3a099467da86252787d9db41ac09cd42b (plain) (tree)



































































































































































































                                                                                                                              
/* 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 */