Home | History | Annotate | Line # | Download | only in net
getnameinfo.c revision 1.49
      1 /*	$NetBSD: getnameinfo.c,v 1.49 2010/01/29 22:26:48 is 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.49 2010/01/29 22:26:48 is 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 #include <net/if_dl.h>
     58 #include <net/if_ieee1394.h>
     59 #include <net/if_types.h>
     60 #include <netatalk/at.h>
     61 #include <netinet/in.h>
     62 #include <arpa/inet.h>
     63 #include <arpa/nameser.h>
     64 #include <assert.h>
     65 #include <limits.h>
     66 #include <netdb.h>
     67 #include <resolv.h>
     68 #include <stddef.h>
     69 #include <string.h>
     70 
     71 #include "servent.h"
     72 
     73 #ifdef __weak_alias
     74 __weak_alias(getnameinfo,_getnameinfo)
     75 #endif
     76 
     77 static const struct afd {
     78 	int		a_af;
     79 	socklen_t	a_addrlen;
     80 	socklen_t	a_socklen;
     81 	int		a_off;
     82 } afdl [] = {
     83 #ifdef INET6
     84 	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
     85 		offsetof(struct sockaddr_in6, sin6_addr)},
     86 #endif
     87 	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
     88 		offsetof(struct sockaddr_in, sin_addr)},
     89 	{0, 0, 0, 0},
     90 };
     91 
     92 struct sockinet {
     93 	u_char	si_len;
     94 	u_char	si_family;
     95 	u_short	si_port;
     96 };
     97 
     98 static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *,
     99     socklen_t, char *, socklen_t, int));
    100 #ifdef INET6
    101 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
    102 				 socklen_t, int));
    103 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t,
    104 				 int));
    105 #endif
    106 static int getnameinfo_atalk __P((const struct sockaddr *, socklen_t, char *,
    107     socklen_t, char *, socklen_t, int));
    108 
    109 static int getnameinfo_link __P((const struct sockaddr *, socklen_t, char *,
    110     socklen_t, char *, socklen_t, int));
    111 static int hexname __P((const u_int8_t *, size_t, char *, socklen_t));
    112 
    113 /*
    114  * Top-level getnameinfo() code.  Look at the address family, and pick an
    115  * appropriate function to call.
    116  */
    117 int
    118 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
    119 	const struct sockaddr *sa;
    120 	socklen_t salen;
    121 	char *host, *serv;
    122 	socklen_t hostlen, servlen;
    123 	int flags;
    124 {
    125 
    126 	switch (sa->sa_family) {
    127 	case AF_APPLETALK:
    128 		return getnameinfo_atalk(sa, salen, host, hostlen,
    129 		    serv, servlen, flags);
    130 	case AF_INET:
    131 	case AF_INET6:
    132 		return getnameinfo_inet(sa, salen, host, hostlen,
    133 		    serv, servlen, flags);
    134 	case AF_LINK:
    135 		return getnameinfo_link(sa, salen, host, hostlen,
    136 		    serv, servlen, flags);
    137 	default:
    138 		return EAI_FAMILY;
    139 	}
    140 }
    141 
    142 /*
    143  * getnameinfo_atalk():
    144  * Format an AppleTalk address into a printable format.
    145  */
    146 /* ARGSUSED */
    147 static int
    148 getnameinfo_atalk(const struct sockaddr *sa, socklen_t salen,
    149     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
    150     int flags)
    151 {
    152 	char numserv[8];
    153 	int n, m=0;
    154 
    155 	const struct sockaddr_at *sat =
    156 	    (const struct sockaddr_at *)(const void *)sa;
    157 
    158 	if (serv != NULL && servlen > 0) {
    159 		snprintf(numserv, sizeof(numserv), "%u", sat->sat_port);
    160 		if (strlen(numserv) + 1 > servlen)
    161 			return EAI_MEMORY;
    162 		strlcpy(serv, numserv, servlen);
    163 	}
    164 
    165         n = snprintf(host, hostlen, "%u.%u",
    166 	    ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
    167 
    168 	if (n < 0 || (socklen_t)(m+n) >= hostlen)
    169 		goto errout;
    170 
    171 	m += n;
    172 
    173 	if (sat->sat_range.r_netrange.nr_phase) {
    174         	n = snprintf(host+m, hostlen-m, " phase %u",
    175 			sat->sat_range.r_netrange.nr_phase);
    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_firstnet) {
    183         	n = snprintf(host+m, hostlen-m, " range %u - %u",
    184 			ntohs(sat->sat_range.r_netrange.nr_firstnet),
    185 			ntohs(sat->sat_range.r_netrange.nr_lastnet ));
    186 
    187 		if (n < 0 || (socklen_t)(m+n) >= hostlen)
    188 			goto errout;
    189 
    190 		m += n;
    191 	}
    192 
    193 	return 0;
    194 
    195 errout:
    196 	if (host && hostlen>0)
    197 		host[m] = '\0';	/* XXX ??? */
    198 
    199 	return EAI_MEMORY;
    200 }
    201 
    202 /*
    203  * getnameinfo_inet():
    204  * Format an IPv4 or IPv6 sockaddr into a printable string.
    205  */
    206 static int
    207 getnameinfo_inet(sa, salen, host, hostlen, serv, servlen, flags)
    208 	const struct sockaddr *sa;
    209 	socklen_t salen;
    210 	char *host;
    211 	socklen_t hostlen;
    212 	char *serv;
    213 	socklen_t servlen;
    214 	int flags;
    215 {
    216 	const struct afd *afd;
    217 	struct servent *sp;
    218 	struct hostent *hp;
    219 	u_short port;
    220 	int family, i;
    221 	const char *addr;
    222 	u_int32_t v4a;
    223 	char numserv[512];
    224 	char numaddr[512];
    225 
    226 	/* sa is checked below */
    227 	/* host may be NULL */
    228 	/* serv may be NULL */
    229 
    230 	if (sa == NULL)
    231 		return EAI_FAIL;
    232 
    233 #ifdef BSD4_4
    234 	if (sa->sa_len != salen)
    235 		return EAI_FAIL;
    236 #endif
    237 
    238 	family = sa->sa_family;
    239 	for (i = 0; afdl[i].a_af; i++)
    240 		if (afdl[i].a_af == family) {
    241 			afd = &afdl[i];
    242 			goto found;
    243 		}
    244 	return EAI_FAMILY;
    245 
    246  found:
    247 	if (salen != afd->a_socklen)
    248 		return EAI_FAIL;
    249 
    250 	/* network byte order */
    251 	port = ((const struct sockinet *)(const void *)sa)->si_port;
    252 	addr = (const char *)(const void *)sa + afd->a_off;
    253 
    254 	if (serv == NULL || servlen == 0) {
    255 		/*
    256 		 * do nothing in this case.
    257 		 * in case you are wondering if "&&" is more correct than
    258 		 * "||" here: rfc2553bis-03 says that serv == NULL OR
    259 		 * servlen == 0 means that the caller does not want the result.
    260 		 */
    261 	} else {
    262 		struct servent_data svd;
    263 		struct servent sv;
    264 
    265 		if (flags & NI_NUMERICSERV)
    266 			sp = NULL;
    267 		else {
    268 			(void)memset(&svd, 0, sizeof(svd));
    269 			sp = getservbyport_r(port,
    270 				(flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd);
    271 		}
    272 		if (sp) {
    273 			if (strlen(sp->s_name) + 1 > servlen) {
    274 				endservent_r(&svd);
    275 				return EAI_MEMORY;
    276 			}
    277 			strlcpy(serv, sp->s_name, servlen);
    278 			endservent_r(&svd);
    279 		} else {
    280 			snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
    281 			if (strlen(numserv) + 1 > servlen)
    282 				return EAI_MEMORY;
    283 			strlcpy(serv, numserv, servlen);
    284 		}
    285 	}
    286 
    287 	switch (sa->sa_family) {
    288 	case AF_INET:
    289 		v4a = (u_int32_t)
    290 		    ntohl(((const struct sockaddr_in *)
    291 		    (const void *)sa)->sin_addr.s_addr);
    292 		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
    293 			flags |= NI_NUMERICHOST;
    294 		v4a >>= IN_CLASSA_NSHIFT;
    295 		if (v4a == 0)
    296 			flags |= NI_NUMERICHOST;
    297 		break;
    298 #ifdef INET6
    299 	case AF_INET6:
    300 	    {
    301 		const struct sockaddr_in6 *sin6;
    302 		sin6 = (const struct sockaddr_in6 *)(const void *)sa;
    303 		switch (sin6->sin6_addr.s6_addr[0]) {
    304 		case 0x00:
    305 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
    306 				;
    307 			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
    308 				;
    309 			else
    310 				flags |= NI_NUMERICHOST;
    311 			break;
    312 		default:
    313 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
    314 				flags |= NI_NUMERICHOST;
    315 			}
    316 			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
    317 				flags |= NI_NUMERICHOST;
    318 			break;
    319 		}
    320 	    }
    321 		break;
    322 #endif
    323 	}
    324 	if (host == NULL || hostlen == 0) {
    325 		/*
    326 		 * do nothing in this case.
    327 		 * in case you are wondering if "&&" is more correct than
    328 		 * "||" here: rfc2553bis-03 says that host == NULL or
    329 		 * hostlen == 0 means that the caller does not want the result.
    330 		 */
    331 	} else if (flags & NI_NUMERICHOST) {
    332 		size_t numaddrlen;
    333 
    334 		/* NUMERICHOST and NAMEREQD conflicts with each other */
    335 		if (flags & NI_NAMEREQD)
    336 			return EAI_NONAME;
    337 
    338 		switch(afd->a_af) {
    339 #ifdef INET6
    340 		case AF_INET6:
    341 		{
    342 			int error;
    343 
    344 			if ((error = ip6_parsenumeric(sa, addr, host,
    345 						      hostlen, flags)) != 0)
    346 				return(error);
    347 			break;
    348 		}
    349 #endif
    350 		default:
    351 			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
    352 			    == NULL)
    353 				return EAI_SYSTEM;
    354 			numaddrlen = strlen(numaddr);
    355 			if (numaddrlen + 1 > hostlen) /* don't forget terminator */
    356 				return EAI_MEMORY;
    357 			strlcpy(host, numaddr, hostlen);
    358 			break;
    359 		}
    360 	} else {
    361 		hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
    362 
    363 		if (hp) {
    364 #if 0
    365 			/*
    366 			 * commented out, since "for local host" is not
    367 			 * implemented here - see RFC2553 p30
    368 			 */
    369 			if (flags & NI_NOFQDN) {
    370 				char *p;
    371 				p = strchr(hp->h_name, '.');
    372 				if (p)
    373 					*p = '\0';
    374 			}
    375 #endif
    376 			if (strlen(hp->h_name) + 1 > hostlen) {
    377 				return EAI_MEMORY;
    378 			}
    379 			strlcpy(host, hp->h_name, hostlen);
    380 		} else {
    381 			if (flags & NI_NAMEREQD)
    382 				return EAI_NONAME;
    383 			switch(afd->a_af) {
    384 #ifdef INET6
    385 			case AF_INET6:
    386 			{
    387 				int error;
    388 
    389 				if ((error = ip6_parsenumeric(sa, addr, host,
    390 							      hostlen,
    391 							      flags)) != 0)
    392 					return(error);
    393 				break;
    394 			}
    395 #endif
    396 			default:
    397 				if (inet_ntop(afd->a_af, addr, host,
    398 				    hostlen) == NULL)
    399 					return EAI_SYSTEM;
    400 				break;
    401 			}
    402 		}
    403 	}
    404 	return(0);
    405 }
    406 
    407 #ifdef INET6
    408 static int
    409 ip6_parsenumeric(sa, addr, host, hostlen, flags)
    410 	const struct sockaddr *sa;
    411 	const char *addr;
    412 	char *host;
    413 	socklen_t hostlen;
    414 	int flags;
    415 {
    416 	size_t numaddrlen;
    417 	char numaddr[512];
    418 
    419 	_DIAGASSERT(sa != NULL);
    420 	_DIAGASSERT(addr != NULL);
    421 	_DIAGASSERT(host != NULL);
    422 
    423 	if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
    424 		return EAI_SYSTEM;
    425 
    426 	numaddrlen = strlen(numaddr);
    427 	if (numaddrlen + 1 > hostlen) /* don't forget terminator */
    428 		return EAI_OVERFLOW;
    429 	strlcpy(host, numaddr, hostlen);
    430 
    431 	if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) {
    432 		char zonebuf[MAXHOSTNAMELEN];
    433 		int zonelen;
    434 
    435 		zonelen = ip6_sa2str(
    436 		    (const struct sockaddr_in6 *)(const void *)sa,
    437 		    zonebuf, sizeof(zonebuf), flags);
    438 		if (zonelen < 0)
    439 			return EAI_OVERFLOW;
    440 		if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen)
    441 			return EAI_OVERFLOW;
    442 		/* construct <numeric-addr><delim><zoneid> */
    443 		memcpy(host + numaddrlen + 1, zonebuf,
    444 		    (size_t)zonelen);
    445 		host[numaddrlen] = SCOPE_DELIMITER;
    446 		host[numaddrlen + 1 + zonelen] = '\0';
    447 	}
    448 
    449 	return 0;
    450 }
    451 
    452 /* ARGSUSED */
    453 static int
    454 ip6_sa2str(sa6, buf, bufsiz, flags)
    455 	const struct sockaddr_in6 *sa6;
    456 	char *buf;
    457 	size_t bufsiz;
    458 	int flags;
    459 {
    460 	unsigned int ifindex;
    461 	const struct in6_addr *a6;
    462 	int n;
    463 
    464 	_DIAGASSERT(sa6 != NULL);
    465 	_DIAGASSERT(buf != NULL);
    466 
    467 	ifindex = (unsigned int)sa6->sin6_scope_id;
    468 	a6 = &sa6->sin6_addr;
    469 
    470 #ifdef NI_NUMERICSCOPE
    471 	if ((flags & NI_NUMERICSCOPE) != 0) {
    472 		n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
    473 		if (n < 0 || (size_t)n >= bufsiz)
    474 			return -1;
    475 		else
    476 			return n;
    477 	}
    478 #endif
    479 
    480 	/* if_indextoname() does not take buffer size.  not a good api... */
    481 	if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
    482 	    bufsiz >= IF_NAMESIZE) {
    483 		char *p = if_indextoname(ifindex, buf);
    484 		if (p) {
    485 			return(strlen(p));
    486 		}
    487 	}
    488 
    489 	/* last resort */
    490 	n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id);
    491 	if (n < 0 || (size_t) n >= bufsiz)
    492 		return -1;
    493 	else
    494 		return n;
    495 }
    496 #endif /* INET6 */
    497 
    498 
    499 /*
    500  * getnameinfo_link():
    501  * Format a link-layer address into a printable format, paying attention to
    502  * the interface type.
    503  */
    504 /* ARGSUSED */
    505 static int
    506 getnameinfo_link(const struct sockaddr *sa, socklen_t salen,
    507     char *host, socklen_t hostlen, char *serv, socklen_t servlen,
    508     int flags)
    509 {
    510 	const struct sockaddr_dl *sdl =
    511 	    (const struct sockaddr_dl *)(const void *)sa;
    512 	const struct ieee1394_hwaddr *iha;
    513 	int n;
    514 
    515 	if (serv != NULL && servlen > 0)
    516 		*serv = '\0';
    517 
    518 	if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) {
    519 		n = snprintf(host, hostlen, "link#%u", sdl->sdl_index);
    520 		if (n < 0 || (socklen_t) n > hostlen) {
    521 			*host = '\0';
    522 			return EAI_MEMORY;
    523 		}
    524 		return 0;
    525 	}
    526 
    527 	switch (sdl->sdl_type) {
    528 #ifdef IFT_ECONET
    529 	case IFT_ECONET:
    530 		if (sdl->sdl_alen < 2)
    531 			return EAI_FAMILY;
    532 		if (CLLADDR(sdl)[1] == 0)
    533 			n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]);
    534 		else
    535 			n = snprintf(host, hostlen, "%u.%u",
    536 			    CLLADDR(sdl)[1], CLLADDR(sdl)[0]);
    537 		if (n < 0 || (socklen_t) n >= hostlen) {
    538 			*host = '\0';
    539 			return EAI_MEMORY;
    540 		} else
    541 			return 0;
    542 #endif
    543 	case IFT_IEEE1394:
    544 		if (sdl->sdl_alen < sizeof(iha->iha_uid))
    545 			return EAI_FAMILY;
    546 		iha =
    547 		    (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl);
    548 		return hexname(iha->iha_uid, sizeof(iha->iha_uid),
    549 		    host, hostlen);
    550 	/*
    551 	 * The following have zero-length addresses.
    552 	 * IFT_ATM	(net/if_atmsubr.c)
    553 	 * IFT_FAITH	(net/if_faith.c)
    554 	 * IFT_GIF	(net/if_gif.c)
    555 	 * IFT_LOOP	(net/if_loop.c)
    556 	 * IFT_PPP	(net/if_ppp.c, net/if_spppsubr.c)
    557 	 * IFT_SLIP	(net/if_sl.c, net/if_strip.c)
    558 	 * IFT_STF	(net/if_stf.c)
    559 	 * IFT_L2VLAN	(net/if_vlan.c)
    560 	 * IFT_PROPVIRTUAL (net/if_bridge.h>
    561 	 */
    562 	/*
    563 	 * The following use IPv4 addresses as link-layer addresses:
    564 	 * IFT_OTHER	(net/if_gre.c)
    565 	 */
    566 	case IFT_ARCNET: /* default below is believed correct for all these. */
    567 	case IFT_ETHER:
    568 	case IFT_FDDI:
    569 	case IFT_HIPPI:
    570 	case IFT_ISO88025:
    571 	default:
    572 		return hexname((const u_int8_t *)CLLADDR(sdl),
    573 		    (size_t)sdl->sdl_alen, host, hostlen);
    574 	}
    575 }
    576 
    577 static int
    578 hexname(cp, len, host, hostlen)
    579 	const u_int8_t *cp;
    580 	char *host;
    581 	size_t len;
    582 	socklen_t hostlen;
    583 {
    584 	int n;
    585 	size_t i;
    586 	char *outp = host;
    587 
    588 	*outp = '\0';
    589 	for (i = 0; i < len; i++) {
    590 		n = snprintf(outp, hostlen, "%s%02x",
    591 		    i ? ":" : "", cp[i]);
    592 		if (n < 0 || (socklen_t) n >= hostlen) {
    593 			*host = '\0';
    594 			return EAI_MEMORY;
    595 		}
    596 		outp += n;
    597 		hostlen -= n;
    598 	}
    599 	return 0;
    600 }
    601