getnameinfo.c revision 99252
166374Sitojun/*	$KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $	*/
262615Sitojun
355163Sshin/*
455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
555163Sshin * All rights reserved.
655163Sshin *
755163Sshin * Redistribution and use in source and binary forms, with or without
855163Sshin * modification, are permitted provided that the following conditions
955163Sshin * are met:
1055163Sshin * 1. Redistributions of source code must retain the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer.
1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355163Sshin *    notice, this list of conditions and the following disclaimer in the
1455163Sshin *    documentation and/or other materials provided with the distribution.
1555163Sshin * 3. Neither the name of the project nor the names of its contributors
1655163Sshin *    may be used to endorse or promote products derived from this software
1755163Sshin *    without specific prior written permission.
1855163Sshin *
1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955163Sshin * SUCH DAMAGE.
3055163Sshin */
3155163Sshin
3255163Sshin/*
3355163Sshin * Issues to be discussed:
3455163Sshin * - Thread safe-ness must be checked
3562615Sitojun * - RFC2553 says that we should raise error on short buffer.  X/Open says
3662615Sitojun *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
3766374Sitojun *   modified).  ipngwg rough consensus seems to follow RFC2553.
3862615Sitojun * - What is "local" in NI_FQDN?
3962615Sitojun * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
4099252Sume * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
4199252Sume *   sin6_scope_id is filled - standardization status?
4299252Sume *   XXX breaks backward compat for code that expects no scopeid.
4399252Sume *   beware on merge.
4455163Sshin */
4555163Sshin
4692986Sobrien#include <sys/cdefs.h>
4792986Sobrien__FBSDID("$FreeBSD: head/lib/libc/net/getnameinfo.c 99252 2002-07-02 11:05:31Z ume $");
4892986Sobrien
4955163Sshin#include <sys/types.h>
5055163Sshin#include <sys/socket.h>
5155163Sshin#include <net/if.h>
5255163Sshin#include <netinet/in.h>
5355163Sshin#include <arpa/inet.h>
5455163Sshin#include <arpa/nameser.h>
5555163Sshin#include <netdb.h>
5655163Sshin#include <resolv.h>
5755163Sshin#include <string.h>
5855163Sshin#include <stddef.h>
5962615Sitojun#include <errno.h>
6055163Sshin
6162615Sitojun#define SUCCESS 0
6262615Sitojun#define ANY 0
6362615Sitojun#define YES 1
6462615Sitojun#define NO  0
6555163Sshin
6655163Sshinstatic struct afd {
6755163Sshin	int a_af;
6855163Sshin	int a_addrlen;
6955163Sshin	int a_socklen;
7055163Sshin	int a_off;
7155163Sshin} afdl [] = {
7255163Sshin#ifdef INET6
7355163Sshin	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
7455163Sshin		offsetof(struct sockaddr_in6, sin6_addr)},
7555163Sshin#endif
7655163Sshin	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
7755163Sshin		offsetof(struct sockaddr_in, sin_addr)},
7855163Sshin	{0, 0, 0},
7955163Sshin};
8055163Sshin
8155163Sshinstruct sockinet {
8255163Sshin	u_char	si_len;
8355163Sshin	u_char	si_family;
8455163Sshin	u_short	si_port;
8555163Sshin};
8655163Sshin
8762615Sitojun#ifdef INET6
8892941Sobrienstatic int ip6_parsenumeric(const struct sockaddr *, const char *, char *,
8992941Sobrien	    size_t, int);
9092905Sobrienstatic int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int);
9162615Sitojun#endif
9255163Sshin
9366374Sitojun/* 2553bis: use EAI_xx for getnameinfo */
9462615Sitojun#define ENI_NOSOCKET 	EAI_FAIL		/*XXX*/
9562615Sitojun#define ENI_NOSERVNAME	EAI_NONAME
9662615Sitojun#define ENI_NOHOSTNAME	EAI_NONAME
9762615Sitojun#define ENI_MEMORY	EAI_MEMORY
9862615Sitojun#define ENI_SYSTEM	EAI_SYSTEM
9962615Sitojun#define ENI_FAMILY	EAI_FAMILY
10062615Sitojun#define ENI_SALEN	EAI_FAMILY
10162615Sitojun
10255163Sshinint
10355163Sshingetnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
10455163Sshin	const struct sockaddr *sa;
10572510Sume	socklen_t salen;
10655163Sshin	char *host;
10755163Sshin	size_t hostlen;
10855163Sshin	char *serv;
10955163Sshin	size_t servlen;
11055163Sshin	int flags;
11155163Sshin{
11255163Sshin	struct afd *afd;
11355163Sshin	struct servent *sp;
11455163Sshin	struct hostent *hp;
11555163Sshin	u_short port;
11655163Sshin	int family, i;
11762615Sitojun	const char *addr;
11862615Sitojun	u_int32_t v4a;
11955163Sshin	int h_error;
12055163Sshin	char numserv[512];
12155163Sshin	char numaddr[512];
12255163Sshin
12355163Sshin	if (sa == NULL)
12455163Sshin		return ENI_NOSOCKET;
12555163Sshin
12655163Sshin	if (sa->sa_len != salen)
12755163Sshin		return ENI_SALEN;
12862615Sitojun
12955163Sshin	family = sa->sa_family;
13055163Sshin	for (i = 0; afdl[i].a_af; i++)
13155163Sshin		if (afdl[i].a_af == family) {
13255163Sshin			afd = &afdl[i];
13355163Sshin			goto found;
13455163Sshin		}
13555163Sshin	return ENI_FAMILY;
13662615Sitojun
13755163Sshin found:
13855163Sshin	if (salen != afd->a_socklen)
13955163Sshin		return ENI_SALEN;
14062615Sitojun
14162615Sitojun	/* network byte order */
14262615Sitojun	port = ((const struct sockinet *)sa)->si_port;
14362615Sitojun	addr = (const char *)sa + afd->a_off;
14455163Sshin
14555163Sshin	if (serv == NULL || servlen == 0) {
14662615Sitojun		/*
14762615Sitojun		 * do nothing in this case.
14862615Sitojun		 * in case you are wondering if "&&" is more correct than
14962615Sitojun		 * "||" here: RFC2553 says that serv == NULL OR servlen == 0
15062615Sitojun		 * means that the caller does not want the result.
15162615Sitojun		 */
15255163Sshin	} else {
15355163Sshin		if (flags & NI_NUMERICSERV)
15455163Sshin			sp = NULL;
15555163Sshin		else {
15655163Sshin			sp = getservbyport(port,
15755163Sshin				(flags & NI_DGRAM) ? "udp" : "tcp");
15855163Sshin		}
15955163Sshin		if (sp) {
16066374Sitojun			if (strlen(sp->s_name) + 1 > servlen)
16155163Sshin				return ENI_MEMORY;
16255163Sshin			strcpy(serv, sp->s_name);
16355163Sshin		} else {
16455163Sshin			snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
16566374Sitojun			if (strlen(numserv) + 1 > servlen)
16655163Sshin				return ENI_MEMORY;
16755163Sshin			strcpy(serv, numserv);
16855163Sshin		}
16955163Sshin	}
17055163Sshin
17155163Sshin	switch (sa->sa_family) {
17255163Sshin	case AF_INET:
17362615Sitojun		v4a = (u_int32_t)
17462615Sitojun		    ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
17555163Sshin		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
17655163Sshin			flags |= NI_NUMERICHOST;
17755163Sshin		v4a >>= IN_CLASSA_NSHIFT;
17856671Sshin		if (v4a == 0)
17962615Sitojun			flags |= NI_NUMERICHOST;
18055163Sshin		break;
18155163Sshin#ifdef INET6
18255163Sshin	case AF_INET6:
18355163Sshin	    {
18462615Sitojun		const struct sockaddr_in6 *sin6;
18562615Sitojun		sin6 = (const struct sockaddr_in6 *)sa;
18662615Sitojun		switch (sin6->sin6_addr.s6_addr[0]) {
18762615Sitojun		case 0x00:
18862615Sitojun			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
18962615Sitojun				;
19062615Sitojun			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
19162615Sitojun				;
19262615Sitojun			else
19362615Sitojun				flags |= NI_NUMERICHOST;
19462615Sitojun			break;
19562615Sitojun		default:
19662615Sitojun			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
19762615Sitojun				flags |= NI_NUMERICHOST;
19862615Sitojun			}
19962615Sitojun			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
20062615Sitojun				flags |= NI_NUMERICHOST;
20162615Sitojun			break;
20262615Sitojun		}
20355163Sshin	    }
20455163Sshin		break;
20555163Sshin#endif
20655163Sshin	}
20755163Sshin	if (host == NULL || hostlen == 0) {
20862615Sitojun		/*
20962615Sitojun		 * do nothing in this case.
21062615Sitojun		 * in case you are wondering if "&&" is more correct than
21162615Sitojun		 * "||" here: RFC2553 says that host == NULL OR hostlen == 0
21262615Sitojun		 * means that the caller does not want the result.
21362615Sitojun		 */
21455163Sshin	} else if (flags & NI_NUMERICHOST) {
21562615Sitojun		int numaddrlen;
21662615Sitojun
21755163Sshin		/* NUMERICHOST and NAMEREQD conflicts with each other */
21855163Sshin		if (flags & NI_NAMEREQD)
21955163Sshin			return ENI_NOHOSTNAME;
22062615Sitojun
22162615Sitojun		switch(afd->a_af) {
22255163Sshin#ifdef INET6
22362615Sitojun		case AF_INET6:
22462615Sitojun		{
22562615Sitojun			int error;
22655163Sshin
22762615Sitojun			if ((error = ip6_parsenumeric(sa, addr, host,
22862615Sitojun						      hostlen, flags)) != 0)
22962615Sitojun				return(error);
23062615Sitojun			break;
23155163Sshin		}
23262615Sitojun#endif
23362615Sitojun		default:
23462615Sitojun			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
23562615Sitojun			    == NULL)
23662615Sitojun				return ENI_SYSTEM;
23762615Sitojun			numaddrlen = strlen(numaddr);
23862615Sitojun			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
23962615Sitojun				return ENI_MEMORY;
24062615Sitojun			strcpy(host, numaddr);
24162615Sitojun			break;
24262615Sitojun		}
24355163Sshin	} else {
24455163Sshin		hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
24562615Sitojun
24655163Sshin		if (hp) {
24762615Sitojun#if 0
24862615Sitojun			/*
24962615Sitojun			 * commented out, since "for local host" is not
25062615Sitojun			 * implemented here - see RFC2553 p30
25162615Sitojun			 */
25255163Sshin			if (flags & NI_NOFQDN) {
25362615Sitojun				char *p;
25455163Sshin				p = strchr(hp->h_name, '.');
25562615Sitojun				if (p)
25662615Sitojun					*p = '\0';
25755163Sshin			}
25862615Sitojun#endif
25966374Sitojun			if (strlen(hp->h_name) + 1 > hostlen) {
26055163Sshin				freehostent(hp);
26155163Sshin				return ENI_MEMORY;
26255163Sshin			}
26355163Sshin			strcpy(host, hp->h_name);
26455163Sshin			freehostent(hp);
26555163Sshin		} else {
26655163Sshin			if (flags & NI_NAMEREQD)
26755163Sshin				return ENI_NOHOSTNAME;
26862615Sitojun			switch(afd->a_af) {
26962615Sitojun#ifdef INET6
27062615Sitojun			case AF_INET6:
27162615Sitojun			{
27262615Sitojun				int error;
27362615Sitojun
27462615Sitojun				if ((error = ip6_parsenumeric(sa, addr, host,
27562615Sitojun							      hostlen,
27662615Sitojun							      flags)) != 0)
27762615Sitojun					return(error);
27862615Sitojun				break;
27962615Sitojun			}
28062615Sitojun#endif
28162615Sitojun			default:
28262615Sitojun				if (inet_ntop(afd->a_af, addr, host,
28362615Sitojun				    hostlen) == NULL)
28462615Sitojun					return ENI_SYSTEM;
28562615Sitojun				break;
28662615Sitojun			}
28755163Sshin		}
28855163Sshin	}
28955163Sshin	return SUCCESS;
29055163Sshin}
29162615Sitojun
29262615Sitojun#ifdef INET6
29362615Sitojunstatic int
29462615Sitojunip6_parsenumeric(sa, addr, host, hostlen, flags)
29562615Sitojun	const struct sockaddr *sa;
29662615Sitojun	const char *addr;
29762615Sitojun	char *host;
29862615Sitojun	size_t hostlen;
29962615Sitojun	int flags;
30062615Sitojun{
30162615Sitojun	int numaddrlen;
30262615Sitojun	char numaddr[512];
30362615Sitojun
30462615Sitojun	if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr))
30562615Sitojun	    == NULL)
30662615Sitojun		return ENI_SYSTEM;
30762615Sitojun
30862615Sitojun	numaddrlen = strlen(numaddr);
30962615Sitojun	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
31062615Sitojun		return ENI_MEMORY;
31162615Sitojun	strcpy(host, numaddr);
31262615Sitojun
31399252Sume	if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
31499252Sume		char zonebuf[MAXHOSTNAMELEN];
31599252Sume		int zonelen;
31662615Sitojun
31799252Sume		/* ip6_sa2str never fails */
31899252Sume		zonelen = ip6_sa2str(
31999252Sume		    (const struct sockaddr_in6 *)(const void *)sa,
32099252Sume		    zonebuf, sizeof(zonebuf), flags);
32199252Sume		if (zonelen < 0)
32299252Sume			return EAI_MEMORY;
32399252Sume		if (zonelen + 1 + numaddrlen + 1 > hostlen)
32499252Sume			return ENI_MEMORY;
32599252Sume		/* construct <numeric-addr><delim><scopeid> */
32699252Sume		memcpy(host + numaddrlen + 1, zonebuf,
32799252Sume		       (size_t)zonelen);
32899252Sume		host[numaddrlen] = SCOPE_DELIMITER;
32999252Sume		host[numaddrlen + 1 + zonelen] = '\0';
33062615Sitojun	}
33162615Sitojun
33262615Sitojun	return 0;
33362615Sitojun}
33462615Sitojun
33562615Sitojun/* ARGSUSED */
33662615Sitojunstatic int
33762615Sitojunip6_sa2str(sa6, buf, bufsiz, flags)
33862615Sitojun	const struct sockaddr_in6 *sa6;
33962615Sitojun	char *buf;
34062615Sitojun	size_t bufsiz;
34162615Sitojun	int flags;
34262615Sitojun{
34362615Sitojun	unsigned int ifindex = (unsigned int)sa6->sin6_scope_id;
34462615Sitojun	const struct in6_addr *a6 = &sa6->sin6_addr;
34562615Sitojun
34662615Sitojun#ifdef NI_NUMERICSCOPE
34762615Sitojun	if (flags & NI_NUMERICSCOPE) {
34862615Sitojun		return(snprintf(buf, bufsiz, "%d", sa6->sin6_scope_id));
34962615Sitojun	}
35062615Sitojun#endif
35162615Sitojun
35262615Sitojun	/* if_indextoname() does not take buffer size.  not a good api... */
35362615Sitojun	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
35462615Sitojun	    bufsiz >= IF_NAMESIZE) {
35562615Sitojun		char *p = if_indextoname(ifindex, buf);
35662615Sitojun		if (p) {
35762615Sitojun			return(strlen(p));
35862615Sitojun		}
35962615Sitojun	}
36062615Sitojun
36162615Sitojun	/* last resort */
36262615Sitojun	return(snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id));
36362615Sitojun}
36462615Sitojun#endif /* INET6 */
365