1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 /***********************************************************************
26 * Only for threaded name resolves builds
27 **********************************************************************/
28 #ifdef CURLRES_THREADED
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
44 #if defined(USE_THREADS_POSIX)
45 # ifdef HAVE_PTHREAD_H
48 #elif defined(USE_THREADS_WIN32)
49 # ifdef HAVE_PROCESS_H
54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #define in_addr_t unsigned long
59 #ifdef HAVE_GETADDRINFO
60 # define RESOLVER_ENOMEM EAI_MEMORY
62 # define RESOLVER_ENOMEM ENOMEM
73 #include "inet_pton.h"
74 #include "inet_ntop.h"
75 #include "curl_threads.h"
77 /* The last 3 #include files should be in this order */
78 #include "curl_printf.h"
79 #include "curl_memory.h"
83 * Curl_resolver_global_init()
84 * Called from curl_global_init() to initialize global resolver environment.
87 int Curl_resolver_global_init(void)
93 * Curl_resolver_global_cleanup()
94 * Called from curl_global_cleanup() to destroy global resolver environment.
97 void Curl_resolver_global_cleanup(void)
102 * Curl_resolver_init()
103 * Called from curl_easy_init() -> Curl_open() to initialize resolver
104 * URL-state specific environment ('resolver' member of the UrlState
105 * structure). Does nothing here.
107 CURLcode Curl_resolver_init(void **resolver)
114 * Curl_resolver_cleanup()
115 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
116 * URL-state specific environment ('resolver' member of the UrlState
117 * structure). Does nothing here.
119 void Curl_resolver_cleanup(void *resolver)
125 * Curl_resolver_duphandle()
126 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
127 * environment ('resolver' member of the UrlState structure). Does nothing
130 int Curl_resolver_duphandle(void **to, void *from)
137 static void destroy_async_data(struct Curl_async *);
140 * Cancel all possibly still on-going resolves for this connection.
142 void Curl_resolver_cancel(struct connectdata *conn)
144 destroy_async_data(&conn->async);
147 /* This function is used to init a threaded resolve */
148 static bool init_resolve_thread(struct connectdata *conn,
149 const char *hostname, int port,
150 const struct addrinfo *hints);
153 /* Data for synchronization between resolver thread and its parent */
154 struct thread_sync_data {
158 char *hostname; /* hostname to resolve, Curl_async.hostname
163 #ifdef HAVE_GETADDRINFO
164 struct addrinfo hints;
166 struct thread_data *td; /* for thread-self cleanup */
170 curl_thread_t thread_hnd;
171 unsigned int poll_interval;
173 struct thread_sync_data tsd;
176 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
178 return &(((struct thread_data *)conn->async.os_specific)->tsd);
181 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
183 /* Destroy resolver thread synchronization data */
185 void destroy_thread_sync_data(struct thread_sync_data * tsd)
188 Curl_mutex_destroy(tsd->mtx);
195 Curl_freeaddrinfo(tsd->res);
197 memset(tsd, 0, sizeof(*tsd));
200 /* Initialize resolver thread synchronization data */
202 int init_thread_sync_data(struct thread_data * td,
203 const char *hostname,
205 const struct addrinfo *hints)
207 struct thread_sync_data *tsd = &td->tsd;
209 memset(tsd, 0, sizeof(*tsd));
213 /* Treat the request as done until the thread actually starts so any early
214 * cleanup gets done properly.
217 #ifdef HAVE_GETADDRINFO
224 tsd->mtx = malloc(sizeof(curl_mutex_t));
228 Curl_mutex_init(tsd->mtx);
230 tsd->sock_error = CURL_ASYNC_SUCCESS;
232 /* Copying hostname string because original can be destroyed by parent
233 * thread during gethostbyname execution.
235 tsd->hostname = strdup(hostname);
242 /* Memory allocation failed */
243 destroy_thread_sync_data(tsd);
247 static int getaddrinfo_complete(struct connectdata *conn)
249 struct thread_sync_data *tsd = conn_thread_sync_data(conn);
252 rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
253 /* The tsd->res structure has been copied to async.dns and perhaps the DNS
254 cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it.
262 #ifdef HAVE_GETADDRINFO
265 * getaddrinfo_thread() resolves a name and then exits.
267 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
270 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
272 struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
273 struct thread_data *td = tsd->td;
277 snprintf(service, sizeof(service), "%d", tsd->port);
279 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
282 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
283 if(tsd->sock_error == 0)
284 tsd->sock_error = RESOLVER_ENOMEM;
287 Curl_addrinfo_set_port(tsd->res, tsd->port);
290 Curl_mutex_acquire(tsd->mtx);
292 /* too late, gotta clean up the mess */
293 Curl_mutex_release(tsd->mtx);
294 destroy_thread_sync_data(tsd);
299 Curl_mutex_release(tsd->mtx);
305 #else /* HAVE_GETADDRINFO */
308 * gethostbyname_thread() resolves a name and then exits.
310 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
312 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
313 struct thread_data *td = tsd->td;
315 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
318 tsd->sock_error = SOCKERRNO;
319 if(tsd->sock_error == 0)
320 tsd->sock_error = RESOLVER_ENOMEM;
323 Curl_mutex_acquire(tsd->mtx);
325 /* too late, gotta clean up the mess */
326 Curl_mutex_release(tsd->mtx);
327 destroy_thread_sync_data(tsd);
332 Curl_mutex_release(tsd->mtx);
338 #endif /* HAVE_GETADDRINFO */
341 * destroy_async_data() cleans up async resolver data and thread handle.
343 static void destroy_async_data(struct Curl_async *async)
345 if(async->os_specific) {
346 struct thread_data *td = (struct thread_data*) async->os_specific;
350 * if the thread is still blocking in the resolve syscall, detach it and
351 * let the thread do the cleanup...
353 Curl_mutex_acquire(td->tsd.mtx);
356 Curl_mutex_release(td->tsd.mtx);
359 Curl_thread_destroy(td->thread_hnd);
362 if(td->thread_hnd != curl_thread_t_null)
363 Curl_thread_join(&td->thread_hnd);
365 destroy_thread_sync_data(&td->tsd);
367 free(async->os_specific);
370 async->os_specific = NULL;
372 free(async->hostname);
373 async->hostname = NULL;
377 * init_resolve_thread() starts a new thread that performs the actual
378 * resolve. This function returns before the resolve is done.
380 * Returns FALSE in case of failure, otherwise TRUE.
382 static bool init_resolve_thread(struct connectdata *conn,
383 const char *hostname, int port,
384 const struct addrinfo *hints)
386 struct thread_data *td = calloc(1, sizeof(struct thread_data));
389 conn->async.os_specific = (void *)td;
393 conn->async.port = port;
394 conn->async.done = FALSE;
395 conn->async.status = 0;
396 conn->async.dns = NULL;
397 td->thread_hnd = curl_thread_t_null;
399 if(!init_thread_sync_data(td, hostname, port, hints)) {
400 conn->async.os_specific = NULL;
405 free(conn->async.hostname);
406 conn->async.hostname = strdup(hostname);
407 if(!conn->async.hostname)
410 /* The thread will set this to 1 when complete. */
413 #ifdef HAVE_GETADDRINFO
414 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
416 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
419 if(!td->thread_hnd) {
420 /* The thread never started, so mark it as done here for proper cleanup. */
429 destroy_async_data(&conn->async);
437 * resolver_error() calls failf() with the appropriate message after a resolve
441 static CURLcode resolver_error(struct connectdata *conn)
443 const char *host_or_proxy;
446 if(conn->bits.httpproxy) {
447 host_or_proxy = "proxy";
448 result = CURLE_COULDNT_RESOLVE_PROXY;
451 host_or_proxy = "host";
452 result = CURLE_COULDNT_RESOLVE_HOST;
455 failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
456 conn->async.hostname);
462 * Curl_resolver_wait_resolv()
464 * waits for a resolve to finish. This function should be avoided since using
465 * this risk getting the multi interface to "hang".
467 * If 'entry' is non-NULL, make it point to the resolved dns entry
469 * This is the version for resolves-in-a-thread.
471 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
472 struct Curl_dns_entry **entry)
474 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
475 CURLcode result = CURLE_OK;
477 DEBUGASSERT(conn && td);
479 /* wait for the thread to resolve the name */
480 if(Curl_thread_join(&td->thread_hnd))
481 result = getaddrinfo_complete(conn);
485 conn->async.done = TRUE;
488 *entry = conn->async.dns;
491 /* a name was not resolved, report error */
492 result = resolver_error(conn);
494 destroy_async_data(&conn->async);
497 connclose(conn, "asynch resolve failed");
503 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
504 * name resolve request has completed. It should also make sure to time-out if
505 * the operation seems to take too long.
507 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
508 struct Curl_dns_entry **entry)
510 struct Curl_easy *data = conn->data;
511 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
518 return CURLE_COULDNT_RESOLVE_HOST;
521 Curl_mutex_acquire(td->tsd.mtx);
523 Curl_mutex_release(td->tsd.mtx);
526 getaddrinfo_complete(conn);
528 if(!conn->async.dns) {
529 CURLcode result = resolver_error(conn);
530 destroy_async_data(&conn->async);
533 destroy_async_data(&conn->async);
534 *entry = conn->async.dns;
537 /* poll for name lookup done with exponential backoff up to 250ms */
538 timediff_t elapsed = Curl_timediff(Curl_now(),
539 data->progress.t_startsingle);
543 if(td->poll_interval == 0)
544 /* Start at 1ms poll interval */
545 td->poll_interval = 1;
546 else if(elapsed >= td->interval_end)
547 /* Back-off exponentially if last interval expired */
548 td->poll_interval *= 2;
550 if(td->poll_interval > 250)
551 td->poll_interval = 250;
553 td->interval_end = elapsed + td->poll_interval;
554 Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME);
560 int Curl_resolver_getsock(struct connectdata *conn,
561 curl_socket_t *socks,
570 #ifndef HAVE_GETADDRINFO
572 * Curl_getaddrinfo() - for platforms without getaddrinfo
574 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
575 const char *hostname,
581 *waitp = 0; /* default to synchronous response */
583 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
584 /* This is a dotted IP address 123.123.123.123-style */
585 return Curl_ip2addr(AF_INET, &in, hostname, port);
587 /* fire up a new resolver thread! */
588 if(init_resolve_thread(conn, hostname, port, NULL)) {
589 *waitp = 1; /* expect asynchronous response */
593 /* fall-back to blocking version */
594 return Curl_ipv4_resolve_r(hostname, port);
597 #else /* !HAVE_GETADDRINFO */
600 * Curl_resolver_getaddrinfo() - for getaddrinfo
602 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
603 const char *hostname,
607 struct addrinfo hints;
613 *waitp = 0; /* default to synchronous response */
615 #ifndef USE_RESOLVE_ON_IPS
618 /* First check if this is an IPv4 address string */
619 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
620 /* This is a dotted IP address 123.123.123.123-style */
621 return Curl_ip2addr(AF_INET, &in, hostname, port);
626 /* check if this is an IPv6 address string */
627 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
628 /* This is an IPv6 address literal */
629 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
631 #endif /* CURLRES_IPV6 */
632 #endif /* !USE_RESOLVE_ON_IPS */
636 * Check if a limited name resolve has been requested.
638 switch(conn->ip_version) {
639 case CURL_IPRESOLVE_V4:
642 case CURL_IPRESOLVE_V6:
650 if((pf != PF_INET) && !Curl_ipv6works())
651 /* The stack seems to be a non-IPv6 one */
653 #endif /* CURLRES_IPV6 */
655 memset(&hints, 0, sizeof(hints));
656 hints.ai_family = pf;
657 hints.ai_socktype = conn->socktype;
659 snprintf(sbuf, sizeof(sbuf), "%d", port);
661 /* fire up a new resolver thread! */
662 if(init_resolve_thread(conn, hostname, port, &hints)) {
663 *waitp = 1; /* expect asynchronous response */
667 /* fall-back to blocking version */
668 infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
669 hostname, Curl_strerror(conn, errno));
671 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
673 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
674 hostname, port, Curl_strerror(conn, SOCKERRNO));
678 Curl_addrinfo_set_port(res, port);
684 #endif /* !HAVE_GETADDRINFO */
686 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
691 return CURLE_NOT_BUILT_IN;
695 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
700 return CURLE_NOT_BUILT_IN;
703 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
704 const char *local_ip4)
708 return CURLE_NOT_BUILT_IN;
711 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
712 const char *local_ip6)
716 return CURLE_NOT_BUILT_IN;
719 #endif /* CURLRES_THREADED */