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