getnameinfo.c revision 66374
162615Sitojun/*	$FreeBSD: head/lib/libc/net/getnameinfo.c 66374 2000-09-25 23:04:36Z itojun $	*/
266374Sitojun/*	$KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $	*/
362615Sitojun
455163Sshin/*
555163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
655163Sshin * All rights reserved.
755163Sshin *
855163Sshin * Redistribution and use in source and binary forms, with or without
955163Sshin * modification, are permitted provided that the following conditions
1055163Sshin * are met:
1155163Sshin * 1. Redistributions of source code must retain the above copyright
1255163Sshin *    notice, this list of conditions and the following disclaimer.
1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1455163Sshin *    notice, this list of conditions and the following disclaimer in the
1555163Sshin *    documentation and/or other materials provided with the distribution.
1655163Sshin * 3. Neither the name of the project nor the names of its contributors
1755163Sshin *    may be used to endorse or promote products derived from this software
1855163Sshin *    without specific prior written permission.
1955163Sshin *
2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3055163Sshin * SUCH DAMAGE.
3155163Sshin */
3255163Sshin
3355163Sshin/*
3455163Sshin * Issues to be discussed:
3555163Sshin * - Thread safe-ness must be checked
3662615Sitojun * - RFC2553 says that we should raise error on short buffer.  X/Open says
3762615Sitojun *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
3866374Sitojun *   modified).  ipngwg rough consensus seems to follow RFC2553.
3962615Sitojun * - What is "local" in NI_FQDN?
4062615Sitojun * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
4162615Sitojun * - (KAME extension) NI_WITHSCOPEID when called with global address,
4262615Sitojun *   and sin6_scope_id filled
4355163Sshin */
4455163Sshin
4555163Sshin#include <sys/types.h>
4655163Sshin#include <sys/socket.h>
4755163Sshin#include <net/if.h>
4855163Sshin#include <netinet/in.h>
4955163Sshin#include <arpa/inet.h>
5055163Sshin#include <arpa/nameser.h>
5155163Sshin#include <netdb.h>
5255163Sshin#include <resolv.h>
5355163Sshin#include <string.h>
5455163Sshin#include <stddef.h>
5562615Sitojun#include <errno.h>
5655163Sshin
5762615Sitojun#define SUCCESS 0
5862615Sitojun#define ANY 0
5962615Sitojun#define YES 1
6062615Sitojun#define NO  0
6155163Sshin
6255163Sshinstatic struct afd {
6355163Sshin	int a_af;
6455163Sshin	int a_addrlen;
6555163Sshin	int a_socklen;
6655163Sshin	int a_off;
6755163Sshin} afdl [] = {
6855163Sshin#ifdef INET6
6955163Sshin	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
7055163Sshin		offsetof(struct sockaddr_in6, sin6_addr)},
7155163Sshin#endif
7255163Sshin	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
7355163Sshin		offsetof(struct sockaddr_in, sin_addr)},
7455163Sshin	{0, 0, 0},
7555163Sshin};
7655163Sshin
7755163Sshinstruct sockinet {
7855163Sshin	u_char	si_len;
7955163Sshin	u_char	si_family;
8055163Sshin	u_short	si_port;
8155163Sshin};
8255163Sshin
8362615Sitojun#ifdef INET6
8462615Sitojunstatic int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
8562615Sitojun				 size_t, int));
8662615Sitojunstatic int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
8762615Sitojun#endif
8855163Sshin
8966374Sitojun/* 2553bis: use EAI_xx for getnameinfo */
9062615Sitojun#define ENI_NOSOCKET 	EAI_FAIL		/*XXX*/
9162615Sitojun#define ENI_NOSERVNAME	EAI_NONAME
9262615Sitojun#define ENI_NOHOSTNAME	EAI_NONAME
9362615Sitojun#define ENI_MEMORY	EAI_MEMORY
9462615Sitojun#define ENI_SYSTEM	EAI_SYSTEM
9562615Sitojun#define ENI_FAMILY	EAI_FAMILY
9662615Sitojun#define ENI_SALEN	EAI_FAMILY
9762615Sitojun
9855163Sshinint
9955163Sshingetnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
10055163Sshin	const struct sockaddr *sa;
10155163Sshin	size_t salen;
10255163Sshin	char *host;
10355163Sshin	size_t hostlen;
10455163Sshin	char *serv;
10555163Sshin	size_t servlen;
10655163Sshin	int flags;
10755163Sshin{
10855163Sshin	struct afd *afd;
10955163Sshin	struct servent *sp;
11055163Sshin	struct hostent *hp;
11155163Sshin	u_short port;
11255163Sshin	int family, i;
11362615Sitojun	const char *addr;
11462615Sitojun	u_int32_t v4a;
11555163Sshin	int h_error;
11655163Sshin	char numserv[512];
11755163Sshin	char numaddr[512];
11855163Sshin
11955163Sshin	if (sa == NULL)
12055163Sshin		return ENI_NOSOCKET;
12155163Sshin
12255163Sshin	if (sa->sa_len != salen)
12355163Sshin		return ENI_SALEN;
12462615Sitojun
12555163Sshin	family = sa->sa_family;
12655163Sshin	for (i = 0; afdl[i].a_af; i++)
12755163Sshin		if (afdl[i].a_af == family) {
12855163Sshin			afd = &afdl[i];
12955163Sshin			goto found;
13055163Sshin		}
13155163Sshin	return ENI_FAMILY;
13262615Sitojun
13355163Sshin found:
13455163Sshin	if (salen != afd->a_socklen)
13555163Sshin		return ENI_SALEN;
13662615Sitojun
13762615Sitojun	/* network byte order */
13862615Sitojun	port = ((const struct sockinet *)sa)->si_port;
13962615Sitojun	addr = (const char *)sa + afd->a_off;
14055163Sshin
14155163Sshin	if (serv == NULL || servlen == 0) {
14262615Sitojun		/*
14362615Sitojun		 * do nothing in this case.
14462615Sitojun		 * in case you are wondering if "&&" is more correct than
14562615Sitojun		 * "||" here: RFC2553 says that serv == NULL OR servlen == 0
14662615Sitojun		 * means that the caller does not want the result.
14762615Sitojun		 */
14855163Sshin	} else {
14955163Sshin		if (flags & NI_NUMERICSERV)
15055163Sshin			sp = NULL;
15155163Sshin		else {
15255163Sshin			sp = getservbyport(port,
15355163Sshin				(flags & NI_DGRAM) ? "udp" : "tcp");
15455163Sshin		}
15555163Sshin		if (sp) {
15666374Sitojun			if (strlen(sp->s_name) + 1 > servlen)
15755163Sshin				return ENI_MEMORY;
15855163Sshin			strcpy(serv, sp->s_name);
15955163Sshin		} else {
16055163Sshin			snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
16166374Sitojun			if (strlen(numserv) + 1 > servlen)
16255163Sshin				return ENI_MEMORY;
16355163Sshin			strcpy(serv, numserv);
16455163Sshin		}
16555163Sshin	}
16655163Sshin
16755163Sshin	switch (sa->sa_family) {
16855163Sshin	case AF_INET:
16962615Sitojun		v4a = (u_int32_t)
17062615Sitojun		    ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
17155163Sshin		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
17255163Sshin			flags |= NI_NUMERICHOST;
17355163Sshin		v4a >>= IN_CLASSA_NSHIFT;
17456671Sshin		if (v4a == 0)
17562615Sitojun			flags |= NI_NUMERICHOST;
17655163Sshin		break;
17755163Sshin#ifdef INET6
17855163Sshin	case AF_INET6:
17955163Sshin	    {
18062615Sitojun		const struct sockaddr_in6 *sin6;
18162615Sitojun		sin6 = (const struct sockaddr_in6 *)sa;
18262615Sitojun		switch (sin6->sin6_addr.s6_addr[0]) {
18362615Sitojun		case 0x00:
18462615Sitojun			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
18562615Sitojun				;
18662615Sitojun			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
18762615Sitojun				;
18862615Sitojun			else
18962615Sitojun				flags |= NI_NUMERICHOST;
19062615Sitojun			break;
19162615Sitojun		default:
19262615Sitojun			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
19362615Sitojun				flags |= NI_NUMERICHOST;
19462615Sitojun			}
19562615Sitojun			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
19662615Sitojun				flags |= NI_NUMERICHOST;
19762615Sitojun			break;
19862615Sitojun		}
19955163Sshin	    }
20055163Sshin		break;
20155163Sshin#endif
20255163Sshin	}
20355163Sshin	if (host == NULL || hostlen == 0) {
20462615Sitojun		/*
20562615Sitojun		 * do nothing in this case.
20662615Sitojun		 * in case you are wondering if "&&" is more correct than
20762615Sitojun		 * "||" here: RFC2553 says that host == NULL OR hostlen == 0
20862615Sitojun		 * means that the caller does not want the result.
20962615Sitojun		 */
21055163Sshin	} else if (flags & NI_NUMERICHOST) {
21162615Sitojun		int numaddrlen;
21262615Sitojun
21355163Sshin		/* NUMERICHOST and NAMEREQD conflicts with each other */
21455163Sshin		if (flags & NI_NAMEREQD)
21555163Sshin			return ENI_NOHOSTNAME;
21662615Sitojun
21762615Sitojun		switch(afd->a_af) {
21855163Sshin#ifdef INET6
21962615Sitojun		case AF_INET6:
22062615Sitojun		{
22162615Sitojun			int error;
22255163Sshin
22362615Sitojun			if ((error = ip6_parsenumeric(sa, addr, host,
22462615Sitojun						      hostlen, flags)) != 0)
22562615Sitojun				return(error);
22662615Sitojun			break;
22755163Sshin		}
22862615Sitojun#endif
22962615Sitojun		default:
23062615Sitojun			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
23162615Sitojun			    == NULL)
23262615Sitojun				return ENI_SYSTEM;
23362615Sitojun			numaddrlen = strlen(numaddr);
23462615Sitojun			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
23562615Sitojun				return ENI_MEMORY;
23662615Sitojun			strcpy(host, numaddr);
23762615Sitojun			break;
23862615Sitojun		}
23955163Sshin	} else {
24055163Sshin		hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
24162615Sitojun
24255163Sshin		if (hp) {
24362615Sitojun#if 0
24462615Sitojun			/*
24562615Sitojun			 * commented out, since "for local host" is not
24662615Sitojun			 * implemented here - see RFC2553 p30
24762615Sitojun			 */
24855163Sshin			if (flags & NI_NOFQDN) {
24962615Sitojun				char *p;
25055163Sshin				p = strchr(hp->h_name, '.');
25162615Sitojun				if (p)
25262615Sitojun					*p = '\0';
25355163Sshin			}
25462615Sitojun#endif
25566374Sitojun			if (strlen(hp->h_name) + 1 > hostlen) {
25655163Sshin				freehostent(hp);
25755163Sshin				return ENI_MEMORY;
25855163Sshin			}
25955163Sshin			strcpy(host, hp->h_name);
26055163Sshin			freehostent(hp);
26155163Sshin		} else {
26255163Sshin			if (flags & NI_NAMEREQD)
26355163Sshin				return ENI_NOHOSTNAME;
26462615Sitojun			switch(afd->a_af) {
26562615Sitojun#ifdef INET6
26662615Sitojun			case AF_INET6:
26762615Sitojun			{
26862615Sitojun				int error;
26962615Sitojun
27062615Sitojun				if ((error = ip6_parsenumeric(sa, addr, host,
27162615Sitojun							      hostlen,
27262615Sitojun							      flags)) != 0)
27362615Sitojun					return(error);
27462615Sitojun				break;
27562615Sitojun			}
27662615Sitojun#endif
27762615Sitojun			default:
27862615Sitojun				if (inet_ntop(afd->a_af, addr, host,
27962615Sitojun				    hostlen) == NULL)
28062615Sitojun					return ENI_SYSTEM;
28162615Sitojun				break;
28262615Sitojun			}
28355163Sshin		}
28455163Sshin	}
28555163Sshin	return SUCCESS;
28655163Sshin}
28762615Sitojun
28862615Sitojun#ifdef INET6
28962615Sitojunstatic int
29062615Sitojunip6_parsenumeric(sa, addr, host, hostlen, flags)
29162615Sitojun	const struct sockaddr *sa;
29262615Sitojun	const char *addr;
29362615Sitojun	char *host;
29462615Sitojun	size_t hostlen;
29562615Sitojun	int flags;
29662615Sitojun{
29762615Sitojun	int numaddrlen;
29862615Sitojun	char numaddr[512];
29962615Sitojun
30062615Sitojun	if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr))
30162615Sitojun	    == NULL)
30262615Sitojun		return ENI_SYSTEM;
30362615Sitojun
30462615Sitojun	numaddrlen = strlen(numaddr);
30562615Sitojun	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
30662615Sitojun		return ENI_MEMORY;
30762615Sitojun	strcpy(host, numaddr);
30862615Sitojun
30962615Sitojun#ifdef NI_WITHSCOPEID
31062615Sitojun	if (
31162615Sitojun#ifdef DONT_OPAQUE_SCOPEID
31262615Sitojun	    (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) ||
31362615Sitojun	     IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) &&
31462615Sitojun#endif
31562615Sitojun	    ((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
31662615Sitojun#ifndef ALWAYS_WITHSCOPE
31762615Sitojun		if (flags & NI_WITHSCOPEID)
31862615Sitojun#endif /* !ALWAYS_WITHSCOPE */
31962615Sitojun		{
32062615Sitojun			char scopebuf[MAXHOSTNAMELEN];
32162615Sitojun			int scopelen;
32262615Sitojun
32362615Sitojun			/* ip6_sa2str never fails */
32462615Sitojun			scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa,
32562615Sitojun					      scopebuf, sizeof(scopebuf),
32662615Sitojun					      flags);
32762615Sitojun			if (scopelen + 1 + numaddrlen + 1 > hostlen)
32862615Sitojun				return ENI_MEMORY;
32962615Sitojun			/*
33062615Sitojun			 * construct <numeric-addr><delim><scopeid>
33162615Sitojun			 */
33262615Sitojun			memcpy(host + numaddrlen + 1, scopebuf,
33362615Sitojun			       scopelen);
33462615Sitojun			host[numaddrlen] = SCOPE_DELIMITER;
33562615Sitojun			host[numaddrlen + 1 + scopelen] = '\0';
33662615Sitojun		}
33762615Sitojun	}
33862615Sitojun#endif /* NI_WITHSCOPEID */
33962615Sitojun
34062615Sitojun	return 0;
34162615Sitojun}
34262615Sitojun
34362615Sitojun/* ARGSUSED */
34462615Sitojunstatic int
34562615Sitojunip6_sa2str(sa6, buf, bufsiz, flags)
34662615Sitojun	const struct sockaddr_in6 *sa6;
34762615Sitojun	char *buf;
34862615Sitojun	size_t bufsiz;
34962615Sitojun	int flags;
35062615Sitojun{
35162615Sitojun	unsigned int ifindex = (unsigned int)sa6->sin6_scope_id;
35262615Sitojun	const struct in6_addr *a6 = &sa6->sin6_addr;
35362615Sitojun
35462615Sitojun#ifdef NI_NUMERICSCOPE
35562615Sitojun	if (flags & NI_NUMERICSCOPE) {
35662615Sitojun		return(snprintf(buf, bufsiz, "%d", sa6->sin6_scope_id));
35762615Sitojun	}
35862615Sitojun#endif
35962615Sitojun
36062615Sitojun	/* if_indextoname() does not take buffer size.  not a good api... */
36162615Sitojun	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
36262615Sitojun	    bufsiz >= IF_NAMESIZE) {
36362615Sitojun		char *p = if_indextoname(ifindex, buf);
36462615Sitojun		if (p) {
36562615Sitojun			return(strlen(p));
36662615Sitojun		}
36762615Sitojun	}
36862615Sitojun
36962615Sitojun	/* last resort */
37062615Sitojun	return(snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id));
37162615Sitojun}
37262615Sitojun#endif /* INET6 */
373