1 /* 2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the project nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * Issues to be discussed: 32 * - Thread safe-ness must be checked 33 * - Return values. There seems to be no standard for return value (RFC2553) 34 * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). 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). 38 */ 39 40 #ifdef HAVE_CONFIG_H 41 #include <config.h> 42 #endif 43 44 #ifndef lint 45 static const char rcsid[] _U_ = 46 "@(#) $Header: /tcpdump/master/tcpdump/missing/getnameinfo.c,v 1.11 2003-11-16 09:36:49 guy Exp $"; 47 #endif 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 #ifdef NEED_ADDRINFO_H 62 #include "addrinfo.h" 63 #endif 64 65 #define SUCCESS 0 66 #define ANY 0 67 #define YES 1 68 #define NO 0 69 70 static struct afd { 71 int a_af; 72 int a_addrlen; 73 int a_socklen; 74 int a_off; 75 } afdl [] = { 76 #ifdef INET6 77 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 78 offsetof(struct sockaddr_in6, sin6_addr)}, 79 #endif 80 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 81 offsetof(struct sockaddr_in, sin_addr)}, 82 {0, 0, 0}, 83 }; 84 85 struct sockinet { 86 u_char si_len; 87 u_char si_family; 88 u_short si_port; 89 }; 90 91 #define ENI_NOSOCKET 0 92 #define ENI_NOSERVNAME 1 93 #define ENI_NOHOSTNAME 2 94 #define ENI_MEMORY 3 95 #define ENI_SYSTEM 4 96 #define ENI_FAMILY 5 97 #define ENI_SALEN 6 98 99 int 100 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 101 const struct sockaddr *sa; 102 size_t salen; 103 char *host; 104 size_t hostlen; 105 char *serv; 106 size_t servlen; 107 int flags; 108 { 109 struct afd *afd; 110 struct servent *sp; 111 struct hostent *hp; 112 u_short port; 113 int family, i; 114 char *addr, *p; 115 u_int32_t v4a; 116 int h_error; 117 char numserv[512]; 118 char numaddr[512]; 119 120 if (sa == NULL) 121 return ENI_NOSOCKET; 122 123 #ifdef HAVE_SA_LEN /*XXX*/ 124 if (sa->sa_len != salen) 125 return ENI_SALEN; 126 #endif 127 128 family = sa->sa_family; 129 for (i = 0; afdl[i].a_af; i++) 130 if (afdl[i].a_af == family) { 131 afd = &afdl[i]; 132 goto found; 133 } 134 return ENI_FAMILY; 135 136 found: 137 if (salen != afd->a_socklen) 138 return ENI_SALEN; 139 140 port = ((struct sockinet *)sa)->si_port; /* network byte order */ 141 addr = (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(((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 struct sockaddr_in6 *sin6; 183 sin6 = (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 /* NUMERICHOST and NAMEREQD conflicts with each other */ 214 if (flags & NI_NAMEREQD) 215 return ENI_NOHOSTNAME; 216 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 217 == NULL) 218 return ENI_SYSTEM; 219 if (strlen(numaddr) + 1 > hostlen) 220 return ENI_MEMORY; 221 strcpy(host, numaddr); 222 #if defined(INET6) && defined(NI_WITHSCOPEID) 223 if (afd->a_af == AF_INET6 && 224 (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 225 IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 226 ((struct sockaddr_in6 *)sa)->sin6_scope_id) { 227 #ifndef ALWAYS_WITHSCOPE 228 if (flags & NI_WITHSCOPEID) 229 #endif /* !ALWAYS_WITHSCOPE */ 230 { 231 char *ep = strchr(host, '\0'); 232 unsigned int ifindex = 233 ((struct sockaddr_in6 *)sa)->sin6_scope_id; 234 235 *ep = SCOPE_DELIMITER; 236 if ((if_indextoname(ifindex, ep + 1)) == NULL) 237 /* XXX what should we do? */ 238 strncpy(ep + 1, "???", 3); 239 } 240 } 241 #endif /* INET6 */ 242 } else { 243 #ifdef USE_GETIPNODEBY 244 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 245 #else 246 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); 247 #ifdef HAVE_H_ERRNO 248 h_error = h_errno; 249 #else 250 h_error = EINVAL; 251 #endif 252 #endif 253 254 if (hp) { 255 if (flags & NI_NOFQDN) { 256 p = strchr(hp->h_name, '.'); 257 if (p) *p = '\0'; 258 } 259 if (strlen(hp->h_name) + 1 > hostlen) { 260 #ifdef USE_GETIPNODEBY 261 freehostent(hp); 262 #endif 263 return ENI_MEMORY; 264 } 265 strcpy(host, hp->h_name); 266 #ifdef USE_GETIPNODEBY 267 freehostent(hp); 268 #endif 269 } else { 270 if (flags & NI_NAMEREQD) 271 return ENI_NOHOSTNAME; 272 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 273 == NULL) 274 return ENI_NOHOSTNAME; 275 if (strlen(numaddr) + 1 > hostlen) 276 return ENI_MEMORY; 277 strcpy(host, numaddr); 278 } 279 } 280 return SUCCESS; 281 } 282