1/* Get address information (partial implementation). 2 Copyright (C) 1997, 2001-2002, 2004-2014 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 3, 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, see <http://www.gnu.org/licenses/>. */ 17 18/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc 19 optimizes away the sa == NULL test below. */ 20#define _GL_ARG_NONNULL(params) 21 22#include <config.h> 23 24#include <netdb.h> 25 26#if HAVE_NETINET_IN_H 27# include <netinet/in.h> 28#endif 29 30/* Get inet_ntop. */ 31#include <arpa/inet.h> 32 33/* Get calloc. */ 34#include <stdlib.h> 35 36/* Get memcpy, strdup. */ 37#include <string.h> 38 39/* Get snprintf. */ 40#include <stdio.h> 41 42#include <stdbool.h> 43 44#include "gettext.h" 45#define _(String) gettext (String) 46#define N_(String) String 47 48/* BeOS has AF_INET, but not PF_INET. */ 49#ifndef PF_INET 50# define PF_INET AF_INET 51#endif 52/* BeOS also lacks PF_UNSPEC. */ 53#ifndef PF_UNSPEC 54# define PF_UNSPEC 0 55#endif 56 57#if defined _WIN32 || defined __WIN32__ 58# define WINDOWS_NATIVE 59#endif 60 61/* gl_sockets_startup */ 62#include "sockets.h" 63 64#ifdef WINDOWS_NATIVE 65typedef int (WSAAPI *getaddrinfo_func) (const char*, const char*, 66 const struct addrinfo*, 67 struct addrinfo**); 68typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo*); 69typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr*, 70 socklen_t, char*, DWORD, 71 char*, DWORD, int); 72 73static getaddrinfo_func getaddrinfo_ptr = NULL; 74static freeaddrinfo_func freeaddrinfo_ptr = NULL; 75static getnameinfo_func getnameinfo_ptr = NULL; 76 77static int 78use_win32_p (void) 79{ 80 static int done = 0; 81 HMODULE h; 82 83 if (done) 84 return getaddrinfo_ptr ? 1 : 0; 85 86 done = 1; 87 88 h = GetModuleHandle ("ws2_32.dll"); 89 90 if (h) 91 { 92 getaddrinfo_ptr = (getaddrinfo_func) GetProcAddress (h, "getaddrinfo"); 93 freeaddrinfo_ptr = (freeaddrinfo_func) GetProcAddress (h, "freeaddrinfo"); 94 getnameinfo_ptr = (getnameinfo_func) GetProcAddress (h, "getnameinfo"); 95 } 96 97 /* If either is missing, something is odd. */ 98 if (!getaddrinfo_ptr || !freeaddrinfo_ptr || !getnameinfo_ptr) 99 { 100 getaddrinfo_ptr = NULL; 101 freeaddrinfo_ptr = NULL; 102 getnameinfo_ptr = NULL; 103 return 0; 104 } 105 106 gl_sockets_startup (SOCKETS_1_1); 107 108 return 1; 109} 110#endif 111 112static bool 113validate_family (int family) 114{ 115 /* FIXME: Support more families. */ 116#if HAVE_IPV4 117 if (family == PF_INET) 118 return true; 119#endif 120#if HAVE_IPV6 121 if (family == PF_INET6) 122 return true; 123#endif 124 if (family == PF_UNSPEC) 125 return true; 126 return false; 127} 128 129/* Translate name of a service location and/or a service name to set of 130 socket addresses. */ 131int 132getaddrinfo (const char *restrict nodename, 133 const char *restrict servname, 134 const struct addrinfo *restrict hints, 135 struct addrinfo **restrict res) 136{ 137 struct addrinfo *tmp; 138 int port = 0; 139 struct hostent *he; 140 void *storage; 141 size_t size; 142#if HAVE_IPV6 143 struct v6_pair { 144 struct addrinfo addrinfo; 145 struct sockaddr_in6 sockaddr_in6; 146 }; 147#endif 148#if HAVE_IPV4 149 struct v4_pair { 150 struct addrinfo addrinfo; 151 struct sockaddr_in sockaddr_in; 152 }; 153#endif 154 155#ifdef WINDOWS_NATIVE 156 if (use_win32_p ()) 157 return getaddrinfo_ptr (nodename, servname, hints, res); 158#endif 159 160 if (hints && (hints->ai_flags & ~(AI_CANONNAME|AI_PASSIVE))) 161 /* FIXME: Support more flags. */ 162 return EAI_BADFLAGS; 163 164 if (hints && !validate_family (hints->ai_family)) 165 return EAI_FAMILY; 166 167 if (hints && 168 hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) 169 /* FIXME: Support other socktype. */ 170 return EAI_SOCKTYPE; /* FIXME: Better return code? */ 171 172 if (!nodename) 173 { 174 if (!(hints->ai_flags & AI_PASSIVE)) 175 return EAI_NONAME; 176 177#ifdef HAVE_IPV6 178 nodename = (hints->ai_family == AF_INET6) ? "::" : "0.0.0.0"; 179#else 180 nodename = "0.0.0.0"; 181#endif 182 } 183 184 if (servname) 185 { 186 struct servent *se = NULL; 187 const char *proto = 188 (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp"; 189 190 if (hints == NULL || !(hints->ai_flags & AI_NUMERICSERV)) 191 /* FIXME: Use getservbyname_r if available. */ 192 se = getservbyname (servname, proto); 193 194 if (!se) 195 { 196 char *c; 197 if (!(*servname >= '0' && *servname <= '9')) 198 return EAI_NONAME; 199 port = strtoul (servname, &c, 10); 200 if (*c || port > 0xffff) 201 return EAI_NONAME; 202 port = htons (port); 203 } 204 else 205 port = se->s_port; 206 } 207 208 /* FIXME: Use gethostbyname_r if available. */ 209 he = gethostbyname (nodename); 210 if (!he || he->h_addr_list[0] == NULL) 211 return EAI_NONAME; 212 213 switch (he->h_addrtype) 214 { 215#if HAVE_IPV6 216 case PF_INET6: 217 size = sizeof (struct v6_pair); 218 break; 219#endif 220 221#if HAVE_IPV4 222 case PF_INET: 223 size = sizeof (struct v4_pair); 224 break; 225#endif 226 227 default: 228 return EAI_NODATA; 229 } 230 231 storage = calloc (1, size); 232 if (!storage) 233 return EAI_MEMORY; 234 235 switch (he->h_addrtype) 236 { 237#if HAVE_IPV6 238 case PF_INET6: 239 { 240 struct v6_pair *p = storage; 241 struct sockaddr_in6 *sinp = &p->sockaddr_in6; 242 tmp = &p->addrinfo; 243 244 if (port) 245 sinp->sin6_port = port; 246 247 if (he->h_length != sizeof (sinp->sin6_addr)) 248 { 249 free (storage); 250 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 251 } 252 253 memcpy (&sinp->sin6_addr, he->h_addr_list[0], sizeof sinp->sin6_addr); 254 255 tmp->ai_addr = (struct sockaddr *) sinp; 256 tmp->ai_addrlen = sizeof *sinp; 257 } 258 break; 259#endif 260 261#if HAVE_IPV4 262 case PF_INET: 263 { 264 struct v4_pair *p = storage; 265 struct sockaddr_in *sinp = &p->sockaddr_in; 266 tmp = &p->addrinfo; 267 268 if (port) 269 sinp->sin_port = port; 270 271 if (he->h_length != sizeof (sinp->sin_addr)) 272 { 273 free (storage); 274 return EAI_SYSTEM; /* FIXME: Better return code? Set errno? */ 275 } 276 277 memcpy (&sinp->sin_addr, he->h_addr_list[0], sizeof sinp->sin_addr); 278 279 tmp->ai_addr = (struct sockaddr *) sinp; 280 tmp->ai_addrlen = sizeof *sinp; 281 } 282 break; 283#endif 284 285 default: 286 free (storage); 287 return EAI_NODATA; 288 } 289 290 if (hints && hints->ai_flags & AI_CANONNAME) 291 { 292 const char *cn; 293 if (he->h_name) 294 cn = he->h_name; 295 else 296 cn = nodename; 297 298 tmp->ai_canonname = strdup (cn); 299 if (!tmp->ai_canonname) 300 { 301 free (storage); 302 return EAI_MEMORY; 303 } 304 } 305 306 tmp->ai_protocol = (hints) ? hints->ai_protocol : 0; 307 tmp->ai_socktype = (hints) ? hints->ai_socktype : 0; 308 tmp->ai_addr->sa_family = he->h_addrtype; 309 tmp->ai_family = he->h_addrtype; 310 311#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 312 switch (he->h_addrtype) 313 { 314#if HAVE_IPV4 315 case AF_INET: 316 tmp->ai_addr->sa_len = sizeof (struct sockaddr_in); 317 break; 318#endif 319#if HAVE_IPV6 320 case AF_INET6: 321 tmp->ai_addr->sa_len = sizeof (struct sockaddr_in6); 322 break; 323#endif 324 } 325#endif 326 327 /* FIXME: If more than one address, create linked list of addrinfo's. */ 328 329 *res = tmp; 330 331 return 0; 332} 333 334/* Free 'addrinfo' structure AI including associated storage. */ 335void 336freeaddrinfo (struct addrinfo *ai) 337{ 338#ifdef WINDOWS_NATIVE 339 if (use_win32_p ()) 340 { 341 freeaddrinfo_ptr (ai); 342 return; 343 } 344#endif 345 346 while (ai) 347 { 348 struct addrinfo *cur; 349 350 cur = ai; 351 ai = ai->ai_next; 352 353 free (cur->ai_canonname); 354 free (cur); 355 } 356} 357 358int 359getnameinfo (const struct sockaddr *restrict sa, socklen_t salen, 360 char *restrict node, socklen_t nodelen, 361 char *restrict service, socklen_t servicelen, 362 int flags) 363{ 364#ifdef WINDOWS_NATIVE 365 if (use_win32_p ()) 366 return getnameinfo_ptr (sa, salen, node, nodelen, 367 service, servicelen, flags); 368#endif 369 370 /* FIXME: Support other flags. */ 371 if ((node && nodelen > 0 && !(flags & NI_NUMERICHOST)) || 372 (service && servicelen > 0 && !(flags & NI_NUMERICHOST)) || 373 (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV))) 374 return EAI_BADFLAGS; 375 376 if (sa == NULL || salen < sizeof (sa->sa_family)) 377 return EAI_FAMILY; 378 379 switch (sa->sa_family) 380 { 381#if HAVE_IPV4 382 case AF_INET: 383 if (salen < sizeof (struct sockaddr_in)) 384 return EAI_FAMILY; 385 break; 386#endif 387#if HAVE_IPV6 388 case AF_INET6: 389 if (salen < sizeof (struct sockaddr_in6)) 390 return EAI_FAMILY; 391 break; 392#endif 393 default: 394 return EAI_FAMILY; 395 } 396 397 if (node && nodelen > 0 && flags & NI_NUMERICHOST) 398 { 399 switch (sa->sa_family) 400 { 401#if HAVE_IPV4 402 case AF_INET: 403 if (!inet_ntop (AF_INET, 404 &(((const struct sockaddr_in *) sa)->sin_addr), 405 node, nodelen)) 406 return EAI_SYSTEM; 407 break; 408#endif 409 410#if HAVE_IPV6 411 case AF_INET6: 412 if (!inet_ntop (AF_INET6, 413 &(((const struct sockaddr_in6 *) sa)->sin6_addr), 414 node, nodelen)) 415 return EAI_SYSTEM; 416 break; 417#endif 418 419 default: 420 return EAI_FAMILY; 421 } 422 } 423 424 if (service && servicelen > 0 && flags & NI_NUMERICSERV) 425 switch (sa->sa_family) 426 { 427#if HAVE_IPV4 428 case AF_INET: 429#endif 430#if HAVE_IPV6 431 case AF_INET6: 432#endif 433 { 434 unsigned short int port 435 = ntohs (((const struct sockaddr_in *) sa)->sin_port); 436 if (servicelen <= snprintf (service, servicelen, "%u", port)) 437 return EAI_OVERFLOW; 438 } 439 break; 440 } 441 442 return 0; 443} 444