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