1 /* $NetBSD: getnameinfo.c,v 1.44 2006/08/24 15:39:11 christos 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.44 2006/08/24 15:39:11 christos 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}, 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 if (flags & NI_NUMERICSERV) 197 sp = NULL; 198 else { 199 struct servent_data svd; 200 struct servent sv; 201 202 (void)memset(&svd, 0, sizeof(svd)); 203 sp = getservbyport_r(port, 204 (flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd); 205 endservent_r(&svd); 206 } 207 if (sp) { 208 if (strlen(sp->s_name) + 1 > servlen) 209 return EAI_MEMORY; 210 strlcpy(serv, sp->s_name, servlen); 211 } else { 212 snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); 213 if (strlen(numserv) + 1 > servlen) 214 return EAI_MEMORY; 215 strlcpy(serv, numserv, servlen); 216 } 217 } 218 219 switch (sa->sa_family) { 220 case AF_INET: 221 v4a = (u_int32_t) 222 ntohl(((const struct sockaddr_in *) 223 (const void *)sa)->sin_addr.s_addr); 224 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 225 flags |= NI_NUMERICHOST; 226 v4a >>= IN_CLASSA_NSHIFT; 227 if (v4a == 0) 228 flags |= NI_NUMERICHOST; 229 break; 230 #ifdef INET6 231 case AF_INET6: 232 { 233 const struct sockaddr_in6 *sin6; 234 sin6 = (const struct sockaddr_in6 *)(const void *)sa; 235 switch (sin6->sin6_addr.s6_addr[0]) { 236 case 0x00: 237 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 238 ; 239 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 240 ; 241 else 242 flags |= NI_NUMERICHOST; 243 break; 244 default: 245 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 246 flags |= NI_NUMERICHOST; 247 } 248 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 249 flags |= NI_NUMERICHOST; 250 break; 251 } 252 } 253 break; 254 #endif 255 } 256 if (host == NULL || hostlen == 0) { 257 /* 258 * do nothing in this case. 259 * in case you are wondering if "&&" is more correct than 260 * "||" here: rfc2553bis-03 says that host == NULL or 261 * hostlen == 0 means that the caller does not want the result. 262 */ 263 } else if (flags & NI_NUMERICHOST) { 264 size_t numaddrlen; 265 266 /* NUMERICHOST and NAMEREQD conflicts with each other */ 267 if (flags & NI_NAMEREQD) 268 return EAI_NONAME; 269 270 switch(afd->a_af) { 271 #ifdef INET6 272 case AF_INET6: 273 { 274 int error; 275 276 if ((error = ip6_parsenumeric(sa, addr, host, 277 hostlen, flags)) != 0) 278 return(error); 279 break; 280 } 281 #endif 282 default: 283 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 284 == NULL) 285 return EAI_SYSTEM; 286 numaddrlen = strlen(numaddr); 287 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 288 return EAI_MEMORY; 289 strlcpy(host, numaddr, hostlen); 290 break; 291 } 292 } else { 293 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); 294 295 if (hp) { 296 #if 0 297 /* 298 * commented out, since "for local host" is not 299 * implemented here - see RFC2553 p30 300 */ 301 if (flags & NI_NOFQDN) { 302 char *p; 303 p = strchr(hp->h_name, '.'); 304 if (p) 305 *p = '\0'; 306 } 307 #endif 308 if (strlen(hp->h_name) + 1 > hostlen) { 309 return EAI_MEMORY; 310 } 311 strlcpy(host, hp->h_name, hostlen); 312 } else { 313 if (flags & NI_NAMEREQD) 314 return EAI_NONAME; 315 switch(afd->a_af) { 316 #ifdef INET6 317 case AF_INET6: 318 { 319 int error; 320 321 if ((error = ip6_parsenumeric(sa, addr, host, 322 hostlen, 323 flags)) != 0) 324 return(error); 325 break; 326 } 327 #endif 328 default: 329 if (inet_ntop(afd->a_af, addr, host, 330 hostlen) == NULL) 331 return EAI_SYSTEM; 332 break; 333 } 334 } 335 } 336 return(0); 337 } 338 339 #ifdef INET6 340 static int 341 ip6_parsenumeric(sa, addr, host, hostlen, flags) 342 const struct sockaddr *sa; 343 const char *addr; 344 char *host; 345 socklen_t hostlen; 346 int flags; 347 { 348 size_t numaddrlen; 349 char numaddr[512]; 350 351 _DIAGASSERT(sa != NULL); 352 _DIAGASSERT(addr != NULL); 353 _DIAGASSERT(host != NULL); 354 355 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) 356 return EAI_SYSTEM; 357 358 numaddrlen = strlen(numaddr); 359 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 360 return EAI_OVERFLOW; 361 strlcpy(host, numaddr, hostlen); 362 363 if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) { 364 char zonebuf[MAXHOSTNAMELEN]; 365 int zonelen; 366 367 zonelen = ip6_sa2str( 368 (const struct sockaddr_in6 *)(const void *)sa, 369 zonebuf, sizeof(zonebuf), flags); 370 if (zonelen < 0) 371 return EAI_OVERFLOW; 372 if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen) 373 return EAI_OVERFLOW; 374 /* construct <numeric-addr><delim><zoneid> */ 375 memcpy(host + numaddrlen + 1, zonebuf, 376 (size_t)zonelen); 377 host[numaddrlen] = SCOPE_DELIMITER; 378 host[numaddrlen + 1 + zonelen] = '\0'; 379 } 380 381 return 0; 382 } 383 384 /* ARGSUSED */ 385 static int 386 ip6_sa2str(sa6, buf, bufsiz, flags) 387 const struct sockaddr_in6 *sa6; 388 char *buf; 389 size_t bufsiz; 390 int flags; 391 { 392 unsigned int ifindex; 393 const struct in6_addr *a6; 394 int n; 395 396 _DIAGASSERT(sa6 != NULL); 397 _DIAGASSERT(buf != NULL); 398 399 ifindex = (unsigned int)sa6->sin6_scope_id; 400 a6 = &sa6->sin6_addr; 401 402 #ifdef NI_NUMERICSCOPE 403 if ((flags & NI_NUMERICSCOPE) != 0) { 404 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); 405 if (n < 0 || n >= bufsiz) 406 return -1; 407 else 408 return n; 409 } 410 #endif 411 412 /* if_indextoname() does not take buffer size. not a good api... */ 413 if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && 414 bufsiz >= IF_NAMESIZE) { 415 char *p = if_indextoname(ifindex, buf); 416 if (p) { 417 return(strlen(p)); 418 } 419 } 420 421 /* last resort */ 422 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); 423 if (n < 0 || (size_t) n >= bufsiz) 424 return -1; 425 else 426 return n; 427 } 428 #endif /* INET6 */ 429 430 431 /* 432 * getnameinfo_link(): 433 * Format a link-layer address into a printable format, paying attention to 434 * the interface type. 435 */ 436 /* ARGSUSED */ 437 static int 438 getnameinfo_link(const struct sockaddr *sa, socklen_t salen, 439 char *host, socklen_t hostlen, char *serv, socklen_t servlen, 440 int flags) 441 { 442 const struct sockaddr_dl *sdl = 443 (const struct sockaddr_dl *)(const void *)sa; 444 const struct ieee1394_hwaddr *iha; 445 int n; 446 447 if (serv != NULL && servlen > 0) 448 *serv = '\0'; 449 450 if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { 451 n = snprintf(host, hostlen, "link#%u", sdl->sdl_index); 452 if (n < 0 || (socklen_t) n > hostlen) { 453 *host = '\0'; 454 return EAI_MEMORY; 455 } 456 return 0; 457 } 458 459 switch (sdl->sdl_type) { 460 #ifdef IFT_ECONET 461 case IFT_ECONET: 462 if (sdl->sdl_alen < 2) 463 return EAI_FAMILY; 464 if (CLLADDR(sdl)[1] == 0) 465 n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]); 466 else 467 n = snprintf(host, hostlen, "%u.%u", 468 CLLADDR(sdl)[1], CLLADDR(sdl)[0]); 469 if (n < 0 || (socklen_t) n >= hostlen) { 470 *host = '\0'; 471 return EAI_MEMORY; 472 } else 473 return 0; 474 #endif 475 case IFT_IEEE1394: 476 if (sdl->sdl_alen < sizeof(iha->iha_uid)) 477 return EAI_FAMILY; 478 iha = 479 (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl); 480 return hexname(iha->iha_uid, sizeof(iha->iha_uid), 481 host, hostlen); 482 /* 483 * The following have zero-length addresses. 484 * IFT_ATM (net/if_atmsubr.c) 485 * IFT_FAITH (net/if_faith.c) 486 * IFT_GIF (net/if_gif.c) 487 * IFT_LOOP (net/if_loop.c) 488 * IFT_PPP (net/if_ppp.c, net/if_spppsubr.c) 489 * IFT_SLIP (net/if_sl.c, net/if_strip.c) 490 * IFT_STF (net/if_stf.c) 491 * IFT_L2VLAN (net/if_vlan.c) 492 * IFT_PROPVIRTUAL (net/if_bridge.h> 493 */ 494 /* 495 * The following use IPv4 addresses as link-layer addresses: 496 * IFT_OTHER (net/if_gre.c) 497 */ 498 case IFT_ARCNET: /* default below is believed correct for all these. */ 499 case IFT_ETHER: 500 case IFT_FDDI: 501 case IFT_HIPPI: 502 case IFT_ISO88025: 503 default: 504 return hexname((const u_int8_t *)CLLADDR(sdl), 505 (size_t)sdl->sdl_alen, host, hostlen); 506 } 507 } 508 509 static int 510 hexname(cp, len, host, hostlen) 511 const u_int8_t *cp; 512 char *host; 513 size_t len; 514 socklen_t hostlen; 515 { 516 int n; 517 size_t i; 518 char *outp = host; 519 520 *outp = '\0'; 521 for (i = 0; i < len; i++) { 522 n = snprintf(outp, hostlen, "%s%02x", 523 i ? ":" : "", cp[i]); 524 if (n < 0 || (socklen_t) n >= hostlen) { 525 *host = '\0'; 526 return EAI_MEMORY; 527 } 528 outp += n; 529 hostlen -= n; 530 } 531 return 0; 532 } 533