1 /* Get address information (partial implementation). 2 Copyright (C) 1997, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. 3 Contributed by Simon Josefsson <simon@josefsson.org>. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2, or (at your option) 8 any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software Foundation, 17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 #include <sys/cdefs.h> 19 __RCSID("$NetBSD: getaddrinfo.c,v 1.2 2016/05/17 14:00:09 christos Exp $"); 20 21 22 #ifdef HAVE_CONFIG_H 23 # include <config.h> 24 #endif 25 26 #include "getaddrinfo.h" 27 28 /* Get calloc. */ 29 #include <stdlib.h> 30 31 /* Get memcpy. */ 32 #include <string.h> 33 34 #include <stdbool.h> 35 36 #include "gettext.h" 37 #define _(String) gettext (String) 38 #define N_(String) String 39 40 #include "strdup.h" 41 42 static inline bool 43 validate_family (int family) 44 { 45 /* FIXME: Support more families. */ 46 #if HAVE_IPV4 47 if (family == PF_INET) 48 return true; 49 #endif 50 #if HAVE_IPV6 51 if (family == PF_INET6) 52 return true; 53 #endif 54 if (family == PF_UNSPEC) 55 return true; 56 return false; 57 } 58 59 /* Translate name of a service location and/or a service name to set of 60 socket addresses. */ 61 int 62 getaddrinfo (const char *restrict nodename, 63 const char *restrict servname, 64 const struct addrinfo *restrict hints, 65 struct addrinfo **restrict res) 66 { 67 struct addrinfo *tmp; 68 struct servent *se; 69 struct hostent *he; 70 size_t sinlen; 71 72 if (hints && (hints->ai_flags & ~AI_CANONNAME)) 73 /* FIXME: Support more flags. */ 74 return EAI_BADFLAGS; 75 76 if (hints && !validate_family (hints->ai_family)) 77 return EAI_FAMILY; 78 79 if (hints && 80 hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) 81 /* FIXME: Support other socktype. */ 82 return EAI_SOCKTYPE; /* FIXME: Better return code? */ 83 84 if (!nodename) 85 /* FIXME: Support server bind mode. */ 86 return EAI_NONAME; 87 88 if (servname) 89 { 90 const char *proto = 91 (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; 92 93 /* FIXME: Use getservbyname_r if available. */ 94 se = getservbyname (servname, proto); 95 96 if (!se) 97 return EAI_SERVICE; 98 } 99 100 /* FIXME: Use gethostbyname_r if available. */ 101 he = gethostbyname (nodename); 102 if (!he || he->h_addr_list[0] == NULL) 103 return EAI_NONAME; 104 105 switch (he->h_addrtype) 106 { 107 #if HAVE_IPV6 108 case PF_INET6: 109 sinlen = sizeof (struct sockaddr_in6); 110 break; 111 #endif 112 113 #if HAVE_IPV4 114 case PF_INET: 115 sinlen = sizeof (struct sockaddr_in); 116 break; 117 #endif 118 119 default: 120 return EAI_NODATA; 121 } 122 123 tmp = calloc (1, sizeof (*tmp) + sinlen); 124 if (!tmp) 125 return EAI_MEMORY; 126 127 switch (he->h_addrtype) 128 { 129 #if HAVE_IPV6 130 case PF_INET6: 131 { 132 struct sockaddr_in6 *sinp = (char *) tmp + sizeof (*tmp); 133 134 if (se) 135 sinp->sin6_port = se->s_port; 136 137 if (he->h_length != sizeof (sinp->sin6_addr)) 138 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 139 140 memcpy (&sinp->sin6_addr, he->h_addr_list[0], he->h_length); 141 142 tmp->ai_addr = (struct sockaddr *) sinp; 143 tmp->ai_addrlen = sinlen; 144 } 145 break; 146 #endif 147 148 #if HAVE_IPV4 149 case PF_INET: 150 { 151 struct sockaddr_in *sinp = (char *) tmp + sizeof (*tmp); 152 153 if (se) 154 sinp->sin_port = se->s_port; 155 156 if (he->h_length != sizeof (sinp->sin_addr)) 157 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 158 159 memcpy (&sinp->sin_addr, he->h_addr_list[0], he->h_length); 160 161 tmp->ai_addr = (struct sockaddr *) sinp; 162 tmp->ai_addrlen = sinlen; 163 } 164 break; 165 #endif 166 167 default: 168 free (tmp); 169 return EAI_NODATA; 170 } 171 172 if (hints && hints->ai_flags & AI_CANONNAME) 173 { 174 const char *cn; 175 if (he->h_name) 176 cn = he->h_name; 177 else 178 cn = nodename; 179 180 tmp->ai_canonname = strdup (cn); 181 if (!tmp->ai_canonname) 182 { 183 free (tmp); 184 return EAI_MEMORY; 185 } 186 } 187 188 tmp->ai_protocol = (hints) ? hints->ai_protocol : 0; 189 tmp->ai_socktype = (hints) ? hints->ai_socktype : 0; 190 tmp->ai_addr->sa_family = he->h_addrtype; 191 192 /* FIXME: If more than one address, create linked list of addrinfo's. */ 193 194 *res = tmp; 195 196 return 0; 197 } 198 199 /* Free `addrinfo' structure AI including associated storage. */ 200 void 201 freeaddrinfo (struct addrinfo *ai) 202 { 203 while (ai) 204 { 205 struct addrinfo *cur; 206 207 cur = ai; 208 ai = ai->ai_next; 209 210 if (cur->ai_canonname) free (cur->ai_canonname); 211 free (cur); 212 } 213 } 214