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