curl_addrinfo.c (16615B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #include <curl/curl.h> 28 29 #ifdef HAVE_NETINET_IN_H 30 # include <netinet/in.h> 31 #endif 32 #ifdef HAVE_NETINET_IN6_H 33 # include <netinet/in6.h> 34 #endif 35 #ifdef HAVE_NETDB_H 36 # include <netdb.h> 37 #endif 38 #ifdef HAVE_ARPA_INET_H 39 # include <arpa/inet.h> 40 #endif 41 #ifdef HAVE_SYS_UN_H 42 # include <sys/un.h> 43 #endif 44 45 #ifdef __VMS 46 # include <in.h> 47 # include <inet.h> 48 #endif 49 50 #include <stddef.h> 51 52 #include "curl_addrinfo.h" 53 #include "fake_addrinfo.h" 54 #include "curlx/inet_pton.h" 55 #include "curlx/warnless.h" 56 /* The last 3 #include files should be in this order */ 57 #include "curl_printf.h" 58 #include "curl_memory.h" 59 #include "memdebug.h" 60 61 /* 62 * Curl_freeaddrinfo() 63 * 64 * This is used to free a linked list of Curl_addrinfo structs along 65 * with all its associated allocated storage. This function should be 66 * called once for each successful call to Curl_getaddrinfo_ex() or to 67 * any function call which actually allocates a Curl_addrinfo struct. 68 */ 69 70 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ 71 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) 72 /* workaround icc 9.1 optimizer issue */ 73 # define vqualifier volatile 74 #else 75 # define vqualifier 76 #endif 77 78 void 79 Curl_freeaddrinfo(struct Curl_addrinfo *cahead) 80 { 81 struct Curl_addrinfo *vqualifier canext; 82 struct Curl_addrinfo *ca; 83 84 for(ca = cahead; ca; ca = canext) { 85 canext = ca->ai_next; 86 free(ca); 87 } 88 } 89 90 91 #ifdef HAVE_GETADDRINFO 92 /* 93 * Curl_getaddrinfo_ex() 94 * 95 * This is a wrapper function around system's getaddrinfo(), with 96 * the only difference that instead of returning a linked list of 97 * addrinfo structs this one returns a linked list of Curl_addrinfo 98 * ones. The memory allocated by this function *MUST* be free'd with 99 * Curl_freeaddrinfo(). For each successful call to this function 100 * there must be an associated call later to Curl_freeaddrinfo(). 101 * 102 * There should be no single call to system's getaddrinfo() in the 103 * whole library, any such call should be 'routed' through this one. 104 */ 105 106 int 107 Curl_getaddrinfo_ex(const char *nodename, 108 const char *servname, 109 const struct addrinfo *hints, 110 struct Curl_addrinfo **result) 111 { 112 const struct addrinfo *ai; 113 struct addrinfo *aihead; 114 struct Curl_addrinfo *cafirst = NULL; 115 struct Curl_addrinfo *calast = NULL; 116 struct Curl_addrinfo *ca; 117 size_t ss_size; 118 int error; 119 120 *result = NULL; /* assume failure */ 121 122 error = CURL_GETADDRINFO(nodename, servname, hints, &aihead); 123 if(error) 124 return error; 125 126 /* traverse the addrinfo list */ 127 128 for(ai = aihead; ai != NULL; ai = ai->ai_next) { 129 size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0; 130 /* ignore elements with unsupported address family, */ 131 /* settle family-specific sockaddr structure size. */ 132 if(ai->ai_family == AF_INET) 133 ss_size = sizeof(struct sockaddr_in); 134 #ifdef USE_IPV6 135 else if(ai->ai_family == AF_INET6) 136 ss_size = sizeof(struct sockaddr_in6); 137 #endif 138 else 139 continue; 140 141 /* ignore elements without required address info */ 142 if(!ai->ai_addr || !(ai->ai_addrlen > 0)) 143 continue; 144 145 /* ignore elements with bogus address size */ 146 if((size_t)ai->ai_addrlen < ss_size) 147 continue; 148 149 ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); 150 if(!ca) { 151 error = EAI_MEMORY; 152 break; 153 } 154 155 /* copy each structure member individually, member ordering, */ 156 /* size, or padding might be different for each platform. */ 157 158 ca->ai_flags = ai->ai_flags; 159 ca->ai_family = ai->ai_family; 160 ca->ai_socktype = ai->ai_socktype; 161 ca->ai_protocol = ai->ai_protocol; 162 ca->ai_addrlen = (curl_socklen_t)ss_size; 163 ca->ai_addr = NULL; 164 ca->ai_canonname = NULL; 165 ca->ai_next = NULL; 166 167 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); 168 memcpy(ca->ai_addr, ai->ai_addr, ss_size); 169 170 if(namelen) { 171 ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); 172 memcpy(ca->ai_canonname, ai->ai_canonname, namelen); 173 } 174 175 /* if the return list is empty, this becomes the first element */ 176 if(!cafirst) 177 cafirst = ca; 178 179 /* add this element last in the return list */ 180 if(calast) 181 calast->ai_next = ca; 182 calast = ca; 183 184 } 185 186 /* destroy the addrinfo list */ 187 if(aihead) 188 CURL_FREEADDRINFO(aihead); 189 190 /* if we failed, also destroy the Curl_addrinfo list */ 191 if(error) { 192 Curl_freeaddrinfo(cafirst); 193 cafirst = NULL; 194 } 195 else if(!cafirst) { 196 #ifdef EAI_NONAME 197 /* rfc3493 conformant */ 198 error = EAI_NONAME; 199 #else 200 /* rfc3493 obsoleted */ 201 error = EAI_NODATA; 202 #endif 203 #ifdef USE_WINSOCK 204 SET_SOCKERRNO(error); 205 #endif 206 } 207 208 *result = cafirst; 209 210 /* This is not a CURLcode */ 211 return error; 212 } 213 #endif /* HAVE_GETADDRINFO */ 214 215 216 /* 217 * Curl_he2ai() 218 * 219 * This function returns a pointer to the first element of a newly allocated 220 * Curl_addrinfo struct linked list filled with the data of a given hostent. 221 * Curl_addrinfo is meant to work like the addrinfo struct does for an IPv6 222 * stack, but usable also for IPv4, all hosts and environments. 223 * 224 * The memory allocated by this function *MUST* be free'd later on calling 225 * Curl_freeaddrinfo(). For each successful call to this function there 226 * must be an associated call later to Curl_freeaddrinfo(). 227 * 228 * Curl_addrinfo defined in "lib/curl_addrinfo.h" 229 * 230 * struct Curl_addrinfo { 231 * int ai_flags; 232 * int ai_family; 233 * int ai_socktype; 234 * int ai_protocol; 235 * curl_socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo * 236 * char *ai_canonname; 237 * struct sockaddr *ai_addr; 238 * struct Curl_addrinfo *ai_next; 239 * }; 240 * 241 * hostent defined in <netdb.h> 242 * 243 * struct hostent { 244 * char *h_name; 245 * char **h_aliases; 246 * int h_addrtype; 247 * int h_length; 248 * char **h_addr_list; 249 * }; 250 * 251 * for backward compatibility: 252 * 253 * #define h_addr h_addr_list[0] 254 */ 255 256 #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) 257 struct Curl_addrinfo * 258 Curl_he2ai(const struct hostent *he, int port) 259 { 260 struct Curl_addrinfo *ai; 261 struct Curl_addrinfo *prevai = NULL; 262 struct Curl_addrinfo *firstai = NULL; 263 struct sockaddr_in *addr; 264 #ifdef USE_IPV6 265 struct sockaddr_in6 *addr6; 266 #endif 267 CURLcode result = CURLE_OK; 268 int i; 269 char *curr; 270 271 if(!he) 272 /* no input == no output! */ 273 return NULL; 274 275 DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); 276 277 for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { 278 size_t ss_size; 279 size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */ 280 #ifdef USE_IPV6 281 if(he->h_addrtype == AF_INET6) 282 ss_size = sizeof(struct sockaddr_in6); 283 else 284 #endif 285 ss_size = sizeof(struct sockaddr_in); 286 287 /* allocate memory to hold the struct, the address and the name */ 288 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen); 289 if(!ai) { 290 result = CURLE_OUT_OF_MEMORY; 291 break; 292 } 293 /* put the address after the struct */ 294 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); 295 /* then put the name after the address */ 296 ai->ai_canonname = (char *)ai->ai_addr + ss_size; 297 memcpy(ai->ai_canonname, he->h_name, namelen); 298 299 if(!firstai) 300 /* store the pointer we want to return from this function */ 301 firstai = ai; 302 303 if(prevai) 304 /* make the previous entry point to this */ 305 prevai->ai_next = ai; 306 307 ai->ai_family = he->h_addrtype; 308 309 /* we return all names as STREAM, so when using this address for TFTP 310 the type must be ignored and conn->socktype be used instead! */ 311 ai->ai_socktype = SOCK_STREAM; 312 313 ai->ai_addrlen = (curl_socklen_t)ss_size; 314 315 /* leave the rest of the struct filled with zero */ 316 317 switch(ai->ai_family) { 318 case AF_INET: 319 addr = (void *)ai->ai_addr; /* storage area for this info */ 320 321 memcpy(&addr->sin_addr, curr, sizeof(struct in_addr)); 322 addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype); 323 addr->sin_port = htons((unsigned short)port); 324 break; 325 326 #ifdef USE_IPV6 327 case AF_INET6: 328 addr6 = (void *)ai->ai_addr; /* storage area for this info */ 329 330 memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr)); 331 addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype); 332 addr6->sin6_port = htons((unsigned short)port); 333 break; 334 #endif 335 } 336 337 prevai = ai; 338 } 339 340 if(result) { 341 Curl_freeaddrinfo(firstai); 342 firstai = NULL; 343 } 344 345 return firstai; 346 } 347 #endif 348 349 /* 350 * Curl_ip2addr() 351 * 352 * This function takes an Internet address, in binary form, as input parameter 353 * along with its address family and the string version of the address, and it 354 * returns a Curl_addrinfo chain filled in correctly with information for the 355 * given address/host 356 */ 357 358 struct Curl_addrinfo * 359 Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) 360 { 361 struct Curl_addrinfo *ai; 362 size_t addrsize; 363 size_t namelen; 364 struct sockaddr_in *addr; 365 #ifdef USE_IPV6 366 struct sockaddr_in6 *addr6; 367 #endif 368 369 DEBUGASSERT(inaddr && hostname); 370 371 namelen = strlen(hostname) + 1; 372 373 if(af == AF_INET) 374 addrsize = sizeof(struct sockaddr_in); 375 #ifdef USE_IPV6 376 else if(af == AF_INET6) 377 addrsize = sizeof(struct sockaddr_in6); 378 #endif 379 else 380 return NULL; 381 382 /* allocate memory to hold the struct, the address and the name */ 383 ai = calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen); 384 if(!ai) 385 return NULL; 386 /* put the address after the struct */ 387 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); 388 /* then put the name after the address */ 389 ai->ai_canonname = (char *)ai->ai_addr + addrsize; 390 memcpy(ai->ai_canonname, hostname, namelen); 391 ai->ai_family = af; 392 ai->ai_socktype = SOCK_STREAM; 393 ai->ai_addrlen = (curl_socklen_t)addrsize; 394 /* leave the rest of the struct filled with zero */ 395 396 switch(af) { 397 case AF_INET: 398 addr = (void *)ai->ai_addr; /* storage area for this info */ 399 400 memcpy(&addr->sin_addr, inaddr, sizeof(struct in_addr)); 401 #ifdef __MINGW32__ 402 addr->sin_family = (short)af; 403 #else 404 addr->sin_family = (CURL_SA_FAMILY_T)af; 405 #endif 406 addr->sin_port = htons((unsigned short)port); 407 break; 408 409 #ifdef USE_IPV6 410 case AF_INET6: 411 addr6 = (void *)ai->ai_addr; /* storage area for this info */ 412 413 memcpy(&addr6->sin6_addr, inaddr, sizeof(struct in6_addr)); 414 #ifdef __MINGW32__ 415 addr6->sin6_family = (short)af; 416 #else 417 addr6->sin6_family = (CURL_SA_FAMILY_T)af; 418 #endif 419 addr6->sin6_port = htons((unsigned short)port); 420 break; 421 #endif 422 } 423 424 return ai; 425 } 426 427 /* 428 * Given an IPv4 or IPv6 dotted string address, this converts it to a proper 429 * allocated Curl_addrinfo struct and returns it. 430 */ 431 struct Curl_addrinfo *Curl_str2addr(char *address, int port) 432 { 433 struct in_addr in; 434 if(curlx_inet_pton(AF_INET, address, &in) > 0) 435 /* This is a dotted IP address 123.123.123.123-style */ 436 return Curl_ip2addr(AF_INET, &in, address, port); 437 #ifdef USE_IPV6 438 { 439 struct in6_addr in6; 440 if(curlx_inet_pton(AF_INET6, address, &in6) > 0) 441 /* This is a dotted IPv6 address ::1-style */ 442 return Curl_ip2addr(AF_INET6, &in6, address, port); 443 } 444 #endif 445 return NULL; /* bad input format */ 446 } 447 448 #ifdef USE_UNIX_SOCKETS 449 /** 450 * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo 451 * struct initialized with this path. 452 * Set '*longpath' to TRUE if the error is a too long path. 453 */ 454 struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, 455 bool abstract) 456 { 457 struct Curl_addrinfo *ai; 458 struct sockaddr_un *sa_un; 459 size_t path_len; 460 461 *longpath = FALSE; 462 463 ai = calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); 464 if(!ai) 465 return NULL; 466 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); 467 468 sa_un = (void *) ai->ai_addr; 469 sa_un->sun_family = AF_UNIX; 470 471 /* sun_path must be able to store the null-terminated path */ 472 path_len = strlen(path) + 1; 473 if(path_len > sizeof(sa_un->sun_path)) { 474 free(ai); 475 *longpath = TRUE; 476 return NULL; 477 } 478 479 ai->ai_family = AF_UNIX; 480 ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ 481 ai->ai_addrlen = (curl_socklen_t) 482 ((offsetof(struct sockaddr_un, sun_path) + path_len) & 0x7FFFFFFF); 483 484 /* Abstract Unix domain socket have NULL prefix instead of suffix */ 485 if(abstract) 486 memcpy(sa_un->sun_path + 1, path, path_len - 1); 487 else 488 memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */ 489 490 return ai; 491 } 492 #endif 493 494 #if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ 495 defined(HAVE_FREEADDRINFO) 496 /* 497 * curl_dbg_freeaddrinfo() 498 * 499 * This is strictly for memory tracing and are using the same style as the 500 * family otherwise present in memdebug.c. I put these ones here since they 501 * require a bunch of structs I did not want to include in memdebug.c 502 */ 503 504 void 505 curl_dbg_freeaddrinfo(struct addrinfo *freethis, 506 int line, const char *source) 507 { 508 curl_dbg_log("ADDR %s:%d freeaddrinfo(%p)\n", 509 source, line, (void *)freethis); 510 #ifdef USE_LWIPSOCK 511 lwip_freeaddrinfo(freethis); 512 #elif defined(USE_FAKE_GETADDRINFO) 513 { 514 const char *env = getenv("CURL_DNS_SERVER"); 515 if(env) 516 r_freeaddrinfo(freethis); 517 else 518 freeaddrinfo(freethis); 519 } 520 #else 521 freeaddrinfo(freethis); 522 #endif 523 } 524 #endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */ 525 526 527 #if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) 528 /* 529 * curl_dbg_getaddrinfo() 530 * 531 * This is strictly for memory tracing and are using the same style as the 532 * family otherwise present in memdebug.c. I put these ones here since they 533 * require a bunch of structs I did not want to include in memdebug.c 534 */ 535 536 int 537 curl_dbg_getaddrinfo(const char *hostname, 538 const char *service, 539 const struct addrinfo *hints, 540 struct addrinfo **result, 541 int line, const char *source) 542 { 543 #ifdef USE_LWIPSOCK 544 int res = lwip_getaddrinfo(hostname, service, hints, result); 545 #elif defined(USE_FAKE_GETADDRINFO) 546 int res; 547 const char *env = getenv("CURL_DNS_SERVER"); 548 if(env) 549 res = r_getaddrinfo(hostname, service, hints, result); 550 else 551 res = getaddrinfo(hostname, service, hints, result); 552 #else 553 int res = getaddrinfo(hostname, service, hints, result); 554 #endif 555 if(0 == res) 556 /* success */ 557 curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n", 558 source, line, (void *)*result); 559 else 560 curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n", 561 source, line); 562 return res; 563 } 564 #endif /* defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) */ 565 566 #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) 567 /* 568 * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS 569 * 10.11.5. 570 */ 571 void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) 572 { 573 struct Curl_addrinfo *ca; 574 struct sockaddr_in *addr; 575 #ifdef USE_IPV6 576 struct sockaddr_in6 *addr6; 577 #endif 578 for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { 579 switch(ca->ai_family) { 580 case AF_INET: 581 addr = (void *)ca->ai_addr; /* storage area for this info */ 582 addr->sin_port = htons((unsigned short)port); 583 break; 584 585 #ifdef USE_IPV6 586 case AF_INET6: 587 addr6 = (void *)ca->ai_addr; /* storage area for this info */ 588 addr6->sin6_port = htons((unsigned short)port); 589 break; 590 #endif 591 } 592 } 593 } 594 #endif