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) always attach textual scopeid (fe80::1%lo0), if 41 * sin6_scope_id is filled - standardization status? 42 * XXX breaks backward compat for code that expects no scopeid. 43 * beware on merge. 44 */ 45 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 #include <sys/types.h> 50 #include <sys/socket.h> 51 #include <net/if.h> 52 #include <netinet/in.h> 53 #include <arpa/inet.h> 54 #include <arpa/nameser.h> 55 #include <netdb.h> 56 #include <resolv.h> 57 #include <string.h> 58 #include <stddef.h> 59 #include <errno.h> 60 61 #define SUCCESS 0 62 #define ANY 0 63 #define YES 1 64 #define NO 0 65 66 static struct afd { 67 int a_af; 68 int a_addrlen; 69 int a_socklen; 70 int a_off; 71 } afdl [] = { 72 #ifdef INET6 73 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 74 offsetof(struct sockaddr_in6, sin6_addr)}, 75 #endif 76 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 77 offsetof(struct sockaddr_in, sin_addr)}, 78 {0, 0, 0}, 79 }; 80 81 struct sockinet { 82 u_char si_len; 83 u_char si_family; 84 u_short si_port; 85 }; 86 87 #ifdef INET6 88 static int ip6_parsenumeric(const struct sockaddr *, const char *, char *, 89 size_t, int); 90 static int ip6_sa2str(const struct sockaddr_in6 *, char *, size_t, int); 91 #endif 92 93 /* 2553bis: use EAI_xx for getnameinfo */ 94 #define ENI_NOSOCKET EAI_FAIL /*XXX*/ 95 #define ENI_NOSERVNAME EAI_NONAME 96 #define ENI_NOHOSTNAME EAI_NONAME 97 #define ENI_MEMORY EAI_MEMORY 98 #define ENI_SYSTEM EAI_SYSTEM 99 #define ENI_FAMILY EAI_FAMILY 100 #define ENI_SALEN EAI_FAMILY 101 102 int 103 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 104 const struct sockaddr *sa; 105 socklen_t salen; 106 char *host; 107 size_t hostlen; 108 char *serv; 109 size_t servlen; 110 int flags; 111 { 112 struct afd *afd; 113 struct servent *sp; 114 struct hostent *hp; 115 u_short port; 116 int family, i; 117 const char *addr; 118 u_int32_t v4a; 119 int h_error; 120 char numserv[512]; 121 char numaddr[512]; 122 123 if (sa == NULL) 124 return ENI_NOSOCKET; 125 126 if (sa->sa_len != salen) 127 return ENI_SALEN; 128 129 family = sa->sa_family; 130 for (i = 0; afdl[i].a_af; i++) 131 if (afdl[i].a_af == family) { 132 afd = &afdl[i]; 133 goto found; 134 } 135 return ENI_FAMILY; 136 137 found: 138 if (salen != afd->a_socklen) 139 return ENI_SALEN; 140 141 /* network byte order */ 142 port = ((const struct sockinet *)sa)->si_port; 143 addr = (const char *)sa + afd->a_off; 144 145 if (serv == NULL || servlen == 0) { 146 /* 147 * do nothing in this case. 148 * in case you are wondering if "&&" is more correct than 149 * "||" here: RFC2553 says that serv == NULL OR servlen == 0 150 * means that the caller does not want the result. 151 */ 152 } else { 153 if (flags & NI_NUMERICSERV) 154 sp = NULL; 155 else { 156 sp = getservbyport(port, 157 (flags & NI_DGRAM) ? "udp" : "tcp"); 158 } 159 if (sp) { 160 if (strlen(sp->s_name) + 1 > servlen) 161 return ENI_MEMORY; 162 strcpy(serv, sp->s_name); 163 } else { 164 snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 165 if (strlen(numserv) + 1 > servlen) 166 return ENI_MEMORY; 167 strcpy(serv, numserv); 168 } 169 } 170 171 switch (sa->sa_family) { 172 case AF_INET: 173 v4a = (u_int32_t) 174 ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); 175 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 176 flags |= NI_NUMERICHOST; 177 v4a >>= IN_CLASSA_NSHIFT; 178 if (v4a == 0) 179 flags |= NI_NUMERICHOST; 180 break; 181 #ifdef INET6 182 case AF_INET6: 183 { 184 const struct sockaddr_in6 *sin6; 185 sin6 = (const struct sockaddr_in6 *)sa; 186 switch (sin6->sin6_addr.s6_addr[0]) { 187 case 0x00: 188 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 189 ; 190 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 191 ; 192 else 193 flags |= NI_NUMERICHOST; 194 break; 195 default: 196 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 197 flags |= NI_NUMERICHOST; 198 } 199 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 200 flags |= NI_NUMERICHOST; 201 break; 202 } 203 } 204 break; 205 #endif 206 } 207 if (host == NULL || hostlen == 0) { 208 /* 209 * do nothing in this case. 210 * in case you are wondering if "&&" is more correct than 211 * "||" here: RFC2553 says that host == NULL OR hostlen == 0 212 * means that the caller does not want the result. 213 */ 214 } else if (flags & NI_NUMERICHOST) { 215 int numaddrlen; 216 217 /* NUMERICHOST and NAMEREQD conflicts with each other */ 218 if (flags & NI_NAMEREQD) 219 return ENI_NOHOSTNAME; 220 221 switch(afd->a_af) { 222 #ifdef INET6 223 case AF_INET6: 224 { 225 int error; 226 227 if ((error = ip6_parsenumeric(sa, addr, host, 228 hostlen, flags)) != 0) 229 return(error); 230 break; 231 } 232 #endif 233 default: 234 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 235 == NULL) 236 return ENI_SYSTEM; 237 numaddrlen = strlen(numaddr); 238 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 239 return ENI_MEMORY; 240 strcpy(host, numaddr); 241 break; 242 } 243 } else { 244 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 245 246 if (hp) { 247 #if 0 248 /* 249 * commented out, since "for local host" is not 250 * implemented here - see RFC2553 p30 251 */ 252 if (flags & NI_NOFQDN) { 253 char *p; 254 p = strchr(hp->h_name, '.'); 255 if (p) 256 *p = '\0'; 257 } 258 #endif 259 if (strlen(hp->h_name) + 1 > hostlen) { 260 freehostent(hp); 261 return ENI_MEMORY; 262 } 263 strcpy(host, hp->h_name); 264 freehostent(hp); 265 } else { 266 if (flags & NI_NAMEREQD) 267 return ENI_NOHOSTNAME; 268 switch(afd->a_af) { 269 #ifdef INET6 270 case AF_INET6: 271 { 272 int error; 273 274 if ((error = ip6_parsenumeric(sa, addr, host, 275 hostlen, 276 flags)) != 0) 277 return(error); 278 break; 279 } 280 #endif 281 default: 282 if (inet_ntop(afd->a_af, addr, host, 283 hostlen) == NULL) 284 return ENI_SYSTEM; 285 break; 286 } 287 } 288 } 289 return SUCCESS; 290 } 291 292 #ifdef INET6 293 static int 294 ip6_parsenumeric(sa, addr, host, hostlen, flags) 295 const struct sockaddr *sa; 296 const char *addr; 297 char *host; 298 size_t hostlen; 299 int flags; 300 { 301 int numaddrlen; 302 char numaddr[512]; 303 304 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) 305 == NULL) 306 return ENI_SYSTEM; 307 308 numaddrlen = strlen(numaddr); 309 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 310 return ENI_MEMORY; 311 strcpy(host, numaddr); 312 313 if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { 314 char zonebuf[MAXHOSTNAMELEN]; 315 int zonelen; 316 317 /* ip6_sa2str never fails */ 318 zonelen = ip6_sa2str( 319 (const struct sockaddr_in6 *)(const void *)sa, 320 zonebuf, sizeof(zonebuf), flags); 321 if (zonelen < 0) 322 return EAI_MEMORY; 323 if (zonelen + 1 + numaddrlen + 1 > hostlen) 324 return ENI_MEMORY; 325 /* construct <numeric-addr><delim><scopeid> */ 326 memcpy(host + numaddrlen + 1, zonebuf, 327 (size_t)zonelen); 328 host[numaddrlen] = SCOPE_DELIMITER; 329 host[numaddrlen + 1 + zonelen] = '\0'; 330 } 331 332 return 0; 333 } 334 335 /* ARGSUSED */ 336 static int 337 ip6_sa2str(sa6, buf, bufsiz, flags) 338 const struct sockaddr_in6 *sa6; 339 char *buf; 340 size_t bufsiz; 341 int flags; 342 { 343 unsigned int ifindex = (unsigned int)sa6->sin6_scope_id; 344 const struct in6_addr *a6 = &sa6->sin6_addr; 345 346 #ifdef NI_NUMERICSCOPE 347 if (flags & NI_NUMERICSCOPE) { 348 return(snprintf(buf, bufsiz, "%d", sa6->sin6_scope_id)); 349 } 350 #endif 351 352 /* if_indextoname() does not take buffer size. not a good api... */ 353 if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && 354 bufsiz >= IF_NAMESIZE) { 355 char *p = if_indextoname(ifindex, buf); 356 if (p) { 357 return(strlen(p)); 358 } 359 } 360 361 /* last resort */ 362 return(snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id)); 363 } 364 #endif /* INET6 */ 365