1100628Sume/*	$KAME: getnameinfo.c,v 1.61 2002/06/27 09:25:47 itojun Exp $	*/
262615Sitojun
355163Sshin/*
455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5167121Sbms * Copyright (c) 2000 Ben Harris.
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.
4199252Sume * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
4299252Sume *   sin6_scope_id is filled - standardization status?
4399252Sume *   XXX breaks backward compat for code that expects no scopeid.
4499252Sume *   beware on merge.
4555163Sshin */
4655163Sshin
4792986Sobrien#include <sys/cdefs.h>
4892986Sobrien__FBSDID("$FreeBSD$");
4992986Sobrien
5055163Sshin#include <sys/types.h>
5155163Sshin#include <sys/socket.h>
5255163Sshin#include <net/if.h>
53167121Sbms#include <net/if_dl.h>
54167121Sbms#include <net/if_types.h>
55199221Sume#include <net/firewire.h>
5655163Sshin#include <netinet/in.h>
5755163Sshin#include <arpa/inet.h>
5855163Sshin#include <arpa/nameser.h>
5955163Sshin#include <netdb.h>
6055163Sshin#include <resolv.h>
6155163Sshin#include <string.h>
6255163Sshin#include <stddef.h>
6362615Sitojun#include <errno.h>
6455163Sshin
65167121Sbmsstatic int	getnameinfo_inet(const struct sockaddr *, socklen_t, char *,
66167121Sbms    size_t, char *, size_t, int);
67167121Sbms#ifdef INET6
68167121Sbmsstatic int ip6_parsenumeric(const struct sockaddr *, const char *, char *,
69167121Sbms    size_t, int);
70167121Sbmsstatic int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int);
71167121Sbms#endif
72167121Sbmsstatic int	getnameinfo_link(const struct sockaddr *, socklen_t, char *,
73167121Sbms    size_t, char *, size_t, int);
74167121Sbmsstatic int	hexname(const u_int8_t *, size_t, char *, size_t);
75167121Sbms
76167121Sbmsint
77167121Sbmsgetnameinfo(const struct sockaddr *sa, socklen_t salen,
78167121Sbms    char *host, size_t hostlen, char *serv, size_t servlen,
79167121Sbms    int flags)
80167121Sbms{
81287729Shrs	if (sa == NULL)
82287729Shrs		return (EAI_FAIL);
83167121Sbms
84167121Sbms	switch (sa->sa_family) {
85167121Sbms	case AF_INET:
86167121Sbms#ifdef INET6
87167121Sbms	case AF_INET6:
88167121Sbms#endif
89167121Sbms		return getnameinfo_inet(sa, salen, host, hostlen, serv,
90167121Sbms		    servlen, flags);
91167121Sbms	case AF_LINK:
92167121Sbms		return getnameinfo_link(sa, salen, host, hostlen, serv,
93167121Sbms		    servlen, flags);
94167121Sbms	default:
95167121Sbms		return EAI_FAMILY;
96167121Sbms	}
97167121Sbms}
98167121Sbms
99100628Sumestatic const struct afd {
10055163Sshin	int a_af;
101145831Sume	size_t a_addrlen;
102145831Sume	socklen_t a_socklen;
10355163Sshin	int a_off;
10455163Sshin} afdl [] = {
10555163Sshin#ifdef INET6
10655163Sshin	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
10755163Sshin		offsetof(struct sockaddr_in6, sin6_addr)},
10855163Sshin#endif
10955163Sshin	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
11055163Sshin		offsetof(struct sockaddr_in, sin_addr)},
11155163Sshin	{0, 0, 0},
11255163Sshin};
11355163Sshin
11455163Sshinstruct sockinet {
11555163Sshin	u_char	si_len;
11655163Sshin	u_char	si_family;
11755163Sshin	u_short	si_port;
11855163Sshin};
11955163Sshin
120167121Sbmsstatic int
121167121Sbmsgetnameinfo_inet(const struct sockaddr *sa, socklen_t salen,
122157119Sume    char *host, size_t hostlen, char *serv, size_t servlen,
123157119Sume    int flags)
12455163Sshin{
125100628Sume	const struct afd *afd;
12655163Sshin	struct servent *sp;
12755163Sshin	struct hostent *hp;
12855163Sshin	u_short port;
12962615Sitojun	const char *addr;
13062615Sitojun	u_int32_t v4a;
13155163Sshin	int h_error;
13255163Sshin	char numserv[512];
13355163Sshin	char numaddr[512];
13455163Sshin
135287729Shrs	for (afd = &afdl[0]; afd->a_af > 0; afd++) {
136287729Shrs		if (afd->a_af == sa->sa_family)
137287729Shrs			break;
138287729Shrs	}
139287729Shrs	if (afd->a_af == 0)
140287729Shrs		return (EAI_FAMILY);
14155163Sshin
14255163Sshin	if (salen != afd->a_socklen)
143100628Sume		return EAI_FAIL;
144100628Sume
14562615Sitojun	/* network byte order */
14662615Sitojun	port = ((const struct sockinet *)sa)->si_port;
14762615Sitojun	addr = (const char *)sa + afd->a_off;
14855163Sshin
14955163Sshin	if (serv == NULL || servlen == 0) {
15062615Sitojun		/*
15162615Sitojun		 * do nothing in this case.
15262615Sitojun		 * in case you are wondering if "&&" is more correct than
153100628Sume		 * "||" here: rfc2553bis-03 says that serv == NULL OR
154100628Sume		 * servlen == 0 means that the caller does not want the result.
15562615Sitojun		 */
15655163Sshin	} else {
15755163Sshin		if (flags & NI_NUMERICSERV)
15855163Sshin			sp = NULL;
15955163Sshin		else {
16055163Sshin			sp = getservbyport(port,
16155163Sshin				(flags & NI_DGRAM) ? "udp" : "tcp");
16255163Sshin		}
16355163Sshin		if (sp) {
16466374Sitojun			if (strlen(sp->s_name) + 1 > servlen)
165100628Sume				return EAI_MEMORY;
166114443Snectar			strlcpy(serv, sp->s_name, servlen);
16755163Sshin		} else {
168100628Sume			snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
16966374Sitojun			if (strlen(numserv) + 1 > servlen)
170100628Sume				return EAI_MEMORY;
171114443Snectar			strlcpy(serv, numserv, servlen);
17255163Sshin		}
17355163Sshin	}
17455163Sshin
17555163Sshin	switch (sa->sa_family) {
17655163Sshin	case AF_INET:
17762615Sitojun		v4a = (u_int32_t)
17862615Sitojun		    ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
17955163Sshin		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
18055163Sshin			flags |= NI_NUMERICHOST;
18155163Sshin		v4a >>= IN_CLASSA_NSHIFT;
18256671Sshin		if (v4a == 0)
183157119Sume			flags |= NI_NUMERICHOST;
18455163Sshin		break;
18555163Sshin#ifdef INET6
18655163Sshin	case AF_INET6:
18755163Sshin	    {
18862615Sitojun		const struct sockaddr_in6 *sin6;
18962615Sitojun		sin6 = (const struct sockaddr_in6 *)sa;
19062615Sitojun		switch (sin6->sin6_addr.s6_addr[0]) {
19162615Sitojun		case 0x00:
19262615Sitojun			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
19362615Sitojun				;
19462615Sitojun			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
19562615Sitojun				;
19662615Sitojun			else
19762615Sitojun				flags |= NI_NUMERICHOST;
19862615Sitojun			break;
19962615Sitojun		default:
20062615Sitojun			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
20162615Sitojun				flags |= NI_NUMERICHOST;
20262615Sitojun			}
20362615Sitojun			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
20462615Sitojun				flags |= NI_NUMERICHOST;
20562615Sitojun			break;
20662615Sitojun		}
20755163Sshin	    }
20855163Sshin		break;
20955163Sshin#endif
21055163Sshin	}
21155163Sshin	if (host == NULL || hostlen == 0) {
21262615Sitojun		/*
21362615Sitojun		 * do nothing in this case.
21462615Sitojun		 * in case you are wondering if "&&" is more correct than
215100628Sume		 * "||" here: rfc2553bis-03 says that host == NULL or
216100628Sume		 * hostlen == 0 means that the caller does not want the result.
21762615Sitojun		 */
21855163Sshin	} else if (flags & NI_NUMERICHOST) {
219145831Sume		size_t numaddrlen;
22062615Sitojun
22155163Sshin		/* NUMERICHOST and NAMEREQD conflicts with each other */
22255163Sshin		if (flags & NI_NAMEREQD)
223100628Sume			return EAI_NONAME;
22462615Sitojun
22562615Sitojun		switch(afd->a_af) {
22655163Sshin#ifdef INET6
22762615Sitojun		case AF_INET6:
22862615Sitojun		{
22962615Sitojun			int error;
23055163Sshin
23162615Sitojun			if ((error = ip6_parsenumeric(sa, addr, host,
23262615Sitojun						      hostlen, flags)) != 0)
23362615Sitojun				return(error);
23462615Sitojun			break;
23555163Sshin		}
23662615Sitojun#endif
23762615Sitojun		default:
23862615Sitojun			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
23962615Sitojun			    == NULL)
240100628Sume				return EAI_SYSTEM;
24162615Sitojun			numaddrlen = strlen(numaddr);
24262615Sitojun			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
243100628Sume				return EAI_MEMORY;
244114443Snectar			strlcpy(host, numaddr, hostlen);
24562615Sitojun			break;
24662615Sitojun		}
24755163Sshin	} else {
24855163Sshin		hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
24962615Sitojun
25055163Sshin		if (hp) {
25162615Sitojun#if 0
25262615Sitojun			/*
25362615Sitojun			 * commented out, since "for local host" is not
25462615Sitojun			 * implemented here - see RFC2553 p30
25562615Sitojun			 */
25655163Sshin			if (flags & NI_NOFQDN) {
25762615Sitojun				char *p;
25855163Sshin				p = strchr(hp->h_name, '.');
25962615Sitojun				if (p)
26062615Sitojun					*p = '\0';
26155163Sshin			}
26262615Sitojun#endif
26366374Sitojun			if (strlen(hp->h_name) + 1 > hostlen) {
26455163Sshin				freehostent(hp);
265100628Sume				return EAI_MEMORY;
26655163Sshin			}
267114443Snectar			strlcpy(host, hp->h_name, hostlen);
26855163Sshin			freehostent(hp);
26955163Sshin		} else {
27055163Sshin			if (flags & NI_NAMEREQD)
271100628Sume				return EAI_NONAME;
27262615Sitojun			switch(afd->a_af) {
27362615Sitojun#ifdef INET6
27462615Sitojun			case AF_INET6:
27562615Sitojun			{
27662615Sitojun				int error;
27762615Sitojun
27862615Sitojun				if ((error = ip6_parsenumeric(sa, addr, host,
27962615Sitojun							      hostlen,
28062615Sitojun							      flags)) != 0)
28162615Sitojun					return(error);
28262615Sitojun				break;
28362615Sitojun			}
28462615Sitojun#endif
28562615Sitojun			default:
28662615Sitojun				if (inet_ntop(afd->a_af, addr, host,
28762615Sitojun				    hostlen) == NULL)
288100628Sume					return EAI_SYSTEM;
28962615Sitojun				break;
29062615Sitojun			}
29155163Sshin		}
29255163Sshin	}
293100628Sume	return(0);
29455163Sshin}
29562615Sitojun
29662615Sitojun#ifdef INET6
29762615Sitojunstatic int
298157119Sumeip6_parsenumeric(const struct sockaddr *sa, const char *addr,
299157119Sume    char *host, size_t hostlen, int flags)
30062615Sitojun{
301145831Sume	size_t numaddrlen;
30262615Sitojun	char numaddr[512];
30362615Sitojun
304100628Sume	if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
305100628Sume		return EAI_SYSTEM;
30662615Sitojun
30762615Sitojun	numaddrlen = strlen(numaddr);
30862615Sitojun	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
309158790Sume		return EAI_OVERFLOW;
310114443Snectar	strlcpy(host, numaddr, hostlen);
31162615Sitojun
31299252Sume	if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
31399252Sume		char zonebuf[MAXHOSTNAMELEN];
31499252Sume		int zonelen;
31562615Sitojun
31699252Sume		zonelen = ip6_sa2str(
31799252Sume		    (const struct sockaddr_in6 *)(const void *)sa,
31899252Sume		    zonebuf, sizeof(zonebuf), flags);
31999252Sume		if (zonelen < 0)
320158790Sume			return EAI_OVERFLOW;
32199252Sume		if (zonelen + 1 + numaddrlen + 1 > hostlen)
322158790Sume			return EAI_OVERFLOW;
323100628Sume
324100628Sume		/* construct <numeric-addr><delim><zoneid> */
32599252Sume		memcpy(host + numaddrlen + 1, zonebuf,
326100628Sume		    (size_t)zonelen);
32799252Sume		host[numaddrlen] = SCOPE_DELIMITER;
32899252Sume		host[numaddrlen + 1 + zonelen] = '\0';
32962615Sitojun	}
33062615Sitojun
33162615Sitojun	return 0;
33262615Sitojun}
33362615Sitojun
33462615Sitojun/* ARGSUSED */
33562615Sitojunstatic int
336157119Sumeip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, size_t bufsiz, int flags)
33762615Sitojun{
338100628Sume	unsigned int ifindex;
339100628Sume	const struct in6_addr *a6;
340100628Sume	int n;
34162615Sitojun
342100628Sume	ifindex = (unsigned int)sa6->sin6_scope_id;
343100628Sume	a6 = &sa6->sin6_addr;
344100628Sume
34562615Sitojun#ifdef NI_NUMERICSCOPE
346100628Sume	if ((flags & NI_NUMERICSCOPE) != 0) {
347100628Sume		n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
348100628Sume		if (n < 0 || n >= bufsiz)
349100628Sume			return -1;
350100628Sume		else
351100628Sume			return n;
35262615Sitojun	}
35362615Sitojun#endif
35462615Sitojun
35562615Sitojun	/* if_indextoname() does not take buffer size.  not a good api... */
356100628Sume	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
357100628Sume	     IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) {
35862615Sitojun		char *p = if_indextoname(ifindex, buf);
35962615Sitojun		if (p) {
36062615Sitojun			return(strlen(p));
36162615Sitojun		}
36262615Sitojun	}
36362615Sitojun
36462615Sitojun	/* last resort */
365100628Sume	n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
366145831Sume	if (n < 0 || (size_t)n >= bufsiz)
367100628Sume		return -1;
368100628Sume	else
369100628Sume		return n;
37062615Sitojun}
37162615Sitojun#endif /* INET6 */
372167121Sbms
373167121Sbms/*
374167121Sbms * getnameinfo_link():
375167121Sbms * Format a link-layer address into a printable format, paying attention to
376167121Sbms * the interface type.
377167121Sbms */
378167121Sbms/* ARGSUSED */
379167121Sbmsstatic int
380167121Sbmsgetnameinfo_link(const struct sockaddr *sa, socklen_t salen,
381167121Sbms    char *host, size_t hostlen, char *serv, size_t servlen, int flags)
382167121Sbms{
383167121Sbms	const struct sockaddr_dl *sdl =
384167121Sbms	    (const struct sockaddr_dl *)(const void *)sa;
385199221Sume	const struct fw_hwaddr *iha;
386167121Sbms	int n;
387167121Sbms
388167121Sbms	if (serv != NULL && servlen > 0)
389167121Sbms		*serv = '\0';
390167121Sbms
391167121Sbms	if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) {
392167121Sbms		n = snprintf(host, hostlen, "link#%d", sdl->sdl_index);
393287736Shrs		if (n >= hostlen) {
394167121Sbms			*host = '\0';
395287736Shrs			return (EAI_MEMORY);
396167121Sbms		}
397287736Shrs		return (0);
398167121Sbms	}
399167121Sbms
400287736Shrs	if (sdl->sdl_nlen > 0 && sdl->sdl_alen == 0) {
401287736Shrs		n = sdl->sdl_nlen;
402287736Shrs		if (n >= hostlen) {
403287736Shrs			*host = '\0';
404287736Shrs			return (EAI_MEMORY);
405287736Shrs		}
406287736Shrs		memcpy(host, sdl->sdl_data, sdl->sdl_nlen);
407287736Shrs		host[n] = '\0';
408287736Shrs		return (0);
409287736Shrs	}
410287736Shrs
411167121Sbms	switch (sdl->sdl_type) {
412199221Sume	case IFT_IEEE1394:
413199221Sume		if (sdl->sdl_alen < sizeof(iha->sender_unique_ID_hi) +
414199221Sume		    sizeof(iha->sender_unique_ID_lo))
415199221Sume			return EAI_FAMILY;
416199221Sume		iha = (const struct fw_hwaddr *)(const void *)LLADDR(sdl);
417199221Sume		return hexname((const u_int8_t *)&iha->sender_unique_ID_hi,
418199221Sume		    sizeof(iha->sender_unique_ID_hi) +
419199221Sume		    sizeof(iha->sender_unique_ID_lo),
420199221Sume		    host, hostlen);
421167121Sbms	/*
422167121Sbms	 * The following have zero-length addresses.
423167121Sbms	 * IFT_ATM	(net/if_atmsubr.c)
424167121Sbms	 * IFT_FAITH	(net/if_faith.c)
425167121Sbms	 * IFT_GIF	(net/if_gif.c)
426167121Sbms	 * IFT_LOOP	(net/if_loop.c)
427167121Sbms	 * IFT_PPP	(net/if_ppp.c, net/if_spppsubr.c)
428167121Sbms	 * IFT_SLIP	(net/if_sl.c, net/if_strip.c)
429167121Sbms	 * IFT_STF	(net/if_stf.c)
430167121Sbms	 * IFT_L2VLAN	(net/if_vlan.c)
431167121Sbms	 * IFT_BRIDGE (net/if_bridge.h>
432167121Sbms	 */
433167121Sbms	/*
434167121Sbms	 * The following use IPv4 addresses as link-layer addresses:
435167121Sbms	 * IFT_OTHER	(net/if_gre.c)
436167121Sbms	 * IFT_OTHER	(netinet/ip_ipip.c)
437167121Sbms	 */
438167121Sbms	/* default below is believed correct for all these. */
439167121Sbms	case IFT_ARCNET:
440167121Sbms	case IFT_ETHER:
441167121Sbms	case IFT_FDDI:
442167121Sbms	case IFT_HIPPI:
443167121Sbms	case IFT_ISO88025:
444167121Sbms	default:
445167121Sbms		return hexname((u_int8_t *)LLADDR(sdl), (size_t)sdl->sdl_alen,
446167121Sbms		    host, hostlen);
447167121Sbms	}
448167121Sbms}
449167121Sbms
450167121Sbmsstatic int
451287736Shrshexname(const u_int8_t *cp, size_t len, char *host, size_t hostlen)
452167121Sbms{
453167121Sbms	int i, n;
454167121Sbms	char *outp = host;
455167121Sbms
456167121Sbms	*outp = '\0';
457167121Sbms	for (i = 0; i < len; i++) {
458167121Sbms		n = snprintf(outp, hostlen, "%s%02x",
459167121Sbms		    i ? ":" : "", cp[i]);
460167121Sbms		if (n < 0 || n >= hostlen) {
461167121Sbms			*host = '\0';
462167121Sbms			return EAI_MEMORY;
463167121Sbms		}
464167121Sbms		outp += n;
465167121Sbms		hostlen -= n;
466167121Sbms	}
467167121Sbms	return 0;
468167121Sbms}
469