1 /* $KAME: getnameinfo.c,v 1.61 2002/06/27 09:25:47 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * Copyright (c) 2000 Ben Harris. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Issues to be discussed: 35 * - Thread safe-ness must be checked 36 * - RFC2553 says that we should raise error on short buffer. X/Open says 37 * we need to truncate the result. We obey RFC2553 (and X/Open should be 38 * modified). ipngwg rough consensus seems to follow RFC2553. 39 * - What is "local" in NI_FQDN? 40 * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. 41 * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if 42 * sin6_scope_id is filled - standardization status? 43 * XXX breaks backward compat for code that expects no scopeid. 44 * beware on merge. 45 */ 46 47 #include <sys/cdefs.h> 48 __FBSDID("$FreeBSD$"); 49 50 #include <sys/types.h> 51 #include <sys/socket.h> 52 #include <net/if.h> 53 #include <net/if_dl.h> 54 #include <net/if_types.h> 55 #include <net/firewire.h> 56 #include <netinet/in.h> 57 #include <arpa/inet.h> 58 #include <arpa/nameser.h> 59 #include <netdb.h> 60 #include <resolv.h> 61 #include <string.h> 62 #include <stddef.h> 63 #include <errno.h> 64 65 static int getnameinfo_inet(const struct sockaddr *, socklen_t, char *, 66 size_t, char *, size_t, int); 67 #ifdef INET6 68 static int ip6_parsenumeric(const struct sockaddr *, const char *, char *, 69 size_t, int); 70 static int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int); 71 #endif 72 static int getnameinfo_link(const struct sockaddr *, socklen_t, char *, 73 size_t, char *, size_t, int); 74 static int hexname(const u_int8_t *, size_t, char *, size_t); 75 76 int 77 getnameinfo(const struct sockaddr *sa, socklen_t salen, 78 char *host, size_t hostlen, char *serv, size_t servlen, 79 int flags) 80 { 81 82 switch (sa->sa_family) { 83 case AF_INET: 84 #ifdef INET6 85 case AF_INET6: 86 #endif 87 return getnameinfo_inet(sa, salen, host, hostlen, serv, 88 servlen, flags); 89 case AF_LINK: 90 return getnameinfo_link(sa, salen, host, hostlen, serv, 91 servlen, flags); 92 default: 93 return EAI_FAMILY; 94 } 95 } 96 97 static const struct afd { 98 int a_af; 99 size_t a_addrlen; 100 socklen_t a_socklen; 101 int a_off; 102 } afdl [] = { 103 #ifdef INET6 104 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 105 offsetof(struct sockaddr_in6, sin6_addr)}, 106 #endif 107 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 108 offsetof(struct sockaddr_in, sin_addr)}, 109 {0, 0, 0}, 110 }; 111 112 struct sockinet { 113 u_char si_len; 114 u_char si_family; 115 u_short si_port; 116 }; 117 118 static int 119 getnameinfo_inet(const struct sockaddr *sa, socklen_t salen, 120 char *host, size_t hostlen, char *serv, size_t servlen, 121 int flags) 122 { 123 const struct afd *afd; 124 struct servent *sp; 125 struct hostent *hp; 126 u_short port; 127 int family, i; 128 const char *addr; 129 u_int32_t v4a; 130 int h_error; 131 char numserv[512]; 132 char numaddr[512]; 133 134 if (sa == NULL) 135 return EAI_FAIL; 136 137 family = sa->sa_family; 138 for (i = 0; afdl[i].a_af; i++) 139 if (afdl[i].a_af == family) { 140 afd = &afdl[i]; 141 goto found; 142 } 143 return EAI_FAMILY; 144 145 found: 146 if (salen != afd->a_socklen) 147 return EAI_FAIL; 148 149 /* network byte order */ 150 port = ((const struct sockinet *)sa)->si_port; 151 addr = (const char *)sa + afd->a_off; 152 153 if (serv == NULL || servlen == 0) { 154 /* 155 * do nothing in this case. 156 * in case you are wondering if "&&" is more correct than 157 * "||" here: rfc2553bis-03 says that serv == NULL OR 158 * servlen == 0 means that the caller does not want the result. 159 */ 160 } else { 161 if (flags & NI_NUMERICSERV) 162 sp = NULL; 163 else { 164 sp = getservbyport(port, 165 (flags & NI_DGRAM) ? "udp" : "tcp"); 166 } 167 if (sp) { 168 if (strlen(sp->s_name) + 1 > servlen) 169 return EAI_MEMORY; 170 strlcpy(serv, sp->s_name, servlen); 171 } else { 172 snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); 173 if (strlen(numserv) + 1 > servlen) 174 return EAI_MEMORY; 175 strlcpy(serv, numserv, servlen); 176 } 177 } 178 179 switch (sa->sa_family) { 180 case AF_INET: 181 v4a = (u_int32_t) 182 ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); 183 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 184 flags |= NI_NUMERICHOST; 185 v4a >>= IN_CLASSA_NSHIFT; 186 if (v4a == 0) 187 flags |= NI_NUMERICHOST; 188 break; 189 #ifdef INET6 190 case AF_INET6: 191 { 192 const struct sockaddr_in6 *sin6; 193 sin6 = (const struct sockaddr_in6 *)sa; 194 switch (sin6->sin6_addr.s6_addr[0]) { 195 case 0x00: 196 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 197 ; 198 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 199 ; 200 else 201 flags |= NI_NUMERICHOST; 202 break; 203 default: 204 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 205 flags |= NI_NUMERICHOST; 206 } 207 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 208 flags |= NI_NUMERICHOST; 209 break; 210 } 211 } 212 break; 213 #endif 214 } 215 if (host == NULL || hostlen == 0) { 216 /* 217 * do nothing in this case. 218 * in case you are wondering if "&&" is more correct than 219 * "||" here: rfc2553bis-03 says that host == NULL or 220 * hostlen == 0 means that the caller does not want the result. 221 */ 222 } else if (flags & NI_NUMERICHOST) { 223 size_t numaddrlen; 224 225 /* NUMERICHOST and NAMEREQD conflicts with each other */ 226 if (flags & NI_NAMEREQD) 227 return EAI_NONAME; 228 229 switch(afd->a_af) { 230 #ifdef INET6 231 case AF_INET6: 232 { 233 int error; 234 235 if ((error = ip6_parsenumeric(sa, addr, host, 236 hostlen, flags)) != 0) 237 return(error); 238 break; 239 } 240 #endif 241 default: 242 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 243 == NULL) 244 return EAI_SYSTEM; 245 numaddrlen = strlen(numaddr); 246 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 247 return EAI_MEMORY; 248 strlcpy(host, numaddr, hostlen); 249 break; 250 } 251 } else { 252 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 253 254 if (hp) { 255 #if 0 256 /* 257 * commented out, since "for local host" is not 258 * implemented here - see RFC2553 p30 259 */ 260 if (flags & NI_NOFQDN) { 261 char *p; 262 p = strchr(hp->h_name, '.'); 263 if (p) 264 *p = '\0'; 265 } 266 #endif 267 if (strlen(hp->h_name) + 1 > hostlen) { 268 freehostent(hp); 269 return EAI_MEMORY; 270 } 271 strlcpy(host, hp->h_name, hostlen); 272 freehostent(hp); 273 } else { 274 if (flags & NI_NAMEREQD) 275 return EAI_NONAME; 276 switch(afd->a_af) { 277 #ifdef INET6 278 case AF_INET6: 279 { 280 int error; 281 282 if ((error = ip6_parsenumeric(sa, addr, host, 283 hostlen, 284 flags)) != 0) 285 return(error); 286 break; 287 } 288 #endif 289 default: 290 if (inet_ntop(afd->a_af, addr, host, 291 hostlen) == NULL) 292 return EAI_SYSTEM; 293 break; 294 } 295 } 296 } 297 return(0); 298 } 299 300 #ifdef INET6 301 static int 302 ip6_parsenumeric(const struct sockaddr *sa, const char *addr, 303 char *host, size_t hostlen, int flags) 304 { 305 size_t numaddrlen; 306 char numaddr[512]; 307 308 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) 309 return EAI_SYSTEM; 310 311 numaddrlen = strlen(numaddr); 312 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 313 return EAI_OVERFLOW; 314 strlcpy(host, numaddr, hostlen); 315 316 if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { 317 char zonebuf[MAXHOSTNAMELEN]; 318 int zonelen; 319 320 zonelen = ip6_sa2str( 321 (const struct sockaddr_in6 *)(const void *)sa, 322 zonebuf, sizeof(zonebuf), flags); 323 if (zonelen < 0) 324 return EAI_OVERFLOW; 325 if (zonelen + 1 + numaddrlen + 1 > hostlen) 326 return EAI_OVERFLOW; 327 328 /* construct <numeric-addr><delim><zoneid> */ 329 memcpy(host + numaddrlen + 1, zonebuf, 330 (size_t)zonelen); 331 host[numaddrlen] = SCOPE_DELIMITER; 332 host[numaddrlen + 1 + zonelen] = '\0'; 333 } 334 335 return 0; 336 } 337 338 /* ARGSUSED */ 339 static int 340 ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, size_t bufsiz, int flags) 341 { 342 unsigned int ifindex; 343 const struct in6_addr *a6; 344 int n; 345 346 ifindex = (unsigned int)sa6->sin6_scope_id; 347 a6 = &sa6->sin6_addr; 348 349 #ifdef NI_NUMERICSCOPE 350 if ((flags & NI_NUMERICSCOPE) != 0) { 351 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); 352 if (n < 0 || n >= bufsiz) 353 return -1; 354 else 355 return n; 356 } 357 #endif 358 359 /* if_indextoname() does not take buffer size. not a good api... */ 360 if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || 361 IN6_IS_ADDR_MC_NODELOCAL(a6)) && bufsiz >= IF_NAMESIZE) { 362 char *p = if_indextoname(ifindex, buf); 363 if (p) { 364 return(strlen(p)); 365 } 366 } 367 368 /* last resort */ 369 n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); 370 if (n < 0 || (size_t)n >= bufsiz) 371 return -1; 372 else 373 return n; 374 } 375 #endif /* INET6 */ 376 377 /* 378 * getnameinfo_link(): 379 * Format a link-layer address into a printable format, paying attention to 380 * the interface type. 381 */ 382 /* ARGSUSED */ 383 static int 384 getnameinfo_link(const struct sockaddr *sa, socklen_t salen, 385 char *host, size_t hostlen, char *serv, size_t servlen, int flags) 386 { 387 const struct sockaddr_dl *sdl = 388 (const struct sockaddr_dl *)(const void *)sa; 389 const struct fw_hwaddr *iha; 390 int n; 391 392 if (serv != NULL && servlen > 0) 393 *serv = '\0'; 394 395 if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { 396 n = snprintf(host, hostlen, "link#%d", sdl->sdl_index); 397 if (n > hostlen) { 398 *host = '\0'; 399 return EAI_MEMORY; 400 } 401 return 0; 402 } 403 404 switch (sdl->sdl_type) { 405 case IFT_IEEE1394: 406 if (sdl->sdl_alen < sizeof(iha->sender_unique_ID_hi) + 407 sizeof(iha->sender_unique_ID_lo)) 408 return EAI_FAMILY; 409 iha = (const struct fw_hwaddr *)(const void *)LLADDR(sdl); 410 return hexname((const u_int8_t *)&iha->sender_unique_ID_hi, 411 sizeof(iha->sender_unique_ID_hi) + 412 sizeof(iha->sender_unique_ID_lo), 413 host, hostlen); 414 /* 415 * The following have zero-length addresses. 416 * IFT_ATM (net/if_atmsubr.c) 417 * IFT_GIF (net/if_gif.c) 418 * IFT_LOOP (net/if_loop.c) 419 * IFT_PPP (net/if_ppp.c, net/if_spppsubr.c) 420 * IFT_SLIP (net/if_sl.c, net/if_strip.c) 421 * IFT_STF (net/if_stf.c) 422 * IFT_L2VLAN (net/if_vlan.c) 423 * IFT_BRIDGE (net/if_bridge.h> 424 */ 425 /* 426 * The following use IPv4 addresses as link-layer addresses: 427 * IFT_OTHER (net/if_gre.c) 428 * IFT_OTHER (netinet/ip_ipip.c) 429 */ 430 /* default below is believed correct for all these. */ 431 case IFT_ARCNET: 432 case IFT_ETHER: 433 case IFT_FDDI: 434 case IFT_HIPPI: 435 case IFT_ISO88025: 436 default: 437 return hexname((u_int8_t *)LLADDR(sdl), (size_t)sdl->sdl_alen, 438 host, hostlen); 439 } 440 } 441 442 static int 443 hexname(cp, len, host, hostlen) 444 const u_int8_t *cp; 445 char *host; 446 size_t len, hostlen; 447 { 448 int i, n; 449 char *outp = host; 450 451 *outp = '\0'; 452 for (i = 0; i < len; i++) { 453 n = snprintf(outp, hostlen, "%s%02x", 454 i ? ":" : "", cp[i]); 455 if (n < 0 || n >= hostlen) { 456 *host = '\0'; 457 return EAI_MEMORY; 458 } 459 outp += n; 460 hostlen -= n; 461 } 462 return 0; 463 } 464