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 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include "getaddrinfo.h" 24 25 /* Get calloc. */ 26 #include <stdlib.h> 27 28 /* Get memcpy. */ 29 #include <string.h> 30 31 #include <stdbool.h> 32 33 #include "gettext.h" 34 #define _(String) gettext (String) 35 #define N_(String) String 36 37 #include "strdup.h" 38 39 static inline bool 40 validate_family (int family) 41 { 42 /* FIXME: Support more families. */ 43 #if HAVE_IPV4 44 if (family == PF_INET) 45 return true; 46 #endif 47 #if HAVE_IPV6 48 if (family == PF_INET6) 49 return true; 50 #endif 51 if (family == PF_UNSPEC) 52 return true; 53 return false; 54 } 55 56 /* Translate name of a service location and/or a service name to set of 57 socket addresses. */ 58 int 59 getaddrinfo (const char *restrict nodename, 60 const char *restrict servname, 61 const struct addrinfo *restrict hints, 62 struct addrinfo **restrict res) 63 { 64 struct addrinfo *tmp; 65 struct servent *se; 66 struct hostent *he; 67 size_t sinlen; 68 69 if (hints && (hints->ai_flags & ~AI_CANONNAME)) 70 /* FIXME: Support more flags. */ 71 return EAI_BADFLAGS; 72 73 if (hints && !validate_family (hints->ai_family)) 74 return EAI_FAMILY; 75 76 if (hints && 77 hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) 78 /* FIXME: Support other socktype. */ 79 return EAI_SOCKTYPE; /* FIXME: Better return code? */ 80 81 if (!nodename) 82 /* FIXME: Support server bind mode. */ 83 return EAI_NONAME; 84 85 if (servname) 86 { 87 const char *proto = 88 (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; 89 90 /* FIXME: Use getservbyname_r if available. */ 91 se = getservbyname (servname, proto); 92 93 if (!se) 94 return EAI_SERVICE; 95 } 96 97 /* FIXME: Use gethostbyname_r if available. */ 98 he = gethostbyname (nodename); 99 if (!he || he->h_addr_list[0] == NULL) 100 return EAI_NONAME; 101 102 switch (he->h_addrtype) 103 { 104 #if HAVE_IPV6 105 case PF_INET6: 106 sinlen = sizeof (struct sockaddr_in6); 107 break; 108 #endif 109 110 #if HAVE_IPV4 111 case PF_INET: 112 sinlen = sizeof (struct sockaddr_in); 113 break; 114 #endif 115 116 default: 117 return EAI_NODATA; 118 } 119 120 tmp = calloc (1, sizeof (*tmp) + sinlen); 121 if (!tmp) 122 return EAI_MEMORY; 123 124 switch (he->h_addrtype) 125 { 126 #if HAVE_IPV6 127 case PF_INET6: 128 { 129 struct sockaddr_in6 *sinp = (char *) tmp + sizeof (*tmp); 130 131 if (se) 132 sinp->sin6_port = se->s_port; 133 134 if (he->h_length != sizeof (sinp->sin6_addr)) 135 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 136 137 memcpy (&sinp->sin6_addr, he->h_addr_list[0], he->h_length); 138 139 tmp->ai_addr = (struct sockaddr *) sinp; 140 tmp->ai_addrlen = sinlen; 141 } 142 break; 143 #endif 144 145 #if HAVE_IPV4 146 case PF_INET: 147 { 148 struct sockaddr_in *sinp = (char *) tmp + sizeof (*tmp); 149 150 if (se) 151 sinp->sin_port = se->s_port; 152 153 if (he->h_length != sizeof (sinp->sin_addr)) 154 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 155 156 memcpy (&sinp->sin_addr, he->h_addr_list[0], he->h_length); 157 158 tmp->ai_addr = (struct sockaddr *) sinp; 159 tmp->ai_addrlen = sinlen; 160 } 161 break; 162 #endif 163 164 default: 165 free (tmp); 166 return EAI_NODATA; 167 } 168 169 if (hints && hints->ai_flags & AI_CANONNAME) 170 { 171 const char *cn; 172 if (he->h_name) 173 cn = he->h_name; 174 else 175 cn = nodename; 176 177 tmp->ai_canonname = strdup (cn); 178 if (!tmp->ai_canonname) 179 { 180 free (tmp); 181 return EAI_MEMORY; 182 } 183 } 184 185 tmp->ai_protocol = (hints) ? hints->ai_protocol : 0; 186 tmp->ai_socktype = (hints) ? hints->ai_socktype : 0; 187 tmp->ai_addr->sa_family = he->h_addrtype; 188 189 /* FIXME: If more than one address, create linked list of addrinfo's. */ 190 191 *res = tmp; 192 193 return 0; 194 } 195 196 /* Free `addrinfo' structure AI including associated storage. */ 197 void 198 freeaddrinfo (struct addrinfo *ai) 199 { 200 while (ai) 201 { 202 struct addrinfo *cur; 203 204 cur = ai; 205 ai = ai->ai_next; 206 207 if (cur->ai_canonname) free (cur->ai_canonname); 208 free (cur); 209 } 210 } 211