1 /* $NetBSD: getnameinfo.c,v 1.23 2000/09/25 23:37:55 itojun Exp $ */ 2 /* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 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) NI_WITHSCOPEID when called with global address, 42 * and sin6_scope_id filled 43 */ 44 45 #include <sys/cdefs.h> 46 #if defined(LIBC_SCCS) && !defined(lint) 47 __RCSID("$NetBSD: getnameinfo.c,v 1.23 2000/09/25 23:37:55 itojun Exp $"); 48 #endif /* LIBC_SCCS and not lint */ 49 50 #include "namespace.h" 51 #include <sys/types.h> 52 #include <sys/socket.h> 53 #include <net/if.h> 54 #include <netinet/in.h> 55 #include <arpa/inet.h> 56 #include <arpa/nameser.h> 57 #include <netdb.h> 58 #include <resolv.h> 59 #include <string.h> 60 #include <stddef.h> 61 62 #ifdef __weak_alias 63 __weak_alias(getnameinfo,_getnameinfo) 64 #endif 65 66 #define SUCCESS 0 67 #define ANY 0 68 #define YES 1 69 #define NO 0 70 71 static struct afd { 72 int a_af; 73 int a_addrlen; 74 int a_socklen; 75 int a_off; 76 } afdl [] = { 77 #ifdef INET6 78 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 79 offsetof(struct sockaddr_in6, sin6_addr)}, 80 #endif 81 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 82 offsetof(struct sockaddr_in, sin_addr)}, 83 {0, 0, 0}, 84 }; 85 86 struct sockinet { 87 u_char si_len; 88 u_char si_family; 89 u_short si_port; 90 }; 91 92 #ifdef INET6 93 static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, 94 size_t, int)); 95 static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int)); 96 #endif 97 98 /* 2553bis: use EAI_xx for getnameinfo */ 99 #define ENI_NOSOCKET EAI_FAIL /*XXX*/ 100 #define ENI_NOSERVNAME EAI_NONAME 101 #define ENI_NOHOSTNAME EAI_NONAME 102 #define ENI_MEMORY EAI_MEMORY 103 #define ENI_SYSTEM EAI_SYSTEM 104 #define ENI_FAMILY EAI_FAMILY 105 #define ENI_SALEN EAI_FAMILY 106 107 int 108 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 109 const struct sockaddr *sa; 110 socklen_t salen; 111 char *host; 112 size_t hostlen; 113 char *serv; 114 size_t servlen; 115 int flags; 116 { 117 struct afd *afd; 118 struct servent *sp; 119 struct hostent *hp; 120 u_short port; 121 int family, i; 122 const char *addr; 123 u_int32_t v4a; 124 char numserv[512]; 125 char numaddr[512]; 126 127 if (sa == NULL) 128 return ENI_NOSOCKET; 129 130 #ifdef BSD4_4 131 if (sa->sa_len != salen) 132 return ENI_SALEN; 133 #endif 134 135 family = sa->sa_family; 136 for (i = 0; afdl[i].a_af; i++) 137 if (afdl[i].a_af == family) { 138 afd = &afdl[i]; 139 goto found; 140 } 141 return ENI_FAMILY; 142 143 found: 144 if (salen != afd->a_socklen) 145 return ENI_SALEN; 146 147 /* network byte order */ 148 port = ((const struct sockinet *)(const void *)sa)->si_port; 149 addr = (const char *)(const void *)sa + afd->a_off; 150 151 if (serv == NULL || servlen == 0) { 152 /* 153 * do nothing in this case. 154 * in case you are wondering if "&&" is more correct than 155 * "||" here: RFC2553 says that serv == NULL OR servlen == 0 156 * means that the caller does not want the result. 157 */ 158 } else { 159 if (flags & NI_NUMERICSERV) 160 sp = NULL; 161 else { 162 sp = getservbyport(port, 163 (flags & NI_DGRAM) ? "udp" : "tcp"); 164 } 165 if (sp) { 166 if (strlen(sp->s_name) + 1 > servlen) 167 return ENI_MEMORY; 168 strcpy(serv, sp->s_name); 169 } else { 170 snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 171 if (strlen(numserv) + 1 > servlen) 172 return ENI_MEMORY; 173 strcpy(serv, numserv); 174 } 175 } 176 177 switch (sa->sa_family) { 178 case AF_INET: 179 v4a = (u_int32_t) 180 ntohl(((const struct sockaddr_in *) 181 (const void *)sa)->sin_addr.s_addr); 182 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 183 flags |= NI_NUMERICHOST; 184 v4a >>= IN_CLASSA_NSHIFT; 185 if (v4a == 0) 186 flags |= NI_NUMERICHOST; 187 break; 188 #ifdef INET6 189 case AF_INET6: 190 { 191 const struct sockaddr_in6 *sin6; 192 sin6 = (const struct sockaddr_in6 *)(const void *)sa; 193 switch (sin6->sin6_addr.s6_addr[0]) { 194 case 0x00: 195 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 196 ; 197 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 198 ; 199 else 200 flags |= NI_NUMERICHOST; 201 break; 202 default: 203 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 204 flags |= NI_NUMERICHOST; 205 } 206 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 207 flags |= NI_NUMERICHOST; 208 break; 209 } 210 } 211 break; 212 #endif 213 } 214 if (host == NULL || hostlen == 0) { 215 /* 216 * do nothing in this case. 217 * in case you are wondering if "&&" is more correct than 218 * "||" here: RFC2553 says that host == NULL OR hostlen == 0 219 * means that the caller does not want the result. 220 */ 221 } else if (flags & NI_NUMERICHOST) { 222 int numaddrlen; 223 224 /* NUMERICHOST and NAMEREQD conflicts with each other */ 225 if (flags & NI_NAMEREQD) 226 return ENI_NOHOSTNAME; 227 228 switch(afd->a_af) { 229 #ifdef INET6 230 case AF_INET6: 231 { 232 int error; 233 234 if ((error = ip6_parsenumeric(sa, addr, host, 235 hostlen, flags)) != 0) 236 return(error); 237 break; 238 } 239 #endif 240 default: 241 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 242 == NULL) 243 return ENI_SYSTEM; 244 numaddrlen = strlen(numaddr); 245 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 246 return ENI_MEMORY; 247 strcpy(host, numaddr); 248 break; 249 } 250 } else { 251 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); 252 253 if (hp) { 254 #if 0 255 /* 256 * commented out, since "for local host" is not 257 * implemented here - see RFC2553 p30 258 */ 259 if (flags & NI_NOFQDN) { 260 char *p; 261 p = strchr(hp->h_name, '.'); 262 if (p) 263 *p = '\0'; 264 } 265 #endif 266 if (strlen(hp->h_name) + 1 > hostlen) { 267 return ENI_MEMORY; 268 } 269 strcpy(host, hp->h_name); 270 } else { 271 if (flags & NI_NAMEREQD) 272 return ENI_NOHOSTNAME; 273 switch(afd->a_af) { 274 #ifdef INET6 275 case AF_INET6: 276 { 277 int error; 278 279 if ((error = ip6_parsenumeric(sa, addr, host, 280 hostlen, 281 flags)) != 0) 282 return(error); 283 break; 284 } 285 #endif 286 default: 287 if (inet_ntop(afd->a_af, addr, host, 288 hostlen) == NULL) 289 return ENI_SYSTEM; 290 break; 291 } 292 } 293 } 294 return SUCCESS; 295 } 296 297 #ifdef INET6 298 static int 299 ip6_parsenumeric(sa, addr, host, hostlen, flags) 300 const struct sockaddr *sa; 301 const char *addr; 302 char *host; 303 size_t hostlen; 304 int flags; 305 { 306 int numaddrlen; 307 char numaddr[512]; 308 309 if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) 310 == NULL) 311 return ENI_SYSTEM; 312 313 numaddrlen = strlen(numaddr); 314 if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 315 return ENI_MEMORY; 316 strcpy(host, numaddr); 317 318 #ifdef NI_WITHSCOPEID 319 if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) { 320 if (flags & NI_WITHSCOPEID) 321 { 322 char scopebuf[MAXHOSTNAMELEN]; 323 int scopelen; 324 325 /* ip6_sa2str never fails */ 326 scopelen = ip6_sa2str( 327 (const struct sockaddr_in6 *)(const void *)sa, 328 scopebuf, sizeof(scopebuf), 0); 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 (size_t)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 notyet 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