1 /* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Issues to be discussed: 34 * - Thread safe-ness must be checked 35 * - RFC2553 says that we should raise error on short buffer. X/Open says 36 * we need to truncate the result. We obey RFC2553 (and X/Open should be 37 * modified). ipngwg rough consensus seems to follow RFC2553. 38 * - What is "local" in NI_FQDN? 39 * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. 40 * - (KAME extension) NI_WITHSCOPEID when called with global address, 41 * and sin6_scope_id filled 42 */ 43 44 #include <sys/cdefs.h> 45 __FBSDID("$FreeBSD$"); 46 47 #include <sys/types.h> 48 #include <sys/socket.h> 49 #include <net/if.h> 50 #include <netinet/in.h> 51 #include <arpa/inet.h> 52 #include <arpa/nameser.h> 53 #include <netdb.h> 54 #include <resolv.h> 55 #include <string.h> 56 #include <stddef.h> 57 #include <errno.h> 58 59 #define SUCCESS 0 60 #define ANY 0 61 #define YES 1 62 #define NO 0 63 64 static struct afd { 65 int a_af; 66 int a_addrlen; 67 int a_socklen; 68 int a_off; 69 } afdl [] = { 70 #ifdef INET6 71 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 72 offsetof(struct sockaddr_in6, sin6_addr)}, 73 #endif 74 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 75 offsetof(struct sockaddr_in, sin_addr)}, 76 {0, 0, 0}, 77 }; 78 79 struct sockinet { 80 u_char si_len; 81 u_char si_family; 82 u_short si_port; 83 }; 84 85 #ifdef INET6 86 static int ip6_parsenumeric(const struct sockaddr *, const char *, char *, 87 size_t, int); 88 static int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int); 89 #endif 90 91 /* 2553bis: use EAI_xx for getnameinfo */ 92 #define ENI_NOSOCKET EAI_FAIL /*XXX*/ 93 #define ENI_NOSERVNAME EAI_NONAME 94 #define ENI_NOHOSTNAME EAI_NONAME 95 #define ENI_MEMORY EAI_MEMORY 96 #define ENI_SYSTEM EAI_SYSTEM 97 #define ENI_FAMILY EAI_FAMILY 98 #define ENI_SALEN EAI_FAMILY 99 100 int 101 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 102 const struct sockaddr *sa; 103 socklen_t salen; 104 char *host; 105 size_t hostlen; 106 char *serv; 107 size_t servlen; 108 int flags; 109 { 110 struct afd *afd; 111 struct servent *sp; 112 struct hostent *hp; 113 u_short port; 114 int family, i; 115 const char *addr; 116 u_int32_t v4a; 117 int h_error; 118 char numserv[512]; 119 char numaddr[512]; 120 121 if (sa == NULL) 122 return ENI_NOSOCKET; 123 124 if (sa->sa_len != salen) 125 return ENI_SALEN; 126 127 family = sa->sa_family; 128 for (i = 0; afdl[i].a_af; i++) 129 if (afdl[i].a_af == family) { 130 afd = &afdl[i]; 131 goto found; 132 } 133 return ENI_FAMILY; 134 135 found: 136 if (salen != afd->a_socklen) 137 return ENI_SALEN; 138 139 /* network byte order */ 140 port = ((const struct sockinet *)sa)->si_port; 141 addr = (const char *)sa + afd->a_off; 142 143 if (serv == NULL || servlen == 0) { 144 /* 145 * do nothing in this case. 146 * in case you are wondering if "&&" is more correct than 147 * "||" here: RFC2553 says that serv == NULL OR servlen == 0 148 * means that the caller does not want the result. 149 */ 150 } else { 151 if (flags & NI_NUMERICSERV) 152 sp = NULL; 153 else { 154 sp = getservbyport(port, 155 (flags & NI_DGRAM) ? "udp" : "tcp"); 156 } 157 if (sp) { 158 if (strlen(sp->s_name) + 1 > servlen) 159 return ENI_MEMORY; 160 strcpy(serv, sp->s_name); 161 } else { 162 snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 163 if (strlen(numserv) + 1 > servlen) 164 return ENI_MEMORY; 165 strcpy(serv, numserv); 166 } 167 } 168 169 switch (sa->sa_family) { 170 case AF_INET: 171 v4a = (u_int32_t) 172 ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); 173 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 174 flags |= NI_NUMERICHOST; 175 v4a >>= IN_CLASSA_NSHIFT; 176 if (v4a == 0) 177 flags |= NI_NUMERICHOST; 178 break; 179 #ifdef INET6 180 case AF_INET6: 181 { 182 const struct sockaddr_in6 *sin6; 183 sin6 = (const struct sockaddr_in6 *)sa; 184 switch (sin6->sin6_addr.s6_addr[0]) { 185 case 0x00: 186 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 187 ; 188 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 189 ; 190 else 191 flags |= NI_NUMERICHOST; 192 break; 193 default: 194 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 195 flags |= NI_NUMERICHOST; 196 } 197 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 198 flags |= NI_NUMERICHOST; 199 break; 200 } 201 } 202 break; 203 #endif 204 } 205 if (host == NULL || hostlen == 0) { 206 /* 207 * do nothing in this case. 208 * in case you are wondering if "&&" is more correct than 209 * "||" here: RFC2553 says that host == NULL OR hostlen == 0 210 * means that the caller does not want the result. 211 */ 212 } else if (flags & NI_NUMERICHOST) { 213 int numaddrlen; 214 215 /* NUMERICHOST and NAMEREQD conflicts with each other */ 216 if (flags & NI_NAMEREQD) 217 return ENI_NOHOSTNAME; 218 219 switch(afd->a_af) { 220 #ifdef INET6 221 case AF_INET6: 222 { 223 int error; 224 225 if ((error = ip6_parsenumeric(sa, addr, host, 226 hostlen, flags)) != 0) 227 return(error); 228 break; 229 } 230 #endif 231 default: 232 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 233 == NULL) 234 return ENI_SYSTEM; 235 numaddrlen = strlen(numaddr); 236 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 237 return ENI_MEMORY; 238 strcpy(host, numaddr); 239 break; 240 } 241 } else { 242 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 243 244 if (hp) { 245 #if 0 246 /* 247 * commented out, since "for local host" is not 248 * implemented here - see RFC2553 p30 249 */ 250 if (flags & NI_NOFQDN) { 251 char *p; 252 p = strchr(hp->h_name, '.'); 253 if (p) 254 *p = '\0'; 255 } 256 #endif 257 if (strlen(hp->h_name) + 1 > hostlen) { 258 freehostent(hp); 259 return ENI_MEMORY; 260 } 261 strcpy(host, hp->h_name); 262 freehostent(hp); 263 } else { 264 if (flags & NI_NAMEREQD) 265 return ENI_NOHOSTNAME; 266 switch(afd->a_af) { 267 #ifdef INET6 268 case AF_INET6: 269 { 270 int error; 271 272 if ((error = ip6_parsenumeric(sa, addr, host, 273 hostlen, 274 flags)) != 0) 275 return(error); 276 break; 277 } 278 #endif 279 default: 280 if (inet_ntop(afd->a_af, addr, host, 281 hostlen) == NULL) 282 return ENI_SYSTEM; 283 break; 284 } 285 } 286 } 287 return SUCCESS; 288 } 289 290 #ifdef INET6 291 static int 292 ip6_parsenumeric(sa, addr, host, hostlen, flags) 293 const struct sockaddr *sa; 294 const char *addr; 295 char *host; 296 size_t hostlen; 297 int flags; 298 { 299 int numaddrlen; 300 char numaddr[512]; 301 302 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) 303 == NULL) 304 return ENI_SYSTEM; 305 306 numaddrlen = strlen(numaddr); 307 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 308 return ENI_MEMORY; 309 strcpy(host, numaddr); 310 311 #ifdef NI_WITHSCOPEID 312 if ( 313 #ifdef DONT_OPAQUE_SCOPEID 314 (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 315 IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 316 #endif 317 ((const struct sockaddr_in6 *)sa)->sin6_scope_id) { 318 #ifndef ALWAYS_WITHSCOPE 319 if (flags & NI_WITHSCOPEID) 320 #endif /* !ALWAYS_WITHSCOPE */ 321 { 322 char scopebuf[MAXHOSTNAMELEN]; 323 int scopelen; 324 325 /* ip6_sa2str never fails */ 326 scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa, 327 scopebuf, sizeof(scopebuf), 328 flags); 329 if (scopelen + 1 + numaddrlen + 1 > hostlen) 330 return ENI_MEMORY; 331 /* 332 * construct <numeric-addr><delim><scopeid> 333 */ 334 memcpy(host + numaddrlen + 1, scopebuf, 335 scopelen); 336 host[numaddrlen] = SCOPE_DELIMITER; 337 host[numaddrlen + 1 + scopelen] = '\0'; 338 } 339 } 340 #endif /* NI_WITHSCOPEID */ 341 342 return 0; 343 } 344 345 /* ARGSUSED */ 346 static int 347 ip6_sa2str(sa6, buf, bufsiz, flags) 348 const struct sockaddr_in6 *sa6; 349 char *buf; 350 size_t bufsiz; 351 int flags; 352 { 353 unsigned int ifindex = (unsigned int)sa6->sin6_scope_id; 354 const struct in6_addr *a6 = &sa6->sin6_addr; 355 356 #ifdef NI_NUMERICSCOPE 357 if (flags & NI_NUMERICSCOPE) { 358 return(snprintf(buf, bufsiz, "%d", sa6->sin6_scope_id)); 359 } 360 #endif 361 362 /* if_indextoname() does not take buffer size. not a good api... */ 363 if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && 364 bufsiz >= IF_NAMESIZE) { 365 char *p = if_indextoname(ifindex, buf); 366 if (p) { 367 return(strlen(p)); 368 } 369 } 370 371 /* last resort */ 372 return(snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id)); 373 } 374 #endif /* INET6 */ 375