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 #include <sys/types.h> 45 #include <sys/socket.h> 46 #include <net/if.h> 47 #include <netinet/in.h> 48 #include <arpa/inet.h> 49 #include <arpa/nameser.h> 50 #include <netdb.h> 51 #include <resolv.h> 52 #include <string.h> 53 #include <stddef.h> 54 #include <errno.h> 55 56 #ifdef NEED_ADDRINFO_H 57 #include "addrinfo.h" 58 #endif 59 60 #define SUCCESS 0 61 #define ANY 0 62 #define YES 1 63 #define NO 0 64 65 static struct afd { 66 int a_af; 67 int a_addrlen; 68 int a_socklen; 69 int a_off; 70 } afdl [] = { 71 #ifdef INET6 72 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 73 offsetof(struct sockaddr_in6, sin6_addr)}, 74 #endif 75 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 76 offsetof(struct sockaddr_in, sin_addr)}, 77 {0, 0, 0}, 78 }; 79 80 struct sockinet { 81 u_char si_len; 82 u_char si_family; 83 u_short si_port; 84 }; 85 86 #define ENI_NOSOCKET 0 87 #define ENI_NOSERVNAME 1 88 #define ENI_NOHOSTNAME 2 89 #define ENI_MEMORY 3 90 #define ENI_SYSTEM 4 91 #define ENI_FAMILY 5 92 #define ENI_SALEN 6 93 94 int 95 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 96 const struct sockaddr *sa; 97 size_t salen; 98 char *host; 99 size_t hostlen; 100 char *serv; 101 size_t servlen; 102 int flags; 103 { 104 struct afd *afd; 105 struct servent *sp; 106 struct hostent *hp; 107 u_short port; 108 int family, i; 109 char *addr, *p; 110 uint32_t v4a; 111 int h_error; 112 char numserv[512]; 113 char numaddr[512]; 114 115 if (sa == NULL) 116 return ENI_NOSOCKET; 117 118 #ifdef HAVE_SA_LEN /*XXX*/ 119 if (sa->sa_len != salen) 120 return ENI_SALEN; 121 #endif 122 123 family = sa->sa_family; 124 for (i = 0; afdl[i].a_af; i++) 125 if (afdl[i].a_af == family) { 126 afd = &afdl[i]; 127 goto found; 128 } 129 return ENI_FAMILY; 130 131 found: 132 if (salen != afd->a_socklen) 133 return ENI_SALEN; 134 135 port = ((struct sockinet *)sa)->si_port; /* network byte order */ 136 addr = (char *)sa + afd->a_off; 137 138 if (serv == NULL || servlen == 0) { 139 /* 140 * do nothing in this case. 141 * in case you are wondering if "&&" is more correct than 142 * "||" here: RFC2553 says that serv == NULL OR servlen == 0 143 * means that the caller does not want the result. 144 */ 145 } else { 146 if (flags & NI_NUMERICSERV) 147 sp = NULL; 148 else { 149 sp = getservbyport(port, 150 (flags & NI_DGRAM) ? "udp" : "tcp"); 151 } 152 if (sp) { 153 if (strlen(sp->s_name) + 1 > servlen) 154 return ENI_MEMORY; 155 strcpy(serv, sp->s_name); 156 } else { 157 snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 158 if (strlen(numserv) + 1 > servlen) 159 return ENI_MEMORY; 160 strcpy(serv, numserv); 161 } 162 } 163 164 switch (sa->sa_family) { 165 case AF_INET: 166 v4a = (uint32_t) 167 ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); 168 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 169 flags |= NI_NUMERICHOST; 170 v4a >>= IN_CLASSA_NSHIFT; 171 if (v4a == 0) 172 flags |= NI_NUMERICHOST; 173 break; 174 #ifdef INET6 175 case AF_INET6: 176 { 177 struct sockaddr_in6 *sin6; 178 sin6 = (struct sockaddr_in6 *)sa; 179 switch (sin6->sin6_addr.s6_addr[0]) { 180 case 0x00: 181 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 182 ; 183 else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 184 ; 185 else 186 flags |= NI_NUMERICHOST; 187 break; 188 default: 189 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 190 flags |= NI_NUMERICHOST; 191 } 192 else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 193 flags |= NI_NUMERICHOST; 194 break; 195 } 196 } 197 break; 198 #endif 199 } 200 if (host == NULL || hostlen == 0) { 201 /* 202 * do nothing in this case. 203 * in case you are wondering if "&&" is more correct than 204 * "||" here: RFC2553 says that host == NULL OR hostlen == 0 205 * means that the caller does not want the result. 206 */ 207 } else if (flags & NI_NUMERICHOST) { 208 /* NUMERICHOST and NAMEREQD conflicts with each other */ 209 if (flags & NI_NAMEREQD) 210 return ENI_NOHOSTNAME; 211 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 212 == NULL) 213 return ENI_SYSTEM; 214 if (strlen(numaddr) + 1 > hostlen) 215 return ENI_MEMORY; 216 strcpy(host, numaddr); 217 #if defined(INET6) && defined(NI_WITHSCOPEID) 218 if (afd->a_af == AF_INET6 && 219 (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 220 IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 221 ((struct sockaddr_in6 *)sa)->sin6_scope_id) { 222 #ifndef ALWAYS_WITHSCOPE 223 if (flags & NI_WITHSCOPEID) 224 #endif /* !ALWAYS_WITHSCOPE */ 225 { 226 char *ep = strchr(host, '\0'); 227 unsigned int ifindex = 228 ((struct sockaddr_in6 *)sa)->sin6_scope_id; 229 230 *ep = SCOPE_DELIMITER; 231 if ((if_indextoname(ifindex, ep + 1)) == NULL) 232 /* XXX what should we do? */ 233 strncpy(ep + 1, "???", 3); 234 } 235 } 236 #endif /* INET6 */ 237 } else { 238 #ifdef USE_GETIPNODEBY 239 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 240 #else 241 hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); 242 #ifdef HAVE_H_ERRNO 243 h_error = h_errno; 244 #else 245 h_error = EINVAL; 246 #endif 247 #endif 248 249 if (hp) { 250 if (flags & NI_NOFQDN) { 251 p = strchr(hp->h_name, '.'); 252 if (p) *p = '\0'; 253 } 254 if (strlen(hp->h_name) + 1 > hostlen) { 255 #ifdef USE_GETIPNODEBY 256 freehostent(hp); 257 #endif 258 return ENI_MEMORY; 259 } 260 strcpy(host, hp->h_name); 261 #ifdef USE_GETIPNODEBY 262 freehostent(hp); 263 #endif 264 } else { 265 if (flags & NI_NAMEREQD) 266 return ENI_NOHOSTNAME; 267 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 268 == NULL) 269 return ENI_NOHOSTNAME; 270 if (strlen(numaddr) + 1 > hostlen) 271 return ENI_MEMORY; 272 strcpy(host, numaddr); 273 } 274 } 275 return SUCCESS; 276 } 277