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 * $FreeBSD$ 30 */ 31 32 /* 33 * Issues to be discussed: 34 * - Thread safe-ness must be checked 35 * - Return values. There seems to be no standard for return value (RFC2553) 36 * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). 37 */ 38 39 #include <sys/types.h> 40 #include <sys/socket.h> 41 #include <net/if.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <arpa/nameser.h> 45 #include <netdb.h> 46 #include <resolv.h> 47 #include <string.h> 48 #include <stddef.h> 49 50 #define SUCCESS 0 51 #define ANY 0 52 #define YES 1 53 #define NO 0 54 55 static struct afd { 56 int a_af; 57 int a_addrlen; 58 int a_socklen; 59 int a_off; 60 } afdl [] = { 61 #ifdef INET6 62 {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 63 offsetof(struct sockaddr_in6, sin6_addr)}, 64 #endif 65 {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 66 offsetof(struct sockaddr_in, sin_addr)}, 67 {0, 0, 0}, 68 }; 69 70 struct sockinet { 71 u_char si_len; 72 u_char si_family; 73 u_short si_port; 74 }; 75 76 #define ENI_NOSOCKET 0 77 #define ENI_NOSERVHOST 1 78 #define ENI_NOHOSTNAME 2 79 #define ENI_MEMORY 3 80 #define ENI_SYSTEM 4 81 #define ENI_FAMILY 5 82 #define ENI_SALEN 6 83 84 int 85 getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 86 const struct sockaddr *sa; 87 size_t salen; 88 char *host; 89 size_t hostlen; 90 char *serv; 91 size_t servlen; 92 int flags; 93 { 94 struct afd *afd; 95 struct servent *sp; 96 struct hostent *hp; 97 u_short port; 98 int family, i; 99 char *addr, *p; 100 u_long v4a; 101 int h_error; 102 char numserv[512]; 103 char numaddr[512]; 104 int noserv = 0; 105 106 if (sa == NULL) 107 return ENI_NOSOCKET; 108 109 if (sa->sa_len != salen) 110 return ENI_SALEN; 111 112 family = sa->sa_family; 113 for (i = 0; afdl[i].a_af; i++) 114 if (afdl[i].a_af == family) { 115 afd = &afdl[i]; 116 goto found; 117 } 118 return ENI_FAMILY; 119 120 found: 121 if (salen != afd->a_socklen) 122 return ENI_SALEN; 123 124 port = ((struct sockinet *)sa)->si_port; /* network byte order */ 125 addr = (char *)sa + afd->a_off; 126 127 if (serv == NULL || servlen == 0) { 128 noserv = 1; 129 } else { 130 if (flags & NI_NUMERICSERV) 131 sp = NULL; 132 else { 133 sp = getservbyport(port, 134 (flags & NI_DGRAM) ? "udp" : "tcp"); 135 } 136 if (sp) { 137 if (strlen(sp->s_name) > servlen) 138 return ENI_MEMORY; 139 strcpy(serv, sp->s_name); 140 } else { 141 snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 142 if (strlen(numserv) > servlen) 143 return ENI_MEMORY; 144 strcpy(serv, numserv); 145 } 146 } 147 148 switch (sa->sa_family) { 149 case AF_INET: 150 v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); 151 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 152 flags |= NI_NUMERICHOST; 153 v4a >>= IN_CLASSA_NSHIFT; 154 if (v4a == 0) 155 flags |= NI_NUMERICHOST; 156 break; 157 #ifdef INET6 158 case AF_INET6: 159 { 160 struct sockaddr_in6 *sin6; 161 sin6 = (struct sockaddr_in6 *)sa; 162 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || 163 IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 164 flags |= NI_NUMERICHOST; 165 } 166 break; 167 #endif 168 } 169 if (host == NULL || hostlen == 0) { 170 if (noserv == 1) 171 return ENI_NOSERVHOST; 172 } else if (flags & NI_NUMERICHOST) { 173 /* NUMERICHOST and NAMEREQD conflicts with each other */ 174 if (flags & NI_NAMEREQD) 175 return ENI_NOHOSTNAME; 176 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 177 == NULL) 178 return ENI_SYSTEM; 179 if (strlen(numaddr) > hostlen) 180 return ENI_MEMORY; 181 strcpy(host, numaddr); 182 #ifdef INET6 183 if (afd->a_af == AF_INET6 && 184 (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 185 IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 186 ((struct sockaddr_in6 *)sa)->sin6_scope_id) { 187 if (flags & NI_WITHSCOPEID) { 188 char *ep = strchr(host, '\0'); 189 unsigned int ifindex = 190 ((struct sockaddr_in6 *)sa)->sin6_scope_id; 191 char ifname[IF_NAMESIZE * 2 /* for safety */]; 192 int scopelen, numaddrlen; 193 194 if ((if_indextoname(ifindex, ifname)) == NULL) 195 return ENI_SYSTEM; 196 scopelen = strlen(ifname); 197 numaddrlen = strlen(host); 198 if (numaddrlen + 1 /* SCOPE_DELIMITER */ 199 + scopelen > hostlen) 200 return ENI_MEMORY; 201 /* 202 * Construct <numeric-addr><delim><scopeid> 203 */ 204 memcpy(host + numaddrlen + 1, ifname, scopelen); 205 host[numaddrlen] = SCOPE_DELIMITER; 206 host[numaddrlen + 1 + scopelen] = '\0'; 207 } 208 } 209 #endif /* INET6 */ 210 } else { 211 hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 212 if (hp) { 213 if (flags & NI_NOFQDN) { 214 p = strchr(hp->h_name, '.'); 215 if (p) *p = '\0'; 216 } 217 if (strlen(hp->h_name) > hostlen) { 218 freehostent(hp); 219 return ENI_MEMORY; 220 } 221 strcpy(host, hp->h_name); 222 freehostent(hp); 223 } else { 224 if (flags & NI_NAMEREQD) 225 return ENI_NOHOSTNAME; 226 if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 227 == NULL) 228 return ENI_NOHOSTNAME; 229 if (strlen(numaddr) > hostlen) 230 return ENI_MEMORY; 231 strcpy(host, numaddr); 232 } 233 } 234 return SUCCESS; 235 } 236