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