xref: /minix3/lib/libc/net/getnameinfo.c (revision 6b8821515da8851628dd9dcd785dacf07085019b)
1 /*	$NetBSD: getnameinfo.c,v 1.50 2010/06/29 14:44:19 seanb Exp $	*/
2 /*	$KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $	*/
3 
4 /*
5  * Copyright (c) 2000 Ben Harris.
6  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /*
35  * Issues to be discussed:
36  * - Thread safe-ness must be checked
37  * - RFC2553 says that we should raise error on short buffer.  X/Open says
38  *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
39  *   modified).  ipngwg rough consensus seems to follow RFC2553.
40  * - What is "local" in NI_FQDN?
41  * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
42  * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
43  *   sin6_scope_id is filled - standardization status?
44  *   XXX breaks backward compat for code that expects no scopeid.
45  *   beware on merge.
46  */
47 
48 #include <sys/cdefs.h>
49 #if defined(LIBC_SCCS) && !defined(lint)
50 __RCSID("$NetBSD: getnameinfo.c,v 1.50 2010/06/29 14:44:19 seanb Exp $");
51 #endif /* LIBC_SCCS and not lint */
52 
53 #include "namespace.h"
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <net/if.h>
57 #ifndef __minix
58 #include <net/if_dl.h>
59 #include <net/if_ieee1394.h>
60 #include <net/if_types.h>
61 #include <netatalk/at.h>
62 #endif /* !__minix */
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65 #include <arpa/nameser.h>
66 #include <assert.h>
67 #include <limits.h>
68 #include <netdb.h>
69 #include <resolv.h>
70 #include <stddef.h>
71 #include <string.h>
72 
73 #include "servent.h"
74 
75 #ifdef __weak_alias
76 __weak_alias(getnameinfo,_getnameinfo)
77 #endif
78 
79 static const struct afd {
80 	int		a_af;
81 	socklen_t	a_addrlen;
82 	socklen_t	a_socklen;
83 	int		a_off;
84 } afdl [] = {
85 #ifdef INET6
86 	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
87 		offsetof(struct sockaddr_in6, sin6_addr)},
88 #endif
89 	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
90 		offsetof(struct sockaddr_in, sin_addr)},
91 	{0, 0, 0, 0},
92 };
93 
94 struct sockinet {
95 	u_char	si_len;
96 	u_char	si_family;
97 	u_short	si_port;
98 };
99 
100 static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *,
101     socklen_t, char *, socklen_t, int));
102 #ifdef INET6
103 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
104 				 socklen_t, int));
105 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t,
106 				 int));
107 #endif
108 #ifndef __minix
109 static int getnameinfo_atalk __P((const struct sockaddr *, socklen_t, char *,
110     socklen_t, char *, socklen_t, int));
111 
112 static int getnameinfo_link __P((const struct sockaddr *, socklen_t, char *,
113     socklen_t, char *, socklen_t, int));
114 static int hexname __P((const u_int8_t *, size_t, char *, socklen_t));
115 #endif /* __minix */
116 
117 /*
118  * Top-level getnameinfo() code.  Look at the address family, and pick an
119  * appropriate function to call.
120  */
121 int
122 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
123 	const struct sockaddr *sa;
124 	socklen_t salen;
125 	char *host, *serv;
126 	socklen_t hostlen, servlen;
127 	int flags;
128 {
129 
130 	switch (sa->sa_family) {
131 #ifndef __minix
132 	case AF_APPLETALK:
133 		return getnameinfo_atalk(sa, salen, host, hostlen,
134 		    serv, servlen, flags);
135 #endif /* !__minix */
136 	case AF_INET:
137 	case AF_INET6:
138 		return getnameinfo_inet(sa, salen, host, hostlen,
139 		    serv, servlen, flags);
140 #ifndef __minix
141 	case AF_LINK:
142 		return getnameinfo_link(sa, salen, host, hostlen,
143 		    serv, servlen, flags);
144 #endif /* !__minix */
145 	default:
146 		return EAI_FAMILY;
147 	}
148 }
149 
150 #ifndef __minix
151 /*
152  * getnameinfo_atalk():
153  * Format an AppleTalk address into a printable format.
154  */
155 /* ARGSUSED */
156 static int
157 getnameinfo_atalk(const struct sockaddr *sa, socklen_t salen,
158     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
159     int flags)
160 {
161 	char numserv[8];
162 	int n, m=0;
163 
164 	const struct sockaddr_at *sat =
165 	    (const struct sockaddr_at *)(const void *)sa;
166 
167 	if (serv != NULL && servlen > 0) {
168 		snprintf(numserv, sizeof(numserv), "%u", sat->sat_port);
169 		if (strlen(numserv) + 1 > servlen)
170 			return EAI_MEMORY;
171 		strlcpy(serv, numserv, servlen);
172 	}
173 
174         n = snprintf(host, hostlen, "%u.%u",
175 	    ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
176 
177 	if (n < 0 || (socklen_t)(m+n) >= hostlen)
178 		goto errout;
179 
180 	m += n;
181 
182 	if (sat->sat_range.r_netrange.nr_phase) {
183         	n = snprintf(host+m, hostlen-m, " phase %u",
184 			sat->sat_range.r_netrange.nr_phase);
185 
186 		if (n < 0 || (socklen_t)(m+n) >= hostlen)
187 			goto errout;
188 
189 		m += n;
190 	}
191 	if (sat->sat_range.r_netrange.nr_firstnet) {
192         	n = snprintf(host+m, hostlen-m, " range %u - %u",
193 			ntohs(sat->sat_range.r_netrange.nr_firstnet),
194 			ntohs(sat->sat_range.r_netrange.nr_lastnet ));
195 
196 		if (n < 0 || (socklen_t)(m+n) >= hostlen)
197 			goto errout;
198 
199 		m += n;
200 	}
201 
202 	return 0;
203 
204 errout:
205 	if (host && hostlen>0)
206 		host[m] = '\0';	/* XXX ??? */
207 
208 	return EAI_MEMORY;
209 }
210 #endif /* !__minix */
211 
212 /*
213  * getnameinfo_inet():
214  * Format an IPv4 or IPv6 sockaddr into a printable string.
215  */
216 static int
217 getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
218 	const struct sockaddr *sa;
219 	socklen_t salen;
220 	char *host;
221 	socklen_t hostlen;
222 	char *serv;
223 	socklen_t servlen;
224 	int flags;
225 {
226 	const struct afd *afd;
227 	struct servent *sp;
228 	struct hostent *hp;
229 	u_short port;
230 	int family, i;
231 	const char *addr;
232 	u_int32_t v4a;
233 	char numserv[512];
234 	char numaddr[512];
235 
236 	/* sa is checked below */
237 	/* host may be NULL */
238 	/* serv may be NULL */
239 
240 	if (sa == NULL)
241 		return EAI_FAIL;
242 
243 	family = sa->sa_family;
244 	for (i = 0; afdl[i].a_af; i++)
245 		if (afdl[i].a_af == family) {
246 			afd = &afdl[i];
247 			goto found;
248 		}
249 	return EAI_FAMILY;
250 
251  found:
252 	if (salen != afd->a_socklen)
253 		return EAI_FAIL;
254 
255 	/* network byte order */
256 	port = ((const struct sockinet *)(const void *)sa)->si_port;
257 	addr = (const char *)(const void *)sa + afd->a_off;
258 
259 	if (serv == NULL || servlen == 0) {
260 		/*
261 		 * do nothing in this case.
262 		 * in case you are wondering if "&&" is more correct than
263 		 * "||" here: rfc2553bis-03 says that serv == NULL OR
264 		 * servlen == 0 means that the caller does not want the result.
265 		 */
266 	} else {
267 		struct servent_data svd;
268 		struct servent sv;
269 
270 		if (flags & NI_NUMERICSERV)
271 			sp = NULL;
272 		else {
273 			(void)memset(&svd, 0, sizeof(svd));
274 			sp = getservbyport_r(port,
275 				(flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd);
276 		}
277 		if (sp) {
278 			if (strlen(sp->s_name) + 1 > servlen) {
279 				endservent_r(&svd);
280 				return EAI_MEMORY;
281 			}
282 			strlcpy(serv, sp->s_name, servlen);
283 			endservent_r(&svd);
284 		} else {
285 			snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
286 			if (strlen(numserv) + 1 > servlen)
287 				return EAI_MEMORY;
288 			strlcpy(serv, numserv, servlen);
289 		}
290 	}
291 
292 	switch (sa->sa_family) {
293 	case AF_INET:
294 		v4a = (u_int32_t)
295 		    ntohl(((const struct sockaddr_in *)
296 		    (const void *)sa)->sin_addr.s_addr);
297 		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
298 			flags |= NI_NUMERICHOST;
299 		v4a >>= IN_CLASSA_NSHIFT;
300 		if (v4a == 0)
301 			flags |= NI_NUMERICHOST;
302 		break;
303 #ifdef INET6
304 	case AF_INET6:
305 	    {
306 		const struct sockaddr_in6 *sin6;
307 		sin6 = (const struct sockaddr_in6 *)(const void *)sa;
308 		switch (sin6->sin6_addr.s6_addr[0]) {
309 		case 0x00:
310 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
311 				;
312 			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
313 				;
314 			else
315 				flags |= NI_NUMERICHOST;
316 			break;
317 		default:
318 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
319 				flags |= NI_NUMERICHOST;
320 			}
321 			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
322 				flags |= NI_NUMERICHOST;
323 			break;
324 		}
325 	    }
326 		break;
327 #endif
328 	}
329 	if (host == NULL || hostlen == 0) {
330 		/*
331 		 * do nothing in this case.
332 		 * in case you are wondering if "&&" is more correct than
333 		 * "||" here: rfc2553bis-03 says that host == NULL or
334 		 * hostlen == 0 means that the caller does not want the result.
335 		 */
336 	} else if (flags & NI_NUMERICHOST) {
337 		size_t numaddrlen;
338 
339 		/* NUMERICHOST and NAMEREQD conflicts with each other */
340 		if (flags & NI_NAMEREQD)
341 			return EAI_NONAME;
342 
343 		switch(afd->a_af) {
344 #ifdef INET6
345 		case AF_INET6:
346 		{
347 			int error;
348 
349 			if ((error = ip6_parsenumeric(sa, addr, host,
350 						      hostlen, flags)) != 0)
351 				return(error);
352 			break;
353 		}
354 #endif
355 		default:
356 			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
357 			    == NULL)
358 				return EAI_SYSTEM;
359 			numaddrlen = strlen(numaddr);
360 			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
361 				return EAI_MEMORY;
362 			strlcpy(host, numaddr, hostlen);
363 			break;
364 		}
365 	} else {
366 		hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
367 
368 		if (hp) {
369 #if 0
370 			/*
371 			 * commented out, since "for local host" is not
372 			 * implemented here - see RFC2553 p30
373 			 */
374 			if (flags & NI_NOFQDN) {
375 				char *p;
376 				p = strchr(hp->h_name, '.');
377 				if (p)
378 					*p = '\0';
379 			}
380 #endif
381 			if (strlen(hp->h_name) + 1 > hostlen) {
382 				return EAI_MEMORY;
383 			}
384 			strlcpy(host, hp->h_name, hostlen);
385 		} else {
386 			if (flags & NI_NAMEREQD)
387 				return EAI_NONAME;
388 			switch(afd->a_af) {
389 #ifdef INET6
390 			case AF_INET6:
391 			{
392 				int error;
393 
394 				if ((error = ip6_parsenumeric(sa, addr, host,
395 							      hostlen,
396 							      flags)) != 0)
397 					return(error);
398 				break;
399 			}
400 #endif
401 			default:
402 				if (inet_ntop(afd->a_af, addr, host,
403 				    hostlen) == NULL)
404 					return EAI_SYSTEM;
405 				break;
406 			}
407 		}
408 	}
409 	return(0);
410 }
411 
412 #ifdef INET6
413 static int
414 ip6_parsenumeric(sa, addr, host, hostlen, flags)
415 	const struct sockaddr *sa;
416 	const char *addr;
417 	char *host;
418 	socklen_t hostlen;
419 	int flags;
420 {
421 	size_t numaddrlen;
422 	char numaddr[512];
423 
424 	_DIAGASSERT(sa != NULL);
425 	_DIAGASSERT(addr != NULL);
426 	_DIAGASSERT(host != NULL);
427 
428 	if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
429 		return EAI_SYSTEM;
430 
431 	numaddrlen = strlen(numaddr);
432 	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
433 		return EAI_OVERFLOW;
434 	strlcpy(host, numaddr, hostlen);
435 
436 	if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) {
437 		char zonebuf[MAXHOSTNAMELEN];
438 		int zonelen;
439 
440 		zonelen = ip6_sa2str(
441 		    (const struct sockaddr_in6 *)(const void *)sa,
442 		    zonebuf, sizeof(zonebuf), flags);
443 		if (zonelen < 0)
444 			return EAI_OVERFLOW;
445 		if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen)
446 			return EAI_OVERFLOW;
447 		/* construct <numeric-addr><delim><zoneid> */
448 		memcpy(host + numaddrlen + 1, zonebuf,
449 		    (size_t)zonelen);
450 		host[numaddrlen] = SCOPE_DELIMITER;
451 		host[numaddrlen + 1 + zonelen] = '\0';
452 	}
453 
454 	return 0;
455 }
456 
457 /* ARGSUSED */
458 static int
459 ip6_sa2str(sa6, buf, bufsiz, flags)
460 	const struct sockaddr_in6 *sa6;
461 	char *buf;
462 	size_t bufsiz;
463 	int flags;
464 {
465 	unsigned int ifindex;
466 	const struct in6_addr *a6;
467 	int n;
468 
469 	_DIAGASSERT(sa6 != NULL);
470 	_DIAGASSERT(buf != NULL);
471 
472 	ifindex = (unsigned int)sa6->sin6_scope_id;
473 	a6 = &sa6->sin6_addr;
474 
475 #ifdef NI_NUMERICSCOPE
476 	if ((flags & NI_NUMERICSCOPE) != 0) {
477 		n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
478 		if (n < 0 || (size_t)n >= bufsiz)
479 			return -1;
480 		else
481 			return n;
482 	}
483 #endif
484 
485 	/* if_indextoname() does not take buffer size.  not a good api... */
486 	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
487 	    bufsiz >= IF_NAMESIZE) {
488 		char *p = if_indextoname(ifindex, buf);
489 		if (p) {
490 			return(strlen(p));
491 		}
492 	}
493 
494 	/* last resort */
495 	n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
496 	if (n < 0 || (size_t) n >= bufsiz)
497 		return -1;
498 	else
499 		return n;
500 }
501 #endif /* INET6 */
502 
503 
504 #ifndef __minix
505 /*
506  * getnameinfo_link():
507  * Format a link-layer address into a printable format, paying attention to
508  * the interface type.
509  */
510 /* ARGSUSED */
511 static int
512 getnameinfo_link(const struct sockaddr *sa, socklen_t salen,
513     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
514     int flags)
515 {
516 	const struct sockaddr_dl *sdl =
517 	    (const struct sockaddr_dl *)(const void *)sa;
518 	const struct ieee1394_hwaddr *iha;
519 	int n;
520 
521 	if (serv != NULL && servlen > 0)
522 		*serv = '\0';
523 
524 	if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) {
525 		n = snprintf(host, hostlen, "link#%u", sdl->sdl_index);
526 		if (n < 0 || (socklen_t) n > hostlen) {
527 			*host = '\0';
528 			return EAI_MEMORY;
529 		}
530 		return 0;
531 	}
532 
533 	switch (sdl->sdl_type) {
534 #ifdef IFT_ECONET
535 	case IFT_ECONET:
536 		if (sdl->sdl_alen < 2)
537 			return EAI_FAMILY;
538 		if (CLLADDR(sdl)[1] == 0)
539 			n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]);
540 		else
541 			n = snprintf(host, hostlen, "%u.%u",
542 			    CLLADDR(sdl)[1], CLLADDR(sdl)[0]);
543 		if (n < 0 || (socklen_t) n >= hostlen) {
544 			*host = '\0';
545 			return EAI_MEMORY;
546 		} else
547 			return 0;
548 #endif
549 	case IFT_IEEE1394:
550 		if (sdl->sdl_alen < sizeof(iha->iha_uid))
551 			return EAI_FAMILY;
552 		iha =
553 		    (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl);
554 		return hexname(iha->iha_uid, sizeof(iha->iha_uid),
555 		    host, hostlen);
556 	/*
557 	 * The following have zero-length addresses.
558 	 * IFT_ATM	(net/if_atmsubr.c)
559 	 * IFT_FAITH	(net/if_faith.c)
560 	 * IFT_GIF	(net/if_gif.c)
561 	 * IFT_LOOP	(net/if_loop.c)
562 	 * IFT_PPP	(net/if_ppp.c, net/if_spppsubr.c)
563 	 * IFT_SLIP	(net/if_sl.c, net/if_strip.c)
564 	 * IFT_STF	(net/if_stf.c)
565 	 * IFT_L2VLAN	(net/if_vlan.c)
566 	 * IFT_PROPVIRTUAL (net/if_bridge.h>
567 	 */
568 	/*
569 	 * The following use IPv4 addresses as link-layer addresses:
570 	 * IFT_OTHER	(net/if_gre.c)
571 	 */
572 	case IFT_ARCNET: /* default below is believed correct for all these. */
573 	case IFT_ETHER:
574 	case IFT_FDDI:
575 	case IFT_HIPPI:
576 	case IFT_ISO88025:
577 	default:
578 		return hexname((const u_int8_t *)CLLADDR(sdl),
579 		    (size_t)sdl->sdl_alen, host, hostlen);
580 	}
581 }
582 
583 static int
584 hexname(cp, len, host, hostlen)
585 	const u_int8_t *cp;
586 	char *host;
587 	size_t len;
588 	socklen_t hostlen;
589 {
590 	int n;
591 	size_t i;
592 	char *outp = host;
593 
594 	*outp = '\0';
595 	for (i = 0; i < len; i++) {
596 		n = snprintf(outp, hostlen, "%s%02x",
597 		    i ? ":" : "", cp[i]);
598 		if (n < 0 || (socklen_t) n >= hostlen) {
599 			*host = '\0';
600 			return EAI_MEMORY;
601 		}
602 		outp += n;
603 		hostlen -= n;
604 	}
605 	return 0;
606 }
607 #endif /* !__minix */
608