Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/asyn-ares.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 /*********************************************************************** | |
| 26 * Only for ares-enabled builds | |
| 27 * And only for functions that fulfill the asynch resolver backend API | |
| 28 * as defined in asyn.h, nothing else belongs in this file! | |
| 29 **********************************************************************/ | |
| 30 | |
| 31 #ifdef CURLRES_ARES | |
| 32 | |
| 33 #include <limits.h> | |
| 34 #ifdef HAVE_NETINET_IN_H | |
| 35 #include <netinet/in.h> | |
| 36 #endif | |
| 37 #ifdef HAVE_NETDB_H | |
| 38 #include <netdb.h> | |
| 39 #endif | |
| 40 #ifdef HAVE_ARPA_INET_H | |
| 41 #include <arpa/inet.h> | |
| 42 #endif | |
| 43 #ifdef __VMS | |
| 44 #include <in.h> | |
| 45 #include <inet.h> | |
| 46 #endif | |
| 47 | |
| 48 #ifdef HAVE_PROCESS_H | |
| 49 #include <process.h> | |
| 50 #endif | |
| 51 | |
| 52 #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) | |
| 53 #undef in_addr_t | |
| 54 #define in_addr_t unsigned long | |
| 55 #endif | |
| 56 | |
| 57 #include "urldata.h" | |
| 58 #include "sendf.h" | |
| 59 #include "hostip.h" | |
| 60 #include "hash.h" | |
| 61 #include "share.h" | |
| 62 #include "strerror.h" | |
| 63 #include "url.h" | |
| 64 #include "multiif.h" | |
| 65 #include "inet_pton.h" | |
| 66 #include "connect.h" | |
| 67 #include "select.h" | |
| 68 #include "progress.h" | |
| 69 | |
| 70 # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ | |
| 71 (defined(WIN32) || defined(__SYMBIAN32__)) | |
| 72 # define CARES_STATICLIB | |
| 73 # endif | |
| 74 # include <ares.h> | |
| 75 # include <ares_version.h> /* really old c-ares didn't include this by | |
| 76 itself */ | |
| 77 | |
| 78 #if ARES_VERSION >= 0x010500 | |
| 79 /* c-ares 1.5.0 or later, the callback proto is modified */ | |
| 80 #define HAVE_CARES_CALLBACK_TIMEOUTS 1 | |
| 81 #endif | |
| 82 | |
| 83 /* The last 3 #include files should be in this order */ | |
| 84 #include "curl_printf.h" | |
| 85 #include "curl_memory.h" | |
| 86 #include "memdebug.h" | |
| 87 | |
| 88 struct ResolverResults { | |
| 89 int num_pending; /* number of ares_gethostbyname() requests */ | |
| 90 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ | |
| 91 int last_status; | |
| 92 struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ | |
| 93 }; | |
| 94 | |
| 95 /* How long we are willing to wait for additional parallel responses after | |
| 96 obtaining a "definitive" one. | |
| 97 | |
| 98 This is intended to equal the c-ares default timeout. cURL always uses that | |
| 99 default value. Unfortunately, c-ares doesn't expose its default timeout in | |
| 100 its API, but it is officially documented as 5 seconds. | |
| 101 | |
| 102 See query_completed_cb() for an explanation of how this is used. | |
| 103 */ | |
| 104 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 | |
| 105 | |
| 106 /* | |
| 107 * Curl_resolver_global_init() - the generic low-level asynchronous name | |
| 108 * resolve API. Called from curl_global_init() to initialize global resolver | |
| 109 * environment. Initializes ares library. | |
| 110 */ | |
| 111 int Curl_resolver_global_init(void) | |
| 112 { | |
| 113 #ifdef CARES_HAVE_ARES_LIBRARY_INIT | |
| 114 if(ares_library_init(ARES_LIB_INIT_ALL)) { | |
| 115 return CURLE_FAILED_INIT; | |
| 116 } | |
| 117 #endif | |
| 118 return CURLE_OK; | |
| 119 } | |
| 120 | |
| 121 /* | |
| 122 * Curl_resolver_global_cleanup() | |
| 123 * | |
| 124 * Called from curl_global_cleanup() to destroy global resolver environment. | |
| 125 * Deinitializes ares library. | |
| 126 */ | |
| 127 void Curl_resolver_global_cleanup(void) | |
| 128 { | |
| 129 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP | |
| 130 ares_library_cleanup(); | |
| 131 #endif | |
| 132 } | |
| 133 | |
| 134 | |
| 135 static void Curl_ares_sock_state_cb(void *data, ares_socket_t socket_fd, | |
| 136 int readable, int writable) | |
| 137 { | |
| 138 struct Curl_easy *easy = data; | |
| 139 if(!readable && !writable) { | |
| 140 DEBUGASSERT(easy); | |
| 141 Curl_multi_closed(easy, socket_fd); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 /* | |
| 146 * Curl_resolver_init() | |
| 147 * | |
| 148 * Called from curl_easy_init() -> Curl_open() to initialize resolver | |
| 149 * URL-state specific environment ('resolver' member of the UrlState | |
| 150 * structure). Fills the passed pointer by the initialized ares_channel. | |
| 151 */ | |
| 152 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) | |
| 153 { | |
| 154 int status; | |
| 155 struct ares_options options; | |
| 156 int optmask = ARES_OPT_SOCK_STATE_CB; | |
| 157 options.sock_state_cb = Curl_ares_sock_state_cb; | |
| 158 options.sock_state_cb_data = easy; | |
| 159 status = ares_init_options((ares_channel*)resolver, &options, optmask); | |
| 160 if(status != ARES_SUCCESS) { | |
| 161 if(status == ARES_ENOMEM) | |
| 162 return CURLE_OUT_OF_MEMORY; | |
| 163 else | |
| 164 return CURLE_FAILED_INIT; | |
| 165 } | |
| 166 return CURLE_OK; | |
| 167 /* make sure that all other returns from this function should destroy the | |
| 168 ares channel before returning error! */ | |
| 169 } | |
| 170 | |
| 171 /* | |
| 172 * Curl_resolver_cleanup() | |
| 173 * | |
| 174 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver | |
| 175 * URL-state specific environment ('resolver' member of the UrlState | |
| 176 * structure). Destroys the ares channel. | |
| 177 */ | |
| 178 void Curl_resolver_cleanup(void *resolver) | |
| 179 { | |
| 180 ares_destroy((ares_channel)resolver); | |
| 181 } | |
| 182 | |
| 183 /* | |
| 184 * Curl_resolver_duphandle() | |
| 185 * | |
| 186 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific | |
| 187 * environment ('resolver' member of the UrlState structure). Duplicates the | |
| 188 * 'from' ares channel and passes the resulting channel to the 'to' pointer. | |
| 189 */ | |
| 190 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) | |
| 191 { | |
| 192 (void)from; | |
| 193 /* | |
| 194 * it would be better to call ares_dup instead, but right now | |
| 195 * it is not possible to set 'sock_state_cb_data' outside of | |
| 196 * ares_init_options | |
| 197 */ | |
| 198 return Curl_resolver_init(easy, to); | |
| 199 } | |
| 200 | |
| 201 static void destroy_async_data(struct Curl_async *async); | |
| 202 | |
| 203 /* | |
| 204 * Cancel all possibly still on-going resolves for this connection. | |
| 205 */ | |
| 206 void Curl_resolver_cancel(struct connectdata *conn) | |
| 207 { | |
| 208 if(conn->data && conn->data->state.resolver) | |
| 209 ares_cancel((ares_channel)conn->data->state.resolver); | |
| 210 destroy_async_data(&conn->async); | |
| 211 } | |
| 212 | |
| 213 /* | |
| 214 * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We | |
| 215 * never block. | |
| 216 */ | |
| 217 void Curl_resolver_kill(struct connectdata *conn) | |
| 218 { | |
| 219 /* We don't need to check the resolver state because we can be called safely | |
| 220 at any time and we always do the same thing. */ | |
| 221 Curl_resolver_cancel(conn); | |
| 222 } | |
| 223 | |
| 224 /* | |
| 225 * destroy_async_data() cleans up async resolver data. | |
| 226 */ | |
| 227 static void destroy_async_data(struct Curl_async *async) | |
| 228 { | |
| 229 free(async->hostname); | |
| 230 | |
| 231 if(async->os_specific) { | |
| 232 struct ResolverResults *res = (struct ResolverResults *)async->os_specific; | |
| 233 if(res) { | |
| 234 if(res->temp_ai) { | |
| 235 Curl_freeaddrinfo(res->temp_ai); | |
| 236 res->temp_ai = NULL; | |
| 237 } | |
| 238 free(res); | |
| 239 } | |
| 240 async->os_specific = NULL; | |
| 241 } | |
| 242 | |
| 243 async->hostname = NULL; | |
| 244 } | |
| 245 | |
| 246 /* | |
| 247 * Curl_resolver_getsock() is called when someone from the outside world | |
| 248 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking | |
| 249 * with ares. The caller must make sure that this function is only called when | |
| 250 * we have a working ares channel. | |
| 251 * | |
| 252 * Returns: sockets-in-use-bitmap | |
| 253 */ | |
| 254 | |
| 255 int Curl_resolver_getsock(struct connectdata *conn, | |
| 256 curl_socket_t *socks) | |
| 257 { | |
| 258 struct timeval maxtime; | |
| 259 struct timeval timebuf; | |
| 260 struct timeval *timeout; | |
| 261 long milli; | |
| 262 int max = ares_getsock((ares_channel)conn->data->state.resolver, | |
| 263 (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); | |
| 264 | |
| 265 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; | |
| 266 maxtime.tv_usec = 0; | |
| 267 | |
| 268 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime, | |
| 269 &timebuf); | |
| 270 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); | |
| 271 if(milli == 0) | |
| 272 milli += 10; | |
| 273 Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME); | |
| 274 | |
| 275 return max; | |
| 276 } | |
| 277 | |
| 278 /* | |
| 279 * waitperform() | |
| 280 * | |
| 281 * 1) Ask ares what sockets it currently plays with, then | |
| 282 * 2) wait for the timeout period to check for action on ares' sockets. | |
| 283 * 3) tell ares to act on all the sockets marked as "with action" | |
| 284 * | |
| 285 * return number of sockets it worked on | |
| 286 */ | |
| 287 | |
| 288 static int waitperform(struct connectdata *conn, int timeout_ms) | |
| 289 { | |
| 290 struct Curl_easy *data = conn->data; | |
| 291 int nfds; | |
| 292 int bitmask; | |
| 293 ares_socket_t socks[ARES_GETSOCK_MAXNUM]; | |
| 294 struct pollfd pfd[ARES_GETSOCK_MAXNUM]; | |
| 295 int i; | |
| 296 int num = 0; | |
| 297 | |
| 298 bitmask = ares_getsock((ares_channel)data->state.resolver, socks, | |
| 299 ARES_GETSOCK_MAXNUM); | |
| 300 | |
| 301 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) { | |
| 302 pfd[i].events = 0; | |
| 303 pfd[i].revents = 0; | |
| 304 if(ARES_GETSOCK_READABLE(bitmask, i)) { | |
| 305 pfd[i].fd = socks[i]; | |
| 306 pfd[i].events |= POLLRDNORM|POLLIN; | |
| 307 } | |
| 308 if(ARES_GETSOCK_WRITABLE(bitmask, i)) { | |
| 309 pfd[i].fd = socks[i]; | |
| 310 pfd[i].events |= POLLWRNORM|POLLOUT; | |
| 311 } | |
| 312 if(pfd[i].events != 0) | |
| 313 num++; | |
| 314 else | |
| 315 break; | |
| 316 } | |
| 317 | |
| 318 if(num) | |
| 319 nfds = Curl_poll(pfd, num, timeout_ms); | |
| 320 else | |
| 321 nfds = 0; | |
| 322 | |
| 323 if(!nfds) | |
| 324 /* Call ares_process() unconditonally here, even if we simply timed out | |
| 325 above, as otherwise the ares name resolve won't timeout! */ | |
| 326 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD, | |
| 327 ARES_SOCKET_BAD); | |
| 328 else { | |
| 329 /* move through the descriptors and ask for processing on them */ | |
| 330 for(i = 0; i < num; i++) | |
| 331 ares_process_fd((ares_channel)data->state.resolver, | |
| 332 (pfd[i].revents & (POLLRDNORM|POLLIN))? | |
| 333 pfd[i].fd:ARES_SOCKET_BAD, | |
| 334 (pfd[i].revents & (POLLWRNORM|POLLOUT))? | |
| 335 pfd[i].fd:ARES_SOCKET_BAD); | |
| 336 } | |
| 337 return nfds; | |
| 338 } | |
| 339 | |
| 340 /* | |
| 341 * Curl_resolver_is_resolved() is called repeatedly to check if a previous | |
| 342 * name resolve request has completed. It should also make sure to time-out if | |
| 343 * the operation seems to take too long. | |
| 344 * | |
| 345 * Returns normal CURLcode errors. | |
| 346 */ | |
| 347 CURLcode Curl_resolver_is_resolved(struct connectdata *conn, | |
| 348 struct Curl_dns_entry **dns) | |
| 349 { | |
| 350 struct Curl_easy *data = conn->data; | |
| 351 struct ResolverResults *res = (struct ResolverResults *) | |
| 352 conn->async.os_specific; | |
| 353 CURLcode result = CURLE_OK; | |
| 354 | |
| 355 if(dns) | |
| 356 *dns = NULL; | |
| 357 | |
| 358 waitperform(conn, 0); | |
| 359 | |
| 360 /* Now that we've checked for any last minute results above, see if there are | |
| 361 any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer | |
| 362 expires. */ | |
| 363 if(res | |
| 364 && res->num_pending | |
| 365 /* This is only set to non-zero if the timer was started. */ | |
| 366 && (res->happy_eyeballs_dns_time.tv_sec | |
| 367 || res->happy_eyeballs_dns_time.tv_usec) | |
| 368 && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time) | |
| 369 >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { | |
| 370 /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer | |
| 371 running. */ | |
| 372 memset( | |
| 373 &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time)); | |
| 374 | |
| 375 /* Cancel the raw c-ares request, which will fire query_completed_cb() with | |
| 376 ARES_ECANCELLED synchronously for all pending responses. This will | |
| 377 leave us with res->num_pending == 0, which is perfect for the next | |
| 378 block. */ | |
| 379 ares_cancel((ares_channel)data->state.resolver); | |
| 380 DEBUGASSERT(res->num_pending == 0); | |
| 381 } | |
| 382 | |
| 383 if(res && !res->num_pending) { | |
| 384 if(dns) { | |
| 385 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); | |
| 386 /* temp_ai ownership is moved to the connection, so we need not free-up | |
| 387 them */ | |
| 388 res->temp_ai = NULL; | |
| 389 } | |
| 390 if(!conn->async.dns) { | |
| 391 failf(data, "Could not resolve: %s (%s)", | |
| 392 conn->async.hostname, ares_strerror(conn->async.status)); | |
| 393 result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: | |
| 394 CURLE_COULDNT_RESOLVE_HOST; | |
| 395 } | |
| 396 else if(dns) | |
| 397 *dns = conn->async.dns; | |
| 398 | |
| 399 destroy_async_data(&conn->async); | |
| 400 } | |
| 401 | |
| 402 return result; | |
| 403 } | |
| 404 | |
| 405 /* | |
| 406 * Curl_resolver_wait_resolv() | |
| 407 * | |
| 408 * Waits for a resolve to finish. This function should be avoided since using | |
| 409 * this risk getting the multi interface to "hang". | |
| 410 * | |
| 411 * If 'entry' is non-NULL, make it point to the resolved dns entry | |
| 412 * | |
| 413 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, | |
| 414 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. | |
| 415 */ | |
| 416 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, | |
| 417 struct Curl_dns_entry **entry) | |
| 418 { | |
| 419 CURLcode result = CURLE_OK; | |
| 420 struct Curl_easy *data = conn->data; | |
| 421 timediff_t timeout; | |
| 422 struct curltime now = Curl_now(); | |
| 423 struct Curl_dns_entry *temp_entry; | |
| 424 | |
| 425 if(entry) | |
| 426 *entry = NULL; /* clear on entry */ | |
| 427 | |
| 428 timeout = Curl_timeleft(data, &now, TRUE); | |
| 429 if(timeout < 0) { | |
| 430 /* already expired! */ | |
| 431 connclose(conn, "Timed out before name resolve started"); | |
| 432 return CURLE_OPERATION_TIMEDOUT; | |
| 433 } | |
| 434 if(!timeout) | |
| 435 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ | |
| 436 | |
| 437 /* Wait for the name resolve query to complete. */ | |
| 438 while(!result) { | |
| 439 struct timeval *tvp, tv, store; | |
| 440 int itimeout; | |
| 441 int timeout_ms; | |
| 442 | |
| 443 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; | |
| 444 | |
| 445 store.tv_sec = itimeout/1000; | |
| 446 store.tv_usec = (itimeout%1000)*1000; | |
| 447 | |
| 448 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv); | |
| 449 | |
| 450 /* use the timeout period ares returned to us above if less than one | |
| 451 second is left, otherwise just use 1000ms to make sure the progress | |
| 452 callback gets called frequent enough */ | |
| 453 if(!tvp->tv_sec) | |
| 454 timeout_ms = (int)(tvp->tv_usec/1000); | |
| 455 else | |
| 456 timeout_ms = 1000; | |
| 457 | |
| 458 waitperform(conn, timeout_ms); | |
| 459 result = Curl_resolver_is_resolved(conn, entry?&temp_entry:NULL); | |
| 460 | |
| 461 if(result || conn->async.done) | |
| 462 break; | |
| 463 | |
| 464 if(Curl_pgrsUpdate(conn)) | |
| 465 result = CURLE_ABORTED_BY_CALLBACK; | |
| 466 else { | |
| 467 struct curltime now2 = Curl_now(); | |
| 468 timediff_t timediff = Curl_timediff(now2, now); /* spent time */ | |
| 469 if(timediff <= 0) | |
| 470 timeout -= 1; /* always deduct at least 1 */ | |
| 471 else if(timediff > timeout) | |
| 472 timeout = -1; | |
| 473 else | |
| 474 timeout -= (long)timediff; | |
| 475 now = now2; /* for next loop */ | |
| 476 } | |
| 477 if(timeout < 0) | |
| 478 result = CURLE_OPERATION_TIMEDOUT; | |
| 479 } | |
| 480 if(result) | |
| 481 /* failure, so we cancel the ares operation */ | |
| 482 ares_cancel((ares_channel)data->state.resolver); | |
| 483 | |
| 484 /* Operation complete, if the lookup was successful we now have the entry | |
| 485 in the cache. */ | |
| 486 if(entry) | |
| 487 *entry = conn->async.dns; | |
| 488 | |
| 489 if(result) | |
| 490 /* close the connection, since we can't return failure here without | |
| 491 cleaning up this connection properly. */ | |
| 492 connclose(conn, "c-ares resolve failed"); | |
| 493 | |
| 494 return result; | |
| 495 } | |
| 496 | |
| 497 /* Connects results to the list */ | |
| 498 static void compound_results(struct ResolverResults *res, | |
| 499 Curl_addrinfo *ai) | |
| 500 { | |
| 501 Curl_addrinfo *ai_tail; | |
| 502 if(!ai) | |
| 503 return; | |
| 504 ai_tail = ai; | |
| 505 | |
| 506 while(ai_tail->ai_next) | |
| 507 ai_tail = ai_tail->ai_next; | |
| 508 | |
| 509 /* Add the new results to the list of old results. */ | |
| 510 ai_tail->ai_next = res->temp_ai; | |
| 511 res->temp_ai = ai; | |
| 512 } | |
| 513 | |
| 514 /* | |
| 515 * ares_query_completed_cb() is the callback that ares will call when | |
| 516 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), | |
| 517 * when using ares, is completed either successfully or with failure. | |
| 518 */ | |
| 519 static void query_completed_cb(void *arg, /* (struct connectdata *) */ | |
| 520 int status, | |
| 521 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS | |
| 522 int timeouts, | |
| 523 #endif | |
| 524 struct hostent *hostent) | |
| 525 { | |
| 526 struct connectdata *conn = (struct connectdata *)arg; | |
| 527 struct ResolverResults *res; | |
| 528 | |
| 529 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS | |
| 530 (void)timeouts; /* ignored */ | |
| 531 #endif | |
| 532 | |
| 533 if(ARES_EDESTRUCTION == status) | |
| 534 /* when this ares handle is getting destroyed, the 'arg' pointer may not | |
| 535 be valid so only defer it when we know the 'status' says its fine! */ | |
| 536 return; | |
| 537 | |
| 538 res = (struct ResolverResults *)conn->async.os_specific; | |
| 539 if(res) { | |
| 540 res->num_pending--; | |
| 541 | |
| 542 if(CURL_ASYNC_SUCCESS == status) { | |
| 543 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port); | |
| 544 if(ai) { | |
| 545 compound_results(res, ai); | |
| 546 } | |
| 547 } | |
| 548 /* A successful result overwrites any previous error */ | |
| 549 if(res->last_status != ARES_SUCCESS) | |
| 550 res->last_status = status; | |
| 551 | |
| 552 /* If there are responses still pending, we presume they must be the | |
| 553 complementary IPv4 or IPv6 lookups that we started in parallel in | |
| 554 Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a | |
| 555 "definitive" response from one of a set of parallel queries, we need to | |
| 556 think about how long we're willing to wait for more responses. */ | |
| 557 if(res->num_pending | |
| 558 /* Only these c-ares status values count as "definitive" for these | |
| 559 purposes. For example, ARES_ENODATA is what we expect when there is | |
| 560 no IPv6 entry for a domain name, and that's not a reason to get more | |
| 561 aggressive in our timeouts for the other response. Other errors are | |
| 562 either a result of bad input (which should affect all parallel | |
| 563 requests), local or network conditions, non-definitive server | |
| 564 responses, or us cancelling the request. */ | |
| 565 && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { | |
| 566 /* Right now, there can only be up to two parallel queries, so don't | |
| 567 bother handling any other cases. */ | |
| 568 DEBUGASSERT(res->num_pending == 1); | |
| 569 | |
| 570 /* It's possible that one of these parallel queries could succeed | |
| 571 quickly, but the other could always fail or timeout (when we're | |
| 572 talking to a pool of DNS servers that can only successfully resolve | |
| 573 IPv4 address, for example). | |
| 574 | |
| 575 It's also possible that the other request could always just take | |
| 576 longer because it needs more time or only the second DNS server can | |
| 577 fulfill it successfully. But, to align with the philosophy of Happy | |
| 578 Eyeballs, we don't want to wait _too_ long or users will think | |
| 579 requests are slow when IPv6 lookups don't actually work (but IPv4 ones | |
| 580 do). | |
| 581 | |
| 582 So, now that we have a usable answer (some IPv4 addresses, some IPv6 | |
| 583 addresses, or "no such domain"), we start a timeout for the remaining | |
| 584 pending responses. Even though it is typical that this resolved | |
| 585 request came back quickly, that needn't be the case. It might be that | |
| 586 this completing request didn't get a result from the first DNS server | |
| 587 or even the first round of the whole DNS server pool. So it could | |
| 588 already be quite some time after we issued the DNS queries in the | |
| 589 first place. Without modifying c-ares, we can't know exactly where in | |
| 590 its retry cycle we are. We could guess based on how much time has | |
| 591 gone by, but it doesn't really matter. Happy Eyeballs tells us that, | |
| 592 given usable information in hand, we simply don't want to wait "too | |
| 593 much longer" after we get a result. | |
| 594 | |
| 595 We simply wait an additional amount of time equal to the default | |
| 596 c-ares query timeout. That is enough time for a typical parallel | |
| 597 response to arrive without being "too long". Even on a network | |
| 598 where one of the two types of queries is failing or timing out | |
| 599 constantly, this will usually mean we wait a total of the default | |
| 600 c-ares timeout (5 seconds) plus the round trip time for the successful | |
| 601 request, which seems bearable. The downside is that c-ares might race | |
| 602 with us to issue one more retry just before we give up, but it seems | |
| 603 better to "waste" that request instead of trying to guess the perfect | |
| 604 timeout to prevent it. After all, we don't even know where in the | |
| 605 c-ares retry cycle each request is. | |
| 606 */ | |
| 607 res->happy_eyeballs_dns_time = Curl_now(); | |
| 608 Curl_expire( | |
| 609 conn->data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS); | |
| 610 } | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 /* | |
| 615 * Curl_resolver_getaddrinfo() - when using ares | |
| 616 * | |
| 617 * Returns name information about the given hostname and port number. If | |
| 618 * successful, the 'hostent' is returned and the forth argument will point to | |
| 619 * memory we need to free after use. That memory *MUST* be freed with | |
| 620 * Curl_freeaddrinfo(), nothing else. | |
| 621 */ | |
| 622 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, | |
| 623 const char *hostname, | |
| 624 int port, | |
| 625 int *waitp) | |
| 626 { | |
| 627 char *bufp; | |
| 628 struct Curl_easy *data = conn->data; | |
| 629 struct in_addr in; | |
| 630 int family = PF_INET; | |
| 631 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ | |
| 632 struct in6_addr in6; | |
| 633 #endif /* CURLRES_IPV6 */ | |
| 634 | |
| 635 *waitp = 0; /* default to synchronous response */ | |
| 636 | |
| 637 /* First check if this is an IPv4 address string */ | |
| 638 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { | |
| 639 /* This is a dotted IP address 123.123.123.123-style */ | |
| 640 return Curl_ip2addr(AF_INET, &in, hostname, port); | |
| 641 } | |
| 642 | |
| 643 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ | |
| 644 /* Otherwise, check if this is an IPv6 address string */ | |
| 645 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0) | |
| 646 /* This must be an IPv6 address literal. */ | |
| 647 return Curl_ip2addr(AF_INET6, &in6, hostname, port); | |
| 648 | |
| 649 switch(conn->ip_version) { | |
| 650 default: | |
| 651 #if ARES_VERSION >= 0x010601 | |
| 652 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older | |
| 653 c-ares versions this just falls through and defaults | |
| 654 to PF_INET */ | |
| 655 break; | |
| 656 #endif | |
| 657 case CURL_IPRESOLVE_V4: | |
| 658 family = PF_INET; | |
| 659 break; | |
| 660 case CURL_IPRESOLVE_V6: | |
| 661 family = PF_INET6; | |
| 662 break; | |
| 663 } | |
| 664 #endif /* CURLRES_IPV6 */ | |
| 665 | |
| 666 bufp = strdup(hostname); | |
| 667 if(bufp) { | |
| 668 struct ResolverResults *res = NULL; | |
| 669 free(conn->async.hostname); | |
| 670 conn->async.hostname = bufp; | |
| 671 conn->async.port = port; | |
| 672 conn->async.done = FALSE; /* not done */ | |
| 673 conn->async.status = 0; /* clear */ | |
| 674 conn->async.dns = NULL; /* clear */ | |
| 675 res = calloc(sizeof(struct ResolverResults), 1); | |
| 676 if(!res) { | |
| 677 free(conn->async.hostname); | |
| 678 conn->async.hostname = NULL; | |
| 679 return NULL; | |
| 680 } | |
| 681 conn->async.os_specific = res; | |
| 682 | |
| 683 /* initial status - failed */ | |
| 684 res->last_status = ARES_ENOTFOUND; | |
| 685 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ | |
| 686 if(family == PF_UNSPEC) { | |
| 687 if(Curl_ipv6works()) { | |
| 688 res->num_pending = 2; | |
| 689 | |
| 690 /* areschannel is already setup in the Curl_open() function */ | |
| 691 ares_gethostbyname((ares_channel)data->state.resolver, hostname, | |
| 692 PF_INET, query_completed_cb, conn); | |
| 693 ares_gethostbyname((ares_channel)data->state.resolver, hostname, | |
| 694 PF_INET6, query_completed_cb, conn); | |
| 695 } | |
| 696 else { | |
| 697 res->num_pending = 1; | |
| 698 | |
| 699 /* areschannel is already setup in the Curl_open() function */ | |
| 700 ares_gethostbyname((ares_channel)data->state.resolver, hostname, | |
| 701 PF_INET, query_completed_cb, conn); | |
| 702 } | |
| 703 } | |
| 704 else | |
| 705 #endif /* CURLRES_IPV6 */ | |
| 706 { | |
| 707 res->num_pending = 1; | |
| 708 | |
| 709 /* areschannel is already setup in the Curl_open() function */ | |
| 710 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family, | |
| 711 query_completed_cb, conn); | |
| 712 } | |
| 713 | |
| 714 *waitp = 1; /* expect asynchronous response */ | |
| 715 } | |
| 716 return NULL; /* no struct yet */ | |
| 717 } | |
| 718 | |
| 719 CURLcode Curl_set_dns_servers(struct Curl_easy *data, | |
| 720 char *servers) | |
| 721 { | |
| 722 CURLcode result = CURLE_NOT_BUILT_IN; | |
| 723 int ares_result; | |
| 724 | |
| 725 /* If server is NULL or empty, this would purge all DNS servers | |
| 726 * from ares library, which will cause any and all queries to fail. | |
| 727 * So, just return OK if none are configured and don't actually make | |
| 728 * any changes to c-ares. This lets c-ares use it's defaults, which | |
| 729 * it gets from the OS (for instance from /etc/resolv.conf on Linux). | |
| 730 */ | |
| 731 if(!(servers && servers[0])) | |
| 732 return CURLE_OK; | |
| 733 | |
| 734 #if (ARES_VERSION >= 0x010704) | |
| 735 #if (ARES_VERSION >= 0x010b00) | |
| 736 ares_result = ares_set_servers_ports_csv(data->state.resolver, servers); | |
| 737 #else | |
| 738 ares_result = ares_set_servers_csv(data->state.resolver, servers); | |
| 739 #endif | |
| 740 switch(ares_result) { | |
| 741 case ARES_SUCCESS: | |
| 742 result = CURLE_OK; | |
| 743 break; | |
| 744 case ARES_ENOMEM: | |
| 745 result = CURLE_OUT_OF_MEMORY; | |
| 746 break; | |
| 747 case ARES_ENOTINITIALIZED: | |
| 748 case ARES_ENODATA: | |
| 749 case ARES_EBADSTR: | |
| 750 default: | |
| 751 result = CURLE_BAD_FUNCTION_ARGUMENT; | |
| 752 break; | |
| 753 } | |
| 754 #else /* too old c-ares version! */ | |
| 755 (void)data; | |
| 756 (void)(ares_result); | |
| 757 #endif | |
| 758 return result; | |
| 759 } | |
| 760 | |
| 761 CURLcode Curl_set_dns_interface(struct Curl_easy *data, | |
| 762 const char *interf) | |
| 763 { | |
| 764 #if (ARES_VERSION >= 0x010704) | |
| 765 if(!interf) | |
| 766 interf = ""; | |
| 767 | |
| 768 ares_set_local_dev((ares_channel)data->state.resolver, interf); | |
| 769 | |
| 770 return CURLE_OK; | |
| 771 #else /* c-ares version too old! */ | |
| 772 (void)data; | |
| 773 (void)interf; | |
| 774 return CURLE_NOT_BUILT_IN; | |
| 775 #endif | |
| 776 } | |
| 777 | |
| 778 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, | |
| 779 const char *local_ip4) | |
| 780 { | |
| 781 #if (ARES_VERSION >= 0x010704) | |
| 782 struct in_addr a4; | |
| 783 | |
| 784 if((!local_ip4) || (local_ip4[0] == 0)) { | |
| 785 a4.s_addr = 0; /* disabled: do not bind to a specific address */ | |
| 786 } | |
| 787 else { | |
| 788 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) { | |
| 789 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 790 } | |
| 791 } | |
| 792 | |
| 793 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr)); | |
| 794 | |
| 795 return CURLE_OK; | |
| 796 #else /* c-ares version too old! */ | |
| 797 (void)data; | |
| 798 (void)local_ip4; | |
| 799 return CURLE_NOT_BUILT_IN; | |
| 800 #endif | |
| 801 } | |
| 802 | |
| 803 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, | |
| 804 const char *local_ip6) | |
| 805 { | |
| 806 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6) | |
| 807 unsigned char a6[INET6_ADDRSTRLEN]; | |
| 808 | |
| 809 if((!local_ip6) || (local_ip6[0] == 0)) { | |
| 810 /* disabled: do not bind to a specific address */ | |
| 811 memset(a6, 0, sizeof(a6)); | |
| 812 } | |
| 813 else { | |
| 814 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) { | |
| 815 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 816 } | |
| 817 } | |
| 818 | |
| 819 ares_set_local_ip6((ares_channel)data->state.resolver, a6); | |
| 820 | |
| 821 return CURLE_OK; | |
| 822 #else /* c-ares version too old! */ | |
| 823 (void)data; | |
| 824 (void)local_ip6; | |
| 825 return CURLE_NOT_BUILT_IN; | |
| 826 #endif | |
| 827 } | |
| 828 #endif /* CURLRES_ARES */ |
