Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/hostip.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, 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.haxx.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 ***************************************************************************/ | |
| 22 | |
| 23 #include "curl_setup.h" | |
| 24 | |
| 25 #ifdef HAVE_NETINET_IN_H | |
| 26 #include <netinet/in.h> | |
| 27 #endif | |
| 28 #ifdef HAVE_NETINET_IN6_H | |
| 29 #include <netinet/in6.h> | |
| 30 #endif | |
| 31 #ifdef HAVE_NETDB_H | |
| 32 #include <netdb.h> | |
| 33 #endif | |
| 34 #ifdef HAVE_ARPA_INET_H | |
| 35 #include <arpa/inet.h> | |
| 36 #endif | |
| 37 #ifdef __VMS | |
| 38 #include <in.h> | |
| 39 #include <inet.h> | |
| 40 #endif | |
| 41 | |
| 42 #ifdef HAVE_SETJMP_H | |
| 43 #include <setjmp.h> | |
| 44 #endif | |
| 45 #ifdef HAVE_SIGNAL_H | |
| 46 #include <signal.h> | |
| 47 #endif | |
| 48 | |
| 49 #ifdef HAVE_PROCESS_H | |
| 50 #include <process.h> | |
| 51 #endif | |
| 52 | |
| 53 #include "urldata.h" | |
| 54 #include "sendf.h" | |
| 55 #include "hostip.h" | |
| 56 #include "hash.h" | |
| 57 #include "rand.h" | |
| 58 #include "share.h" | |
| 59 #include "strerror.h" | |
| 60 #include "url.h" | |
| 61 #include "inet_ntop.h" | |
| 62 #include "multiif.h" | |
| 63 #include "doh.h" | |
| 64 #include "warnless.h" | |
| 65 /* The last 3 #include files should be in this order */ | |
| 66 #include "curl_printf.h" | |
| 67 #include "curl_memory.h" | |
| 68 #include "memdebug.h" | |
| 69 | |
| 70 #if defined(CURLRES_SYNCH) && \ | |
| 71 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) | |
| 72 /* alarm-based timeouts can only be used with all the dependencies satisfied */ | |
| 73 #define USE_ALARM_TIMEOUT | |
| 74 #endif | |
| 75 | |
| 76 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ | |
| 77 | |
| 78 /* | |
| 79 * hostip.c explained | |
| 80 * ================== | |
| 81 * | |
| 82 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c | |
| 83 * source file are these: | |
| 84 * | |
| 85 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use | |
| 86 * that. The host may not be able to resolve IPv6, but we don't really have to | |
| 87 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 | |
| 88 * defined. | |
| 89 * | |
| 90 * CURLRES_ARES - is defined if libcurl is built to use c-ares for | |
| 91 * asynchronous name resolves. This can be Windows or *nix. | |
| 92 * | |
| 93 * CURLRES_THREADED - is defined if libcurl is built to run under (native) | |
| 94 * Windows, and then the name resolve will be done in a new thread, and the | |
| 95 * supported API will be the same as for ares-builds. | |
| 96 * | |
| 97 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If | |
| 98 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is | |
| 99 * defined. | |
| 100 * | |
| 101 * The host*.c sources files are split up like this: | |
| 102 * | |
| 103 * hostip.c - method-independent resolver functions and utility functions | |
| 104 * hostasyn.c - functions for asynchronous name resolves | |
| 105 * hostsyn.c - functions for synchronous name resolves | |
| 106 * hostip4.c - IPv4 specific functions | |
| 107 * hostip6.c - IPv6 specific functions | |
| 108 * | |
| 109 * The two asynchronous name resolver backends are implemented in: | |
| 110 * asyn-ares.c - functions for ares-using name resolves | |
| 111 * asyn-thread.c - functions for threaded name resolves | |
| 112 | |
| 113 * The hostip.h is the united header file for all this. It defines the | |
| 114 * CURLRES_* defines based on the config*.h and curl_setup.h defines. | |
| 115 */ | |
| 116 | |
| 117 static void freednsentry(void *freethis); | |
| 118 | |
| 119 /* | |
| 120 * Return # of addresses in a Curl_addrinfo struct | |
| 121 */ | |
| 122 int Curl_num_addresses(const Curl_addrinfo *addr) | |
| 123 { | |
| 124 int i = 0; | |
| 125 while(addr) { | |
| 126 addr = addr->ai_next; | |
| 127 i++; | |
| 128 } | |
| 129 return i; | |
| 130 } | |
| 131 | |
| 132 /* | |
| 133 * Curl_printable_address() returns a printable version of the 1st address | |
| 134 * given in the 'ai' argument. The result will be stored in the buf that is | |
| 135 * bufsize bytes big. | |
| 136 * | |
| 137 * If the conversion fails, it returns NULL. | |
| 138 */ | |
| 139 const char * | |
| 140 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize) | |
| 141 { | |
| 142 const struct sockaddr_in *sa4; | |
| 143 const struct in_addr *ipaddr4; | |
| 144 #ifdef ENABLE_IPV6 | |
| 145 const struct sockaddr_in6 *sa6; | |
| 146 const struct in6_addr *ipaddr6; | |
| 147 #endif | |
| 148 | |
| 149 switch(ai->ai_family) { | |
| 150 case AF_INET: | |
| 151 sa4 = (const void *)ai->ai_addr; | |
| 152 ipaddr4 = &sa4->sin_addr; | |
| 153 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, | |
| 154 bufsize); | |
| 155 #ifdef ENABLE_IPV6 | |
| 156 case AF_INET6: | |
| 157 sa6 = (const void *)ai->ai_addr; | |
| 158 ipaddr6 = &sa6->sin6_addr; | |
| 159 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, | |
| 160 bufsize); | |
| 161 #endif | |
| 162 default: | |
| 163 break; | |
| 164 } | |
| 165 return NULL; | |
| 166 } | |
| 167 | |
| 168 /* | |
| 169 * Create a hostcache id string for the provided host + port, to be used by | |
| 170 * the DNS caching. Without alloc. | |
| 171 */ | |
| 172 static void | |
| 173 create_hostcache_id(const char *name, int port, char *ptr, size_t buflen) | |
| 174 { | |
| 175 size_t len = strlen(name); | |
| 176 if(len > (buflen - 7)) | |
| 177 len = buflen - 7; | |
| 178 /* store and lower case the name */ | |
| 179 while(len--) | |
| 180 *ptr++ = (char)TOLOWER(*name++); | |
| 181 msnprintf(ptr, 7, ":%u", port); | |
| 182 } | |
| 183 | |
| 184 struct hostcache_prune_data { | |
| 185 long cache_timeout; | |
| 186 time_t now; | |
| 187 }; | |
| 188 | |
| 189 /* | |
| 190 * This function is set as a callback to be called for every entry in the DNS | |
| 191 * cache when we want to prune old unused entries. | |
| 192 * | |
| 193 * Returning non-zero means remove the entry, return 0 to keep it in the | |
| 194 * cache. | |
| 195 */ | |
| 196 static int | |
| 197 hostcache_timestamp_remove(void *datap, void *hc) | |
| 198 { | |
| 199 struct hostcache_prune_data *data = | |
| 200 (struct hostcache_prune_data *) datap; | |
| 201 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; | |
| 202 | |
| 203 return (0 != c->timestamp) | |
| 204 && (data->now - c->timestamp >= data->cache_timeout); | |
| 205 } | |
| 206 | |
| 207 /* | |
| 208 * Prune the DNS cache. This assumes that a lock has already been taken. | |
| 209 */ | |
| 210 static void | |
| 211 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now) | |
| 212 { | |
| 213 struct hostcache_prune_data user; | |
| 214 | |
| 215 user.cache_timeout = cache_timeout; | |
| 216 user.now = now; | |
| 217 | |
| 218 Curl_hash_clean_with_criterium(hostcache, | |
| 219 (void *) &user, | |
| 220 hostcache_timestamp_remove); | |
| 221 } | |
| 222 | |
| 223 /* | |
| 224 * Library-wide function for pruning the DNS cache. This function takes and | |
| 225 * returns the appropriate locks. | |
| 226 */ | |
| 227 void Curl_hostcache_prune(struct Curl_easy *data) | |
| 228 { | |
| 229 time_t now; | |
| 230 | |
| 231 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache) | |
| 232 /* cache forever means never prune, and NULL hostcache means | |
| 233 we can't do it */ | |
| 234 return; | |
| 235 | |
| 236 if(data->share) | |
| 237 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 238 | |
| 239 time(&now); | |
| 240 | |
| 241 /* Remove outdated and unused entries from the hostcache */ | |
| 242 hostcache_prune(data->dns.hostcache, | |
| 243 data->set.dns_cache_timeout, | |
| 244 now); | |
| 245 | |
| 246 if(data->share) | |
| 247 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 248 } | |
| 249 | |
| 250 #ifdef HAVE_SIGSETJMP | |
| 251 /* Beware this is a global and unique instance. This is used to store the | |
| 252 return address that we can jump back to from inside a signal handler. This | |
| 253 is not thread-safe stuff. */ | |
| 254 sigjmp_buf curl_jmpenv; | |
| 255 #endif | |
| 256 | |
| 257 /* lookup address, returns entry if found and not stale */ | |
| 258 static struct Curl_dns_entry * | |
| 259 fetch_addr(struct connectdata *conn, | |
| 260 const char *hostname, | |
| 261 int port) | |
| 262 { | |
| 263 struct Curl_dns_entry *dns = NULL; | |
| 264 size_t entry_len; | |
| 265 struct Curl_easy *data = conn->data; | |
| 266 char entry_id[MAX_HOSTCACHE_LEN]; | |
| 267 | |
| 268 /* Create an entry id, based upon the hostname and port */ | |
| 269 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); | |
| 270 entry_len = strlen(entry_id); | |
| 271 | |
| 272 /* See if its already in our dns cache */ | |
| 273 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); | |
| 274 | |
| 275 /* No entry found in cache, check if we might have a wildcard entry */ | |
| 276 if(!dns && data->change.wildcard_resolve) { | |
| 277 create_hostcache_id("*", port, entry_id, sizeof(entry_id)); | |
| 278 entry_len = strlen(entry_id); | |
| 279 | |
| 280 /* See if it's already in our dns cache */ | |
| 281 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); | |
| 282 } | |
| 283 | |
| 284 if(dns && (data->set.dns_cache_timeout != -1)) { | |
| 285 /* See whether the returned entry is stale. Done before we release lock */ | |
| 286 struct hostcache_prune_data user; | |
| 287 | |
| 288 time(&user.now); | |
| 289 user.cache_timeout = data->set.dns_cache_timeout; | |
| 290 | |
| 291 if(hostcache_timestamp_remove(&user, dns)) { | |
| 292 infof(data, "Hostname in DNS cache was stale, zapped\n"); | |
| 293 dns = NULL; /* the memory deallocation is being handled by the hash */ | |
| 294 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 return dns; | |
| 299 } | |
| 300 | |
| 301 /* | |
| 302 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. | |
| 303 * | |
| 304 * Curl_resolv() checks initially and multi_runsingle() checks each time | |
| 305 * it discovers the handle in the state WAITRESOLVE whether the hostname | |
| 306 * has already been resolved and the address has already been stored in | |
| 307 * the DNS cache. This short circuits waiting for a lot of pending | |
| 308 * lookups for the same hostname requested by different handles. | |
| 309 * | |
| 310 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. | |
| 311 * | |
| 312 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after | |
| 313 * use, or we'll leak memory! | |
| 314 */ | |
| 315 struct Curl_dns_entry * | |
| 316 Curl_fetch_addr(struct connectdata *conn, | |
| 317 const char *hostname, | |
| 318 int port) | |
| 319 { | |
| 320 struct Curl_easy *data = conn->data; | |
| 321 struct Curl_dns_entry *dns = NULL; | |
| 322 | |
| 323 if(data->share) | |
| 324 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 325 | |
| 326 dns = fetch_addr(conn, hostname, port); | |
| 327 | |
| 328 if(dns) | |
| 329 dns->inuse++; /* we use it! */ | |
| 330 | |
| 331 if(data->share) | |
| 332 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 333 | |
| 334 return dns; | |
| 335 } | |
| 336 | |
| 337 #ifndef CURL_DISABLE_SHUFFLE_DNS | |
| 338 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, | |
| 339 Curl_addrinfo **addr); | |
| 340 /* | |
| 341 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' | |
| 342 * struct by re-linking its linked list. | |
| 343 * | |
| 344 * The addr argument should be the address of a pointer to the head node of a | |
| 345 * `Curl_addrinfo` list and it will be modified to point to the new head after | |
| 346 * shuffling. | |
| 347 * | |
| 348 * Not declared static only to make it easy to use in a unit test! | |
| 349 * | |
| 350 * @unittest: 1608 | |
| 351 */ | |
| 352 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, | |
| 353 Curl_addrinfo **addr) | |
| 354 { | |
| 355 CURLcode result = CURLE_OK; | |
| 356 const int num_addrs = Curl_num_addresses(*addr); | |
| 357 | |
| 358 if(num_addrs > 1) { | |
| 359 Curl_addrinfo **nodes; | |
| 360 infof(data, "Shuffling %i addresses", num_addrs); | |
| 361 | |
| 362 nodes = malloc(num_addrs*sizeof(*nodes)); | |
| 363 if(nodes) { | |
| 364 int i; | |
| 365 unsigned int *rnd; | |
| 366 const size_t rnd_size = num_addrs * sizeof(*rnd); | |
| 367 | |
| 368 /* build a plain array of Curl_addrinfo pointers */ | |
| 369 nodes[0] = *addr; | |
| 370 for(i = 1; i < num_addrs; i++) { | |
| 371 nodes[i] = nodes[i-1]->ai_next; | |
| 372 } | |
| 373 | |
| 374 rnd = malloc(rnd_size); | |
| 375 if(rnd) { | |
| 376 /* Fisher-Yates shuffle */ | |
| 377 if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { | |
| 378 Curl_addrinfo *swap_tmp; | |
| 379 for(i = num_addrs - 1; i > 0; i--) { | |
| 380 swap_tmp = nodes[rnd[i] % (i + 1)]; | |
| 381 nodes[rnd[i] % (i + 1)] = nodes[i]; | |
| 382 nodes[i] = swap_tmp; | |
| 383 } | |
| 384 | |
| 385 /* relink list in the new order */ | |
| 386 for(i = 1; i < num_addrs; i++) { | |
| 387 nodes[i-1]->ai_next = nodes[i]; | |
| 388 } | |
| 389 | |
| 390 nodes[num_addrs-1]->ai_next = NULL; | |
| 391 *addr = nodes[0]; | |
| 392 } | |
| 393 free(rnd); | |
| 394 } | |
| 395 else | |
| 396 result = CURLE_OUT_OF_MEMORY; | |
| 397 free(nodes); | |
| 398 } | |
| 399 else | |
| 400 result = CURLE_OUT_OF_MEMORY; | |
| 401 } | |
| 402 return result; | |
| 403 } | |
| 404 #endif | |
| 405 | |
| 406 /* | |
| 407 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. | |
| 408 * | |
| 409 * When calling Curl_resolv() has resulted in a response with a returned | |
| 410 * address, we call this function to store the information in the dns | |
| 411 * cache etc | |
| 412 * | |
| 413 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. | |
| 414 */ | |
| 415 struct Curl_dns_entry * | |
| 416 Curl_cache_addr(struct Curl_easy *data, | |
| 417 Curl_addrinfo *addr, | |
| 418 const char *hostname, | |
| 419 int port) | |
| 420 { | |
| 421 char entry_id[MAX_HOSTCACHE_LEN]; | |
| 422 size_t entry_len; | |
| 423 struct Curl_dns_entry *dns; | |
| 424 struct Curl_dns_entry *dns2; | |
| 425 | |
| 426 #ifndef CURL_DISABLE_SHUFFLE_DNS | |
| 427 /* shuffle addresses if requested */ | |
| 428 if(data->set.dns_shuffle_addresses) { | |
| 429 CURLcode result = Curl_shuffle_addr(data, &addr); | |
| 430 if(result) | |
| 431 return NULL; | |
| 432 } | |
| 433 #endif | |
| 434 | |
| 435 /* Create a new cache entry */ | |
| 436 dns = calloc(1, sizeof(struct Curl_dns_entry)); | |
| 437 if(!dns) { | |
| 438 return NULL; | |
| 439 } | |
| 440 | |
| 441 /* Create an entry id, based upon the hostname and port */ | |
| 442 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); | |
| 443 entry_len = strlen(entry_id); | |
| 444 | |
| 445 dns->inuse = 1; /* the cache has the first reference */ | |
| 446 dns->addr = addr; /* this is the address(es) */ | |
| 447 time(&dns->timestamp); | |
| 448 if(dns->timestamp == 0) | |
| 449 dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */ | |
| 450 | |
| 451 /* Store the resolved data in our DNS cache. */ | |
| 452 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, | |
| 453 (void *)dns); | |
| 454 if(!dns2) { | |
| 455 free(dns); | |
| 456 return NULL; | |
| 457 } | |
| 458 | |
| 459 dns = dns2; | |
| 460 dns->inuse++; /* mark entry as in-use */ | |
| 461 return dns; | |
| 462 } | |
| 463 | |
| 464 /* | |
| 465 * Curl_resolv() is the main name resolve function within libcurl. It resolves | |
| 466 * a name and returns a pointer to the entry in the 'entry' argument (if one | |
| 467 * is provided). This function might return immediately if we're using asynch | |
| 468 * resolves. See the return codes. | |
| 469 * | |
| 470 * The cache entry we return will get its 'inuse' counter increased when this | |
| 471 * function is used. You MUST call Curl_resolv_unlock() later (when you're | |
| 472 * done using this struct) to decrease the counter again. | |
| 473 * | |
| 474 * In debug mode, we specifically test for an interface name "LocalHost" | |
| 475 * and resolve "localhost" instead as a means to permit test cases | |
| 476 * to connect to a local test server with any host name. | |
| 477 * | |
| 478 * Return codes: | |
| 479 * | |
| 480 * CURLRESOLV_ERROR (-1) = error, no pointer | |
| 481 * CURLRESOLV_RESOLVED (0) = OK, pointer provided | |
| 482 * CURLRESOLV_PENDING (1) = waiting for response, no pointer | |
| 483 */ | |
| 484 | |
| 485 int Curl_resolv(struct connectdata *conn, | |
| 486 const char *hostname, | |
| 487 int port, | |
| 488 bool allowDOH, | |
| 489 struct Curl_dns_entry **entry) | |
| 490 { | |
| 491 struct Curl_dns_entry *dns = NULL; | |
| 492 struct Curl_easy *data = conn->data; | |
| 493 CURLcode result; | |
| 494 int rc = CURLRESOLV_ERROR; /* default to failure */ | |
| 495 | |
| 496 *entry = NULL; | |
| 497 | |
| 498 if(data->share) | |
| 499 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 500 | |
| 501 dns = fetch_addr(conn, hostname, port); | |
| 502 | |
| 503 if(dns) { | |
| 504 infof(data, "Hostname %s was found in DNS cache\n", hostname); | |
| 505 dns->inuse++; /* we use it! */ | |
| 506 rc = CURLRESOLV_RESOLVED; | |
| 507 } | |
| 508 | |
| 509 if(data->share) | |
| 510 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 511 | |
| 512 if(!dns) { | |
| 513 /* The entry was not in the cache. Resolve it to IP address */ | |
| 514 | |
| 515 Curl_addrinfo *addr; | |
| 516 int respwait = 0; | |
| 517 | |
| 518 /* Check what IP specifics the app has requested and if we can provide it. | |
| 519 * If not, bail out. */ | |
| 520 if(!Curl_ipvalid(conn)) | |
| 521 return CURLRESOLV_ERROR; | |
| 522 | |
| 523 /* notify the resolver start callback */ | |
| 524 if(data->set.resolver_start) { | |
| 525 int st; | |
| 526 Curl_set_in_callback(data, true); | |
| 527 st = data->set.resolver_start(data->state.resolver, NULL, | |
| 528 data->set.resolver_start_client); | |
| 529 Curl_set_in_callback(data, false); | |
| 530 if(st) | |
| 531 return CURLRESOLV_ERROR; | |
| 532 } | |
| 533 | |
| 534 if(allowDOH && data->set.doh) { | |
| 535 addr = Curl_doh(conn, hostname, port, &respwait); | |
| 536 } | |
| 537 else { | |
| 538 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a | |
| 539 non-zero value indicating that we need to wait for the response to the | |
| 540 resolve call */ | |
| 541 addr = Curl_getaddrinfo(conn, | |
| 542 #ifdef DEBUGBUILD | |
| 543 (data->set.str[STRING_DEVICE] | |
| 544 && !strcmp(data->set.str[STRING_DEVICE], | |
| 545 "LocalHost"))?"localhost": | |
| 546 #endif | |
| 547 hostname, port, &respwait); | |
| 548 } | |
| 549 if(!addr) { | |
| 550 if(respwait) { | |
| 551 /* the response to our resolve call will come asynchronously at | |
| 552 a later time, good or bad */ | |
| 553 /* First, check that we haven't received the info by now */ | |
| 554 result = Curl_resolv_check(conn, &dns); | |
| 555 if(result) /* error detected */ | |
| 556 return CURLRESOLV_ERROR; | |
| 557 if(dns) | |
| 558 rc = CURLRESOLV_RESOLVED; /* pointer provided */ | |
| 559 else | |
| 560 rc = CURLRESOLV_PENDING; /* no info yet */ | |
| 561 } | |
| 562 } | |
| 563 else { | |
| 564 if(data->share) | |
| 565 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 566 | |
| 567 /* we got a response, store it in the cache */ | |
| 568 dns = Curl_cache_addr(data, addr, hostname, port); | |
| 569 | |
| 570 if(data->share) | |
| 571 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 572 | |
| 573 if(!dns) | |
| 574 /* returned failure, bail out nicely */ | |
| 575 Curl_freeaddrinfo(addr); | |
| 576 else | |
| 577 rc = CURLRESOLV_RESOLVED; | |
| 578 } | |
| 579 } | |
| 580 | |
| 581 *entry = dns; | |
| 582 | |
| 583 return rc; | |
| 584 } | |
| 585 | |
| 586 #ifdef USE_ALARM_TIMEOUT | |
| 587 /* | |
| 588 * This signal handler jumps back into the main libcurl code and continues | |
| 589 * execution. This effectively causes the remainder of the application to run | |
| 590 * within a signal handler which is nonportable and could lead to problems. | |
| 591 */ | |
| 592 static | |
| 593 RETSIGTYPE alarmfunc(int sig) | |
| 594 { | |
| 595 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ | |
| 596 (void)sig; | |
| 597 siglongjmp(curl_jmpenv, 1); | |
| 598 } | |
| 599 #endif /* USE_ALARM_TIMEOUT */ | |
| 600 | |
| 601 /* | |
| 602 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a | |
| 603 * timeout. This function might return immediately if we're using asynch | |
| 604 * resolves. See the return codes. | |
| 605 * | |
| 606 * The cache entry we return will get its 'inuse' counter increased when this | |
| 607 * function is used. You MUST call Curl_resolv_unlock() later (when you're | |
| 608 * done using this struct) to decrease the counter again. | |
| 609 * | |
| 610 * If built with a synchronous resolver and use of signals is not | |
| 611 * disabled by the application, then a nonzero timeout will cause a | |
| 612 * timeout after the specified number of milliseconds. Otherwise, timeout | |
| 613 * is ignored. | |
| 614 * | |
| 615 * Return codes: | |
| 616 * | |
| 617 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired | |
| 618 * CURLRESOLV_ERROR (-1) = error, no pointer | |
| 619 * CURLRESOLV_RESOLVED (0) = OK, pointer provided | |
| 620 * CURLRESOLV_PENDING (1) = waiting for response, no pointer | |
| 621 */ | |
| 622 | |
| 623 int Curl_resolv_timeout(struct connectdata *conn, | |
| 624 const char *hostname, | |
| 625 int port, | |
| 626 struct Curl_dns_entry **entry, | |
| 627 timediff_t timeoutms) | |
| 628 { | |
| 629 #ifdef USE_ALARM_TIMEOUT | |
| 630 #ifdef HAVE_SIGACTION | |
| 631 struct sigaction keep_sigact; /* store the old struct here */ | |
| 632 volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ | |
| 633 struct sigaction sigact; | |
| 634 #else | |
| 635 #ifdef HAVE_SIGNAL | |
| 636 void (*keep_sigact)(int); /* store the old handler here */ | |
| 637 #endif /* HAVE_SIGNAL */ | |
| 638 #endif /* HAVE_SIGACTION */ | |
| 639 volatile long timeout; | |
| 640 volatile unsigned int prev_alarm = 0; | |
| 641 struct Curl_easy *data = conn->data; | |
| 642 #endif /* USE_ALARM_TIMEOUT */ | |
| 643 int rc; | |
| 644 | |
| 645 *entry = NULL; | |
| 646 | |
| 647 if(timeoutms < 0) | |
| 648 /* got an already expired timeout */ | |
| 649 return CURLRESOLV_TIMEDOUT; | |
| 650 | |
| 651 #ifdef USE_ALARM_TIMEOUT | |
| 652 if(data->set.no_signal) | |
| 653 /* Ignore the timeout when signals are disabled */ | |
| 654 timeout = 0; | |
| 655 else | |
| 656 timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; | |
| 657 | |
| 658 if(!timeout) | |
| 659 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ | |
| 660 return Curl_resolv(conn, hostname, port, TRUE, entry); | |
| 661 | |
| 662 if(timeout < 1000) { | |
| 663 /* The alarm() function only provides integer second resolution, so if | |
| 664 we want to wait less than one second we must bail out already now. */ | |
| 665 failf(data, | |
| 666 "remaining timeout of %ld too small to resolve via SIGALRM method", | |
| 667 timeout); | |
| 668 return CURLRESOLV_TIMEDOUT; | |
| 669 } | |
| 670 /* This allows us to time-out from the name resolver, as the timeout | |
| 671 will generate a signal and we will siglongjmp() from that here. | |
| 672 This technique has problems (see alarmfunc). | |
| 673 This should be the last thing we do before calling Curl_resolv(), | |
| 674 as otherwise we'd have to worry about variables that get modified | |
| 675 before we invoke Curl_resolv() (and thus use "volatile"). */ | |
| 676 if(sigsetjmp(curl_jmpenv, 1)) { | |
| 677 /* this is coming from a siglongjmp() after an alarm signal */ | |
| 678 failf(data, "name lookup timed out"); | |
| 679 rc = CURLRESOLV_ERROR; | |
| 680 goto clean_up; | |
| 681 } | |
| 682 else { | |
| 683 /************************************************************* | |
| 684 * Set signal handler to catch SIGALRM | |
| 685 * Store the old value to be able to set it back later! | |
| 686 *************************************************************/ | |
| 687 #ifdef HAVE_SIGACTION | |
| 688 sigaction(SIGALRM, NULL, &sigact); | |
| 689 keep_sigact = sigact; | |
| 690 keep_copysig = TRUE; /* yes, we have a copy */ | |
| 691 sigact.sa_handler = alarmfunc; | |
| 692 #ifdef SA_RESTART | |
| 693 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ | |
| 694 sigact.sa_flags &= ~SA_RESTART; | |
| 695 #endif | |
| 696 /* now set the new struct */ | |
| 697 sigaction(SIGALRM, &sigact, NULL); | |
| 698 #else /* HAVE_SIGACTION */ | |
| 699 /* no sigaction(), revert to the much lamer signal() */ | |
| 700 #ifdef HAVE_SIGNAL | |
| 701 keep_sigact = signal(SIGALRM, alarmfunc); | |
| 702 #endif | |
| 703 #endif /* HAVE_SIGACTION */ | |
| 704 | |
| 705 /* alarm() makes a signal get sent when the timeout fires off, and that | |
| 706 will abort system calls */ | |
| 707 prev_alarm = alarm(curlx_sltoui(timeout/1000L)); | |
| 708 } | |
| 709 | |
| 710 #else | |
| 711 #ifndef CURLRES_ASYNCH | |
| 712 if(timeoutms) | |
| 713 infof(conn->data, "timeout on name lookup is not supported\n"); | |
| 714 #else | |
| 715 (void)timeoutms; /* timeoutms not used with an async resolver */ | |
| 716 #endif | |
| 717 #endif /* USE_ALARM_TIMEOUT */ | |
| 718 | |
| 719 /* Perform the actual name resolution. This might be interrupted by an | |
| 720 * alarm if it takes too long. | |
| 721 */ | |
| 722 rc = Curl_resolv(conn, hostname, port, TRUE, entry); | |
| 723 | |
| 724 #ifdef USE_ALARM_TIMEOUT | |
| 725 clean_up: | |
| 726 | |
| 727 if(!prev_alarm) | |
| 728 /* deactivate a possibly active alarm before uninstalling the handler */ | |
| 729 alarm(0); | |
| 730 | |
| 731 #ifdef HAVE_SIGACTION | |
| 732 if(keep_copysig) { | |
| 733 /* we got a struct as it looked before, now put that one back nice | |
| 734 and clean */ | |
| 735 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ | |
| 736 } | |
| 737 #else | |
| 738 #ifdef HAVE_SIGNAL | |
| 739 /* restore the previous SIGALRM handler */ | |
| 740 signal(SIGALRM, keep_sigact); | |
| 741 #endif | |
| 742 #endif /* HAVE_SIGACTION */ | |
| 743 | |
| 744 /* switch back the alarm() to either zero or to what it was before minus | |
| 745 the time we spent until now! */ | |
| 746 if(prev_alarm) { | |
| 747 /* there was an alarm() set before us, now put it back */ | |
| 748 timediff_t elapsed_secs = Curl_timediff(Curl_now(), | |
| 749 conn->created) / 1000; | |
| 750 | |
| 751 /* the alarm period is counted in even number of seconds */ | |
| 752 unsigned long alarm_set = prev_alarm - elapsed_secs; | |
| 753 | |
| 754 if(!alarm_set || | |
| 755 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { | |
| 756 /* if the alarm time-left reached zero or turned "negative" (counted | |
| 757 with unsigned values), we should fire off a SIGALRM here, but we | |
| 758 won't, and zero would be to switch it off so we never set it to | |
| 759 less than 1! */ | |
| 760 alarm(1); | |
| 761 rc = CURLRESOLV_TIMEDOUT; | |
| 762 failf(data, "Previous alarm fired off!"); | |
| 763 } | |
| 764 else | |
| 765 alarm((unsigned int)alarm_set); | |
| 766 } | |
| 767 #endif /* USE_ALARM_TIMEOUT */ | |
| 768 | |
| 769 return rc; | |
| 770 } | |
| 771 | |
| 772 /* | |
| 773 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been | |
| 774 * made, the struct may be destroyed due to pruning. It is important that only | |
| 775 * one unlock is made for each Curl_resolv() call. | |
| 776 * | |
| 777 * May be called with 'data' == NULL for global cache. | |
| 778 */ | |
| 779 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) | |
| 780 { | |
| 781 if(data && data->share) | |
| 782 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 783 | |
| 784 freednsentry(dns); | |
| 785 | |
| 786 if(data && data->share) | |
| 787 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 788 } | |
| 789 | |
| 790 /* | |
| 791 * File-internal: release cache dns entry reference, free if inuse drops to 0 | |
| 792 */ | |
| 793 static void freednsentry(void *freethis) | |
| 794 { | |
| 795 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; | |
| 796 DEBUGASSERT(dns && (dns->inuse>0)); | |
| 797 | |
| 798 dns->inuse--; | |
| 799 if(dns->inuse == 0) { | |
| 800 Curl_freeaddrinfo(dns->addr); | |
| 801 free(dns); | |
| 802 } | |
| 803 } | |
| 804 | |
| 805 /* | |
| 806 * Curl_mk_dnscache() inits a new DNS cache and returns success/failure. | |
| 807 */ | |
| 808 int Curl_mk_dnscache(struct curl_hash *hash) | |
| 809 { | |
| 810 return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare, | |
| 811 freednsentry); | |
| 812 } | |
| 813 | |
| 814 /* | |
| 815 * Curl_hostcache_clean() | |
| 816 * | |
| 817 * This _can_ be called with 'data' == NULL but then of course no locking | |
| 818 * can be done! | |
| 819 */ | |
| 820 | |
| 821 void Curl_hostcache_clean(struct Curl_easy *data, | |
| 822 struct curl_hash *hash) | |
| 823 { | |
| 824 if(data && data->share) | |
| 825 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 826 | |
| 827 Curl_hash_clean(hash); | |
| 828 | |
| 829 if(data && data->share) | |
| 830 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 831 } | |
| 832 | |
| 833 | |
| 834 CURLcode Curl_loadhostpairs(struct Curl_easy *data) | |
| 835 { | |
| 836 struct curl_slist *hostp; | |
| 837 char hostname[256]; | |
| 838 int port = 0; | |
| 839 | |
| 840 /* Default is no wildcard found */ | |
| 841 data->change.wildcard_resolve = false; | |
| 842 | |
| 843 for(hostp = data->change.resolve; hostp; hostp = hostp->next) { | |
| 844 char entry_id[MAX_HOSTCACHE_LEN]; | |
| 845 if(!hostp->data) | |
| 846 continue; | |
| 847 if(hostp->data[0] == '-') { | |
| 848 size_t entry_len; | |
| 849 | |
| 850 if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { | |
| 851 infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n", | |
| 852 hostp->data); | |
| 853 continue; | |
| 854 } | |
| 855 | |
| 856 /* Create an entry id, based upon the hostname and port */ | |
| 857 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); | |
| 858 entry_len = strlen(entry_id); | |
| 859 | |
| 860 if(data->share) | |
| 861 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 862 | |
| 863 /* delete entry, ignore if it didn't exist */ | |
| 864 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); | |
| 865 | |
| 866 if(data->share) | |
| 867 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 868 } | |
| 869 else { | |
| 870 struct Curl_dns_entry *dns; | |
| 871 Curl_addrinfo *head = NULL, *tail = NULL; | |
| 872 size_t entry_len; | |
| 873 char address[64]; | |
| 874 #if !defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 875 char *addresses = NULL; | |
| 876 #endif | |
| 877 char *addr_begin; | |
| 878 char *addr_end; | |
| 879 char *port_ptr; | |
| 880 char *end_ptr; | |
| 881 char *host_end; | |
| 882 unsigned long tmp_port; | |
| 883 bool error = true; | |
| 884 | |
| 885 host_end = strchr(hostp->data, ':'); | |
| 886 if(!host_end || | |
| 887 ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname))) | |
| 888 goto err; | |
| 889 | |
| 890 memcpy(hostname, hostp->data, host_end - hostp->data); | |
| 891 hostname[host_end - hostp->data] = '\0'; | |
| 892 | |
| 893 port_ptr = host_end + 1; | |
| 894 tmp_port = strtoul(port_ptr, &end_ptr, 10); | |
| 895 if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':') | |
| 896 goto err; | |
| 897 | |
| 898 port = (int)tmp_port; | |
| 899 #if !defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 900 addresses = end_ptr + 1; | |
| 901 #endif | |
| 902 | |
| 903 while(*end_ptr) { | |
| 904 size_t alen; | |
| 905 Curl_addrinfo *ai; | |
| 906 | |
| 907 addr_begin = end_ptr + 1; | |
| 908 addr_end = strchr(addr_begin, ','); | |
| 909 if(!addr_end) | |
| 910 addr_end = addr_begin + strlen(addr_begin); | |
| 911 end_ptr = addr_end; | |
| 912 | |
| 913 /* allow IP(v6) address within [brackets] */ | |
| 914 if(*addr_begin == '[') { | |
| 915 if(addr_end == addr_begin || *(addr_end - 1) != ']') | |
| 916 goto err; | |
| 917 ++addr_begin; | |
| 918 --addr_end; | |
| 919 } | |
| 920 | |
| 921 alen = addr_end - addr_begin; | |
| 922 if(!alen) | |
| 923 continue; | |
| 924 | |
| 925 if(alen >= sizeof(address)) | |
| 926 goto err; | |
| 927 | |
| 928 memcpy(address, addr_begin, alen); | |
| 929 address[alen] = '\0'; | |
| 930 | |
| 931 #ifndef ENABLE_IPV6 | |
| 932 if(strchr(address, ':')) { | |
| 933 infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n", | |
| 934 address); | |
| 935 continue; | |
| 936 } | |
| 937 #endif | |
| 938 | |
| 939 ai = Curl_str2addr(address, port); | |
| 940 if(!ai) { | |
| 941 infof(data, "Resolve address '%s' found illegal!\n", address); | |
| 942 goto err; | |
| 943 } | |
| 944 | |
| 945 if(tail) { | |
| 946 tail->ai_next = ai; | |
| 947 tail = tail->ai_next; | |
| 948 } | |
| 949 else { | |
| 950 head = tail = ai; | |
| 951 } | |
| 952 } | |
| 953 | |
| 954 if(!head) | |
| 955 goto err; | |
| 956 | |
| 957 error = false; | |
| 958 err: | |
| 959 if(error) { | |
| 960 infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n", | |
| 961 hostp->data); | |
| 962 Curl_freeaddrinfo(head); | |
| 963 continue; | |
| 964 } | |
| 965 | |
| 966 /* Create an entry id, based upon the hostname and port */ | |
| 967 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); | |
| 968 entry_len = strlen(entry_id); | |
| 969 | |
| 970 if(data->share) | |
| 971 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); | |
| 972 | |
| 973 /* See if its already in our dns cache */ | |
| 974 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); | |
| 975 | |
| 976 if(dns) { | |
| 977 infof(data, "RESOLVE %s:%d is - old addresses discarded!\n", | |
| 978 hostname, port); | |
| 979 /* delete old entry entry, there are two reasons for this | |
| 980 1. old entry may have different addresses. | |
| 981 2. even if entry with correct addresses is already in the cache, | |
| 982 but if it is close to expire, then by the time next http | |
| 983 request is made, it can get expired and pruned because old | |
| 984 entry is not necessarily marked as added by CURLOPT_RESOLVE. */ | |
| 985 | |
| 986 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); | |
| 987 } | |
| 988 | |
| 989 /* put this new host in the cache */ | |
| 990 dns = Curl_cache_addr(data, head, hostname, port); | |
| 991 if(dns) { | |
| 992 dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */ | |
| 993 /* release the returned reference; the cache itself will keep the | |
| 994 * entry alive: */ | |
| 995 dns->inuse--; | |
| 996 } | |
| 997 | |
| 998 if(data->share) | |
| 999 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); | |
| 1000 | |
| 1001 if(!dns) { | |
| 1002 Curl_freeaddrinfo(head); | |
| 1003 return CURLE_OUT_OF_MEMORY; | |
| 1004 } | |
| 1005 infof(data, "Added %s:%d:%s to DNS cache\n", | |
| 1006 hostname, port, addresses); | |
| 1007 | |
| 1008 /* Wildcard hostname */ | |
| 1009 if(hostname[0] == '*' && hostname[1] == '\0') { | |
| 1010 infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n", | |
| 1011 hostname, port); | |
| 1012 data->change.wildcard_resolve = true; | |
| 1013 } | |
| 1014 } | |
| 1015 } | |
| 1016 data->change.resolve = NULL; /* dealt with now */ | |
| 1017 | |
| 1018 return CURLE_OK; | |
| 1019 } | |
| 1020 | |
| 1021 CURLcode Curl_resolv_check(struct connectdata *conn, | |
| 1022 struct Curl_dns_entry **dns) | |
| 1023 { | |
| 1024 if(conn->data->set.doh) | |
| 1025 return Curl_doh_is_resolved(conn, dns); | |
| 1026 return Curl_resolver_is_resolved(conn, dns); | |
| 1027 } | |
| 1028 | |
| 1029 int Curl_resolv_getsock(struct connectdata *conn, | |
| 1030 curl_socket_t *socks) | |
| 1031 { | |
| 1032 #ifdef CURLRES_ASYNCH | |
| 1033 if(conn->data->set.doh) | |
| 1034 /* nothing to wait for during DOH resolve, those handles have their own | |
| 1035 sockets */ | |
| 1036 return GETSOCK_BLANK; | |
| 1037 return Curl_resolver_getsock(conn, socks); | |
| 1038 #else | |
| 1039 (void)conn; | |
| 1040 (void)socks; | |
| 1041 return GETSOCK_BLANK; | |
| 1042 #endif | |
| 1043 } | |
| 1044 | |
| 1045 /* Call this function after Curl_connect() has returned async=TRUE and | |
| 1046 then a successful name resolve has been received. | |
| 1047 | |
| 1048 Note: this function disconnects and frees the conn data in case of | |
| 1049 resolve failure */ | |
| 1050 CURLcode Curl_once_resolved(struct connectdata *conn, | |
| 1051 bool *protocol_done) | |
| 1052 { | |
| 1053 CURLcode result; | |
| 1054 | |
| 1055 if(conn->async.dns) { | |
| 1056 conn->dns_entry = conn->async.dns; | |
| 1057 conn->async.dns = NULL; | |
| 1058 } | |
| 1059 | |
| 1060 result = Curl_setup_conn(conn, protocol_done); | |
| 1061 | |
| 1062 if(result) | |
| 1063 /* We're not allowed to return failure with memory left allocated | |
| 1064 in the connectdata struct, free those here */ | |
| 1065 Curl_disconnect(conn->data, conn, TRUE); /* close the connection */ | |
| 1066 | |
| 1067 return result; | |
| 1068 } |
