xref: /netbsd-src/lib/libc/net/getnameinfo.c (revision 03645a16a3042a08d4269219f215ba4b29b62567)
1*03645a16Schristos /*	$NetBSD: getnameinfo.c,v 1.60 2023/09/08 18:17:41 christos Exp $	*/
2a931ac82Sitojun /*	$KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $	*/
34620b004Sitojun 
437e81591Sitojun /*
5b1297979Sbjh21  * Copyright (c) 2000 Ben Harris.
637e81591Sitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
737e81591Sitojun  * All rights reserved.
837e81591Sitojun  *
937e81591Sitojun  * Redistribution and use in source and binary forms, with or without
1037e81591Sitojun  * modification, are permitted provided that the following conditions
1137e81591Sitojun  * are met:
1237e81591Sitojun  * 1. Redistributions of source code must retain the above copyright
1337e81591Sitojun  *    notice, this list of conditions and the following disclaimer.
1437e81591Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1537e81591Sitojun  *    notice, this list of conditions and the following disclaimer in the
1637e81591Sitojun  *    documentation and/or other materials provided with the distribution.
1737e81591Sitojun  * 3. Neither the name of the project nor the names of its contributors
1837e81591Sitojun  *    may be used to endorse or promote products derived from this software
1937e81591Sitojun  *    without specific prior written permission.
2037e81591Sitojun  *
2137e81591Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2237e81591Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2337e81591Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2437e81591Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2537e81591Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2637e81591Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2737e81591Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2837e81591Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2937e81591Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3037e81591Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3137e81591Sitojun  * SUCH DAMAGE.
3237e81591Sitojun  */
3337e81591Sitojun 
3437e81591Sitojun /*
3537e81591Sitojun  * Issues to be discussed:
3637e81591Sitojun  * - Thread safe-ness must be checked
371501f618Sitojun  * - RFC2553 says that we should raise error on short buffer.  X/Open says
381501f618Sitojun  *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
396acd1fa3Sitojun  *   modified).  ipngwg rough consensus seems to follow RFC2553.
403fc57d3fSitojun  * - What is "local" in NI_FQDN?
413fc57d3fSitojun  * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
426c9440f3Sitojun  * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
436c9440f3Sitojun  *   sin6_scope_id is filled - standardization status?
446c9440f3Sitojun  *   XXX breaks backward compat for code that expects no scopeid.
456c9440f3Sitojun  *   beware on merge.
4637e81591Sitojun  */
4737e81591Sitojun 
4872eddcacSitojun #include <sys/cdefs.h>
4972eddcacSitojun #if defined(LIBC_SCCS) && !defined(lint)
50*03645a16Schristos __RCSID("$NetBSD: getnameinfo.c,v 1.60 2023/09/08 18:17:41 christos Exp $");
5172eddcacSitojun #endif /* LIBC_SCCS and not lint */
5272eddcacSitojun 
536011d36bSozaki-r #ifndef RUMP_ACTION
54c6bf4b09Sitojun #include "namespace.h"
556011d36bSozaki-r #endif
5637e81591Sitojun #include <sys/types.h>
5737e81591Sitojun #include <sys/socket.h>
58911623fbSchristos #include <sys/un.h>
591501f618Sitojun #include <net/if.h>
60b1297979Sbjh21 #include <net/if_dl.h>
61b1297979Sbjh21 #include <net/if_ieee1394.h>
62b1297979Sbjh21 #include <net/if_types.h>
63000fbf6bSis #include <netatalk/at.h>
6437e81591Sitojun #include <netinet/in.h>
6537e81591Sitojun #include <arpa/inet.h>
6637e81591Sitojun #include <arpa/nameser.h>
67a976c8e3Slukem #include <assert.h>
68b1297979Sbjh21 #include <limits.h>
6937e81591Sitojun #include <netdb.h>
7037e81591Sitojun #include <resolv.h>
7137e81591Sitojun #include <stddef.h>
72a976c8e3Slukem #include <string.h>
7337e81591Sitojun 
741463de6aSchristos #include "servent.h"
7502dd2447Schristos #include "hostent.h"
761463de6aSchristos 
775d88ea0bSjoerg #ifndef RUMP_ACTION
78c6bf4b09Sitojun #ifdef __weak_alias
79c6bf4b09Sitojun __weak_alias(getnameinfo,_getnameinfo)
80c6bf4b09Sitojun #endif
815d88ea0bSjoerg #endif
82c6bf4b09Sitojun 
83ca797c3cSjdolecek static const struct afd {
8437e81591Sitojun 	int		a_af;
85a51e2828Skleink 	socklen_t	a_addrlen;
86a51e2828Skleink 	socklen_t	a_socklen;
8737e81591Sitojun 	int		a_off;
8837e81591Sitojun } afdl [] = {
8937e81591Sitojun #ifdef INET6
9037e81591Sitojun 	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
9137e81591Sitojun 		offsetof(struct sockaddr_in6, sin6_addr)},
9237e81591Sitojun #endif
9337e81591Sitojun 	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
9437e81591Sitojun 		offsetof(struct sockaddr_in, sin_addr)},
95ce2c90c7Schristos 	{0, 0, 0, 0},
9637e81591Sitojun };
9737e81591Sitojun 
9837e81591Sitojun struct sockinet {
9937e81591Sitojun 	u_char	si_len;
10037e81591Sitojun 	u_char	si_family;
10137e81591Sitojun 	u_short	si_port;
10237e81591Sitojun };
10337e81591Sitojun 
104504f8671Smatt static int getnameinfo_inet(const struct sockaddr *, socklen_t, char *,
105504f8671Smatt     socklen_t, char *, socklen_t, int);
10634a6354bSitojun #ifdef INET6
107504f8671Smatt static int ip6_parsenumeric(const struct sockaddr *, const char *, char *,
108504f8671Smatt 				 socklen_t, int);
109504f8671Smatt static int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int);
11034a6354bSitojun #endif
111504f8671Smatt static int getnameinfo_atalk(const struct sockaddr *, socklen_t, char *,
112504f8671Smatt     socklen_t, char *, socklen_t, int);
113911623fbSchristos static int getnameinfo_local(const struct sockaddr *, socklen_t, char *,
114911623fbSchristos     socklen_t, char *, socklen_t, int);
115000fbf6bSis 
116504f8671Smatt static int getnameinfo_link(const struct sockaddr *, socklen_t, char *,
117504f8671Smatt     socklen_t, char *, socklen_t, int);
118504f8671Smatt static int hexname(const uint8_t *, size_t, char *, socklen_t);
11934a6354bSitojun 
120b1297979Sbjh21 /*
121b1297979Sbjh21  * Top-level getnameinfo() code.  Look at the address family, and pick an
122b1297979Sbjh21  * appropriate function to call.
123b1297979Sbjh21  */
12437e81591Sitojun int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)125504f8671Smatt getnameinfo(const struct sockaddr *sa, socklen_t salen,
126504f8671Smatt 	char *host, socklen_t hostlen,
127504f8671Smatt 	char *serv, socklen_t servlen,
128504f8671Smatt 	int flags)
129b1297979Sbjh21 {
130b1297979Sbjh21 
131*03645a16Schristos 	/*
132*03645a16Schristos 	 * getnameinfo() accepts an salen of sizeof(struct sockaddr_storage)
133*03645a16Schristos 	 * at maximum as shown in RFC 4038 Sec.6.2.3.
134*03645a16Schristos 	 */
135*03645a16Schristos 	if (salen > sizeof(struct sockaddr_storage))
136*03645a16Schristos 		return EAI_FAMILY;
137*03645a16Schristos 
138b1297979Sbjh21 	switch (sa->sa_family) {
139000fbf6bSis 	case AF_APPLETALK:
140000fbf6bSis 		return getnameinfo_atalk(sa, salen, host, hostlen,
141000fbf6bSis 		    serv, servlen, flags);
142b1297979Sbjh21 	case AF_INET:
143b1297979Sbjh21 	case AF_INET6:
144b1297979Sbjh21 		return getnameinfo_inet(sa, salen, host, hostlen,
145b1297979Sbjh21 		    serv, servlen, flags);
146b1297979Sbjh21 	case AF_LINK:
147b1297979Sbjh21 		return getnameinfo_link(sa, salen, host, hostlen,
148b1297979Sbjh21 		    serv, servlen, flags);
149911623fbSchristos 	case AF_LOCAL:
150911623fbSchristos 		return getnameinfo_local(sa, salen, host, hostlen,
151911623fbSchristos 		    serv, servlen, flags);
152b1297979Sbjh21 	default:
153b1297979Sbjh21 		return EAI_FAMILY;
154b1297979Sbjh21 	}
155b1297979Sbjh21 }
156b1297979Sbjh21 
157000fbf6bSis /*
158000fbf6bSis  * getnameinfo_atalk():
159000fbf6bSis  * Format an AppleTalk address into a printable format.
160000fbf6bSis  */
161000fbf6bSis /* ARGSUSED */
162000fbf6bSis static int
getnameinfo_atalk(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)163000fbf6bSis getnameinfo_atalk(const struct sockaddr *sa, socklen_t salen,
164000fbf6bSis     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
165000fbf6bSis     int flags)
166000fbf6bSis {
167000fbf6bSis 	char numserv[8];
16845845cf0Sis 	int n, m=0;
169000fbf6bSis 
170000fbf6bSis 	const struct sockaddr_at *sat =
171000fbf6bSis 	    (const struct sockaddr_at *)(const void *)sa;
172000fbf6bSis 
173000fbf6bSis 	if (serv != NULL && servlen > 0) {
174000fbf6bSis 		snprintf(numserv, sizeof(numserv), "%u", sat->sat_port);
175000fbf6bSis 		if (strlen(numserv) + 1 > servlen)
176000fbf6bSis 			return EAI_MEMORY;
177000fbf6bSis 		strlcpy(serv, numserv, servlen);
178000fbf6bSis 	}
179000fbf6bSis 
180000fbf6bSis         n = snprintf(host, hostlen, "%u.%u",
181000fbf6bSis 	    ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
18245845cf0Sis 
18345845cf0Sis 	if (n < 0 || (socklen_t)(m+n) >= hostlen)
18445845cf0Sis 		goto errout;
18545845cf0Sis 
18645845cf0Sis 	m += n;
18745845cf0Sis 
18845845cf0Sis 	if (sat->sat_range.r_netrange.nr_phase) {
18945845cf0Sis         	n = snprintf(host+m, hostlen-m, " phase %u",
19045845cf0Sis 			sat->sat_range.r_netrange.nr_phase);
19145845cf0Sis 
19245845cf0Sis 		if (n < 0 || (socklen_t)(m+n) >= hostlen)
19345845cf0Sis 			goto errout;
19445845cf0Sis 
19545845cf0Sis 		m += n;
19645845cf0Sis 	}
19745845cf0Sis 	if (sat->sat_range.r_netrange.nr_firstnet) {
19845845cf0Sis         	n = snprintf(host+m, hostlen-m, " range %u - %u",
19945845cf0Sis 			ntohs(sat->sat_range.r_netrange.nr_firstnet),
20045845cf0Sis 			ntohs(sat->sat_range.r_netrange.nr_lastnet ));
20145845cf0Sis 
20245845cf0Sis 		if (n < 0 || (socklen_t)(m+n) >= hostlen)
20345845cf0Sis 			goto errout;
20445845cf0Sis 
20545845cf0Sis 		m += n;
206000fbf6bSis 	}
207000fbf6bSis 
208000fbf6bSis 	return 0;
20945845cf0Sis 
21045845cf0Sis errout:
21145845cf0Sis 	if (host && hostlen>0)
21245845cf0Sis 		host[m] = '\0';	/* XXX ??? */
21345845cf0Sis 
21445845cf0Sis 	return EAI_MEMORY;
215000fbf6bSis }
216b1297979Sbjh21 
217b1297979Sbjh21 /*
218911623fbSchristos  * getnameinfo_local():
219911623fbSchristos  * Format an local address into a printable format.
220911623fbSchristos  */
221911623fbSchristos /* ARGSUSED */
222911623fbSchristos static int
getnameinfo_local(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)223911623fbSchristos getnameinfo_local(const struct sockaddr *sa, socklen_t salen,
224911623fbSchristos     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
225911623fbSchristos     int flags)
226911623fbSchristos {
227911623fbSchristos 	const struct sockaddr_un *sun =
228911623fbSchristos 	    (const struct sockaddr_un *)(const void *)sa;
229911623fbSchristos 
230*03645a16Schristos         if (salen <= sizeof(*sun) - sizeof(sun->sun_path))
231*03645a16Schristos 		return EAI_FAMILY;
232*03645a16Schristos 
233911623fbSchristos 	if (serv != NULL && servlen > 0)
234911623fbSchristos 		serv[0] = '\0';
235911623fbSchristos 
236911623fbSchristos 	if (host && hostlen > 0)
237911623fbSchristos 		strlcpy(host, sun->sun_path,
238911623fbSchristos 		    MIN(sizeof(sun->sun_path) + 1, hostlen));
239911623fbSchristos 
240911623fbSchristos 	return 0;
241911623fbSchristos }
242911623fbSchristos 
243911623fbSchristos /*
244b1297979Sbjh21  * getnameinfo_inet():
245b1297979Sbjh21  * Format an IPv4 or IPv6 sockaddr into a printable string.
246b1297979Sbjh21  */
247b1297979Sbjh21 static int
getnameinfo_inet(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)248504f8671Smatt getnameinfo_inet(const struct sockaddr *sa, socklen_t salen,
249504f8671Smatt 	char *host, socklen_t hostlen,
250504f8671Smatt 	char *serv, socklen_t servlen,
251504f8671Smatt 	int flags)
25237e81591Sitojun {
253ca797c3cSjdolecek 	const struct afd *afd;
25437e81591Sitojun 	struct servent *sp;
25537e81591Sitojun 	struct hostent *hp;
25637e81591Sitojun 	u_short port;
2571501f618Sitojun 	int family, i;
2583fc57d3fSitojun 	const char *addr;
259504f8671Smatt 	uint32_t v4a;
26037e81591Sitojun 	char numserv[512];
26137e81591Sitojun 	char numaddr[512];
26237e81591Sitojun 
263a976c8e3Slukem 	/* sa is checked below */
264a976c8e3Slukem 	/* host may be NULL */
265a976c8e3Slukem 	/* serv may be NULL */
266a976c8e3Slukem 
26737e81591Sitojun 	if (sa == NULL)
26872a8edddSitojun 		return EAI_FAIL;
26937e81591Sitojun 
27037e81591Sitojun 	family = sa->sa_family;
27137e81591Sitojun 	for (i = 0; afdl[i].a_af; i++)
27237e81591Sitojun 		if (afdl[i].a_af == family) {
27337e81591Sitojun 			afd = &afdl[i];
27437e81591Sitojun 			goto found;
27537e81591Sitojun 		}
27672a8edddSitojun 	return EAI_FAMILY;
27737e81591Sitojun 
27837e81591Sitojun  found:
279*03645a16Schristos 	if (salen < afd->a_socklen)
280*03645a16Schristos 		return EAI_FAMILY;
28137e81591Sitojun 
2823fc57d3fSitojun 	/* network byte order */
2837bc5ea81Schristos 	port = ((const struct sockinet *)(const void *)sa)->si_port;
2847bc5ea81Schristos 	addr = (const char *)(const void *)sa + afd->a_off;
28537e81591Sitojun 
28637e81591Sitojun 	if (serv == NULL || servlen == 0) {
2871501f618Sitojun 		/*
2881501f618Sitojun 		 * do nothing in this case.
2891501f618Sitojun 		 * in case you are wondering if "&&" is more correct than
29072a8edddSitojun 		 * "||" here: rfc2553bis-03 says that serv == NULL OR
29172a8edddSitojun 		 * servlen == 0 means that the caller does not want the result.
2921501f618Sitojun 		 */
29337e81591Sitojun 	} else {
2941463de6aSchristos 		struct servent_data svd;
2951463de6aSchristos 		struct servent sv;
2961463de6aSchristos 
297612dfc4dSseanb 		if (flags & NI_NUMERICSERV)
298612dfc4dSseanb 			sp = NULL;
299612dfc4dSseanb 		else {
3001463de6aSchristos 			(void)memset(&svd, 0, sizeof(svd));
3011463de6aSchristos 			sp = getservbyport_r(port,
3021463de6aSchristos 				(flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd);
3031501f618Sitojun 		}
30437e81591Sitojun 		if (sp) {
305612dfc4dSseanb 			if (strlen(sp->s_name) + 1 > servlen) {
306612dfc4dSseanb 				endservent_r(&svd);
30772a8edddSitojun 				return EAI_MEMORY;
308612dfc4dSseanb 			}
309b617695bSitojun 			strlcpy(serv, sp->s_name, servlen);
310612dfc4dSseanb 			endservent_r(&svd);
3111501f618Sitojun 		} else {
3120c63dd3cSitojun 			snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
313a931ac82Sitojun 			if (strlen(numserv) + 1 > servlen)
31472a8edddSitojun 				return EAI_MEMORY;
315b617695bSitojun 			strlcpy(serv, numserv, servlen);
3161501f618Sitojun 		}
31737e81591Sitojun 	}
31837e81591Sitojun 
31937e81591Sitojun 	switch (sa->sa_family) {
32037e81591Sitojun 	case AF_INET:
321504f8671Smatt 		v4a = (uint32_t)
3227bc5ea81Schristos 		    ntohl(((const struct sockaddr_in *)
3237bc5ea81Schristos 		    (const void *)sa)->sin_addr.s_addr);
32437e81591Sitojun 		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
32537e81591Sitojun 			flags |= NI_NUMERICHOST;
32637e81591Sitojun 		v4a >>= IN_CLASSA_NSHIFT;
3272cd2ee94Sitojun 		if (v4a == 0)
32837e81591Sitojun 			flags |= NI_NUMERICHOST;
32937e81591Sitojun 		break;
33037e81591Sitojun #ifdef INET6
33137e81591Sitojun 	case AF_INET6:
332bc3bad7aSitojun 	    {
3333fc57d3fSitojun 		const struct sockaddr_in6 *sin6;
3347bc5ea81Schristos 		sin6 = (const struct sockaddr_in6 *)(const void *)sa;
3351501f618Sitojun 		switch (sin6->sin6_addr.s6_addr[0]) {
336bc3bad7aSitojun 		case 0x00:
337bc3bad7aSitojun 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
338bc3bad7aSitojun 				;
339bc3bad7aSitojun 			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
340bc3bad7aSitojun 				;
341bc3bad7aSitojun 			else
34237e81591Sitojun 				flags |= NI_NUMERICHOST;
34337e81591Sitojun 			break;
344bc3bad7aSitojun 		default:
3451501f618Sitojun 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
346bc3bad7aSitojun 				flags |= NI_NUMERICHOST;
3471501f618Sitojun 			}
348bc3bad7aSitojun 			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
349bc3bad7aSitojun 				flags |= NI_NUMERICHOST;
350bc3bad7aSitojun 			break;
351bc3bad7aSitojun 		}
352bc3bad7aSitojun 	    }
353bc3bad7aSitojun 		break;
35437e81591Sitojun #endif
35537e81591Sitojun 	}
35637e81591Sitojun 	if (host == NULL || hostlen == 0) {
3571501f618Sitojun 		/*
3581501f618Sitojun 		 * do nothing in this case.
3591501f618Sitojun 		 * in case you are wondering if "&&" is more correct than
36072a8edddSitojun 		 * "||" here: rfc2553bis-03 says that host == NULL or
36172a8edddSitojun 		 * hostlen == 0 means that the caller does not want the result.
3621501f618Sitojun 		 */
36337e81591Sitojun 	} else if (flags & NI_NUMERICHOST) {
364585ad39aSthorpej 		size_t numaddrlen;
36534a6354bSitojun 
366bc3bad7aSitojun 		/* NUMERICHOST and NAMEREQD conflicts with each other */
367bc3bad7aSitojun 		if (flags & NI_NAMEREQD)
36872a8edddSitojun 			return EAI_NONAME;
3693fc57d3fSitojun 
3703fc57d3fSitojun 		switch(afd->a_af) {
3713fc57d3fSitojun #ifdef INET6
3723fc57d3fSitojun 		case AF_INET6:
3733fc57d3fSitojun 		{
3743fc57d3fSitojun 			int error;
3753fc57d3fSitojun 
3763fc57d3fSitojun 			if ((error = ip6_parsenumeric(sa, addr, host,
3773fc57d3fSitojun 						      hostlen, flags)) != 0)
3783fc57d3fSitojun 				return(error);
3793fc57d3fSitojun 			break;
3803fc57d3fSitojun 		}
3813fc57d3fSitojun #endif
3823fc57d3fSitojun 		default:
383c5e820caSchristos 			if (inet_ntop(afd->a_af, addr, numaddr,
384c5e820caSchristos 			    (socklen_t)sizeof(numaddr)) == NULL)
38572a8edddSitojun 				return EAI_SYSTEM;
38634a6354bSitojun 			numaddrlen = strlen(numaddr);
38734a6354bSitojun 			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
38872a8edddSitojun 				return EAI_MEMORY;
389b617695bSitojun 			strlcpy(host, numaddr, hostlen);
3903fc57d3fSitojun 			break;
3911501f618Sitojun 		}
39237e81591Sitojun 	} else {
39302dd2447Schristos 		struct hostent hent;
39402dd2447Schristos 		char hbuf[4096];
39502dd2447Schristos 		int he;
39602dd2447Schristos 		hp = gethostbyaddr_r(addr, afd->a_addrlen, afd->a_af, &hent,
39702dd2447Schristos 		    hbuf, sizeof(hbuf), &he);
39837e81591Sitojun 
39937e81591Sitojun 		if (hp) {
4003fc57d3fSitojun #if 0
4013fc57d3fSitojun 			/*
4023fc57d3fSitojun 			 * commented out, since "for local host" is not
4033fc57d3fSitojun 			 * implemented here - see RFC2553 p30
4043fc57d3fSitojun 			 */
40537e81591Sitojun 			if (flags & NI_NOFQDN) {
4063fc57d3fSitojun 				char *p;
40737e81591Sitojun 				p = strchr(hp->h_name, '.');
4083fc57d3fSitojun 				if (p)
4093fc57d3fSitojun 					*p = '\0';
41037e81591Sitojun 			}
4113fc57d3fSitojun #endif
412a931ac82Sitojun 			if (strlen(hp->h_name) + 1 > hostlen) {
41372a8edddSitojun 				return EAI_MEMORY;
41437e81591Sitojun 			}
415b617695bSitojun 			strlcpy(host, hp->h_name, hostlen);
41637e81591Sitojun 		} else {
417b350f7a4Schristos 			switch (he) {
418b350f7a4Schristos 			case NO_DATA:
419b350f7a4Schristos 			case HOST_NOT_FOUND:
42037e81591Sitojun 				if (flags & NI_NAMEREQD)
42172a8edddSitojun 					return EAI_NONAME;
422b350f7a4Schristos 				break;
423b350f7a4Schristos 			case TRY_AGAIN:
424b350f7a4Schristos 				return EAI_AGAIN;
425b350f7a4Schristos 			case NETDB_SUCCESS:
426525756d3Schristos 			case NETDB_INTERNAL:
427525756d3Schristos 			case NO_RECOVERY:
428b350f7a4Schristos 				/*FALLTHROUGH*/
429b350f7a4Schristos 			default:
430525756d3Schristos 				return EAI_SYSTEM;
431b350f7a4Schristos 			}
4323fc57d3fSitojun 			switch(afd->a_af) {
4333fc57d3fSitojun #ifdef INET6
4343fc57d3fSitojun 			case AF_INET6:
4353fc57d3fSitojun 			{
4363fc57d3fSitojun 				int error;
4373fc57d3fSitojun 
4383fc57d3fSitojun 				if ((error = ip6_parsenumeric(sa, addr, host,
4393fc57d3fSitojun 							      hostlen,
4403fc57d3fSitojun 							      flags)) != 0)
4413fc57d3fSitojun 					return(error);
4423fc57d3fSitojun 				break;
4433fc57d3fSitojun 			}
4443fc57d3fSitojun #endif
4453fc57d3fSitojun 			default:
4463fc57d3fSitojun 				if (inet_ntop(afd->a_af, addr, host,
4473fc57d3fSitojun 				    hostlen) == NULL)
44872a8edddSitojun 					return EAI_SYSTEM;
4493fc57d3fSitojun 				break;
4503fc57d3fSitojun 			}
45137e81591Sitojun 		}
45237e81591Sitojun 	}
45372a8edddSitojun 	return(0);
45437e81591Sitojun }
45534a6354bSitojun 
45634a6354bSitojun #ifdef INET6
4573fc57d3fSitojun static int
ip6_parsenumeric(const struct sockaddr * sa,const char * addr,char * host,socklen_t hostlen,int flags)458504f8671Smatt ip6_parsenumeric(const struct sockaddr *sa, const char *addr, char *host,
459504f8671Smatt 	socklen_t hostlen, int flags)
4603fc57d3fSitojun {
461585ad39aSthorpej 	size_t numaddrlen;
4623fc57d3fSitojun 	char numaddr[512];
4633fc57d3fSitojun 
464a976c8e3Slukem 	_DIAGASSERT(sa != NULL);
465a976c8e3Slukem 	_DIAGASSERT(addr != NULL);
466a976c8e3Slukem 	_DIAGASSERT(host != NULL);
467a976c8e3Slukem 
468c5e820caSchristos 	if (inet_ntop(AF_INET6, addr, numaddr, (socklen_t)sizeof(numaddr))
469c5e820caSchristos 	    == NULL)
47072a8edddSitojun 		return EAI_SYSTEM;
4713fc57d3fSitojun 
4723fc57d3fSitojun 	numaddrlen = strlen(numaddr);
4733fc57d3fSitojun 	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
47480194aa8Sginsbach 		return EAI_OVERFLOW;
475b617695bSitojun 	strlcpy(host, numaddr, hostlen);
4763fc57d3fSitojun 
4777bc5ea81Schristos 	if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) {
4786c9440f3Sitojun 		char zonebuf[MAXHOSTNAMELEN];
4796c9440f3Sitojun 		int zonelen;
4803fc57d3fSitojun 
4816c9440f3Sitojun 		zonelen = ip6_sa2str(
4827bc5ea81Schristos 		    (const struct sockaddr_in6 *)(const void *)sa,
4836c9440f3Sitojun 		    zonebuf, sizeof(zonebuf), flags);
4846c9440f3Sitojun 		if (zonelen < 0)
48580194aa8Sginsbach 			return EAI_OVERFLOW;
486585ad39aSthorpej 		if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen)
48780194aa8Sginsbach 			return EAI_OVERFLOW;
4886c9440f3Sitojun 		/* construct <numeric-addr><delim><zoneid> */
4896c9440f3Sitojun 		memcpy(host + numaddrlen + 1, zonebuf,
4906c9440f3Sitojun 		    (size_t)zonelen);
4913fc57d3fSitojun 		host[numaddrlen] = SCOPE_DELIMITER;
4926c9440f3Sitojun 		host[numaddrlen + 1 + zonelen] = '\0';
4933fc57d3fSitojun 	}
4943fc57d3fSitojun 
4953fc57d3fSitojun 	return 0;
4963fc57d3fSitojun }
4973fc57d3fSitojun 
49834a6354bSitojun /* ARGSUSED */
4993fc57d3fSitojun static int
ip6_sa2str(const struct sockaddr_in6 * sa6,char * buf,size_t bufsiz,int flags)500504f8671Smatt ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, size_t bufsiz, int flags)
50134a6354bSitojun {
502a976c8e3Slukem 	unsigned int ifindex;
503a976c8e3Slukem 	const struct in6_addr *a6;
504511e898dSitojun 	int n;
505a976c8e3Slukem 
506a976c8e3Slukem 	_DIAGASSERT(sa6 != NULL);
507a976c8e3Slukem 	_DIAGASSERT(buf != NULL);
508a976c8e3Slukem 
509a976c8e3Slukem 	ifindex = (unsigned int)sa6->sin6_scope_id;
510a976c8e3Slukem 	a6 = &sa6->sin6_addr;
51134a6354bSitojun 
512511e898dSitojun #ifdef NI_NUMERICSCOPE
513511e898dSitojun 	if ((flags & NI_NUMERICSCOPE) != 0) {
514511e898dSitojun 		n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
515da77f10aSlukem 		if (n < 0 || (size_t)n >= bufsiz)
516511e898dSitojun 			return -1;
517511e898dSitojun 		else
518511e898dSitojun 			return n;
51934a6354bSitojun 	}
52034a6354bSitojun #endif
52134a6354bSitojun 
5223fc57d3fSitojun 	/* if_indextoname() does not take buffer size.  not a good api... */
5233fc57d3fSitojun 	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
5243fc57d3fSitojun 	    bufsiz >= IF_NAMESIZE) {
5253fc57d3fSitojun 		char *p = if_indextoname(ifindex, buf);
5263fc57d3fSitojun 		if (p) {
527c5e820caSchristos 			return (int)strlen(p);
52834a6354bSitojun 		}
5293fc57d3fSitojun 	}
5303fc57d3fSitojun 
5313fc57d3fSitojun 	/* last resort */
532511e898dSitojun 	n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
533585ad39aSthorpej 	if (n < 0 || (size_t) n >= bufsiz)
534511e898dSitojun 		return -1;
535511e898dSitojun 	else
536511e898dSitojun 		return n;
5373fc57d3fSitojun }
5383fc57d3fSitojun #endif /* INET6 */
539b1297979Sbjh21 
540b1297979Sbjh21 
541b1297979Sbjh21 /*
542b1297979Sbjh21  * getnameinfo_link():
543b1297979Sbjh21  * Format a link-layer address into a printable format, paying attention to
544b1297979Sbjh21  * the interface type.
545b1297979Sbjh21  */
546b1297979Sbjh21 /* ARGSUSED */
547b1297979Sbjh21 static int
getnameinfo_link(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)548b1297979Sbjh21 getnameinfo_link(const struct sockaddr *sa, socklen_t salen,
5494b77e72eSkleink     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
550a24f74d0Skleink     int flags)
551b1297979Sbjh21 {
552b1297979Sbjh21 	const struct sockaddr_dl *sdl =
553b1297979Sbjh21 	    (const struct sockaddr_dl *)(const void *)sa;
554b1297979Sbjh21 	const struct ieee1394_hwaddr *iha;
555b1297979Sbjh21 	int n;
556b1297979Sbjh21 
557*03645a16Schristos         if (salen <= sizeof(*sdl) - sizeof(sdl->sdl_data))
558*03645a16Schristos 		return EAI_FAMILY;
559*03645a16Schristos 
560b1297979Sbjh21 	if (serv != NULL && servlen > 0)
561b1297979Sbjh21 		*serv = '\0';
562b1297979Sbjh21 
563b1297979Sbjh21 	if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) {
5640c63dd3cSitojun 		n = snprintf(host, hostlen, "link#%u", sdl->sdl_index);
56533eb4d86Schristos 		goto out;
566b1297979Sbjh21 	}
567b1297979Sbjh21 
568b1297979Sbjh21 	switch (sdl->sdl_type) {
569b1297979Sbjh21 #ifdef IFT_ECONET
570b1297979Sbjh21 	case IFT_ECONET:
571b1297979Sbjh21 		if (sdl->sdl_alen < 2)
572b1297979Sbjh21 			return EAI_FAMILY;
57303256c6eSchristos 		if (CLLADDR(sdl)[1] == 0)
57403256c6eSchristos 			n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]);
575b1297979Sbjh21 		else
5760c63dd3cSitojun 			n = snprintf(host, hostlen, "%u.%u",
57703256c6eSchristos 			    CLLADDR(sdl)[1], CLLADDR(sdl)[0]);
57833eb4d86Schristos 		goto out;
579b1297979Sbjh21 #endif
580b1297979Sbjh21 	case IFT_IEEE1394:
581b1297979Sbjh21 		if (sdl->sdl_alen < sizeof(iha->iha_uid))
582b1297979Sbjh21 			return EAI_FAMILY;
583b1297979Sbjh21 		iha =
58403256c6eSchristos 		    (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl);
585b1297979Sbjh21 		return hexname(iha->iha_uid, sizeof(iha->iha_uid),
586b1297979Sbjh21 		    host, hostlen);
587b1297979Sbjh21 	/*
588b1297979Sbjh21 	 * The following have zero-length addresses.
589b1297979Sbjh21 	 * IFT_ATM	(net/if_atmsubr.c)
590b1297979Sbjh21 	 * IFT_FAITH	(net/if_faith.c)
591b1297979Sbjh21 	 * IFT_GIF	(net/if_gif.c)
592b1297979Sbjh21 	 * IFT_LOOP	(net/if_loop.c)
593b1297979Sbjh21 	 * IFT_PPP	(net/if_ppp.c, net/if_spppsubr.c)
594b1297979Sbjh21 	 * IFT_SLIP	(net/if_sl.c, net/if_strip.c)
595b1297979Sbjh21 	 * IFT_STF	(net/if_stf.c)
596b1297979Sbjh21 	 * IFT_L2VLAN	(net/if_vlan.c)
597b1297979Sbjh21 	 * IFT_PROPVIRTUAL (net/if_bridge.h>
598b1297979Sbjh21 	 */
599b1297979Sbjh21 	/*
600b1297979Sbjh21 	 * The following use IPv4 addresses as link-layer addresses:
601b1297979Sbjh21 	 * IFT_OTHER	(net/if_gre.c)
602b1297979Sbjh21 	 */
603b1297979Sbjh21 	case IFT_ARCNET: /* default below is believed correct for all these. */
604b1297979Sbjh21 	case IFT_ETHER:
605b1297979Sbjh21 	case IFT_FDDI:
606b1297979Sbjh21 	case IFT_HIPPI:
607b1297979Sbjh21 	case IFT_ISO88025:
608b1297979Sbjh21 	default:
609504f8671Smatt 		return hexname((const uint8_t *)CLLADDR(sdl),
61003256c6eSchristos 		    (size_t)sdl->sdl_alen, host, hostlen);
611b1297979Sbjh21 	}
61233eb4d86Schristos out:
61333eb4d86Schristos 	if (n < 0 || (socklen_t) n >= hostlen) {
61433eb4d86Schristos 		*host = '\0';
61533eb4d86Schristos 		return EAI_MEMORY;
61633eb4d86Schristos 	}
61733eb4d86Schristos 	return 0;
618b1297979Sbjh21 }
619b1297979Sbjh21 
620b1297979Sbjh21 static int
hexname(const uint8_t * cp,size_t len,char * host,socklen_t hostlen)621504f8671Smatt hexname(const uint8_t *cp, size_t len, char *host, socklen_t hostlen)
622b1297979Sbjh21 {
623585ad39aSthorpej 	int n;
624585ad39aSthorpej 	size_t i;
625b1297979Sbjh21 	char *outp = host;
626b1297979Sbjh21 
627b1297979Sbjh21 	*outp = '\0';
628b1297979Sbjh21 	for (i = 0; i < len; i++) {
629b1297979Sbjh21 		n = snprintf(outp, hostlen, "%s%02x",
630b1297979Sbjh21 		    i ? ":" : "", cp[i]);
631585ad39aSthorpej 		if (n < 0 || (socklen_t) n >= hostlen) {
632b1297979Sbjh21 			*host = '\0';
633b1297979Sbjh21 			return EAI_MEMORY;
634b1297979Sbjh21 		}
635b1297979Sbjh21 		outp += n;
636b1297979Sbjh21 		hostlen -= n;
637b1297979Sbjh21 	}
638b1297979Sbjh21 	return 0;
639b1297979Sbjh21 }
640