1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h>
31 #ifdef HAVE_ARPA_INET_H
32 #include <arpa/inet.h>
39 #if defined(USE_THREADS_POSIX)
40 # ifdef HAVE_PTHREAD_H
43 #elif defined(USE_THREADS_WIN32)
44 # ifdef HAVE_PROCESS_H
49 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
51 #define in_addr_t unsigned long
54 #ifdef HAVE_GETADDRINFO
55 # define RESOLVER_ENOMEM EAI_MEMORY
57 # define RESOLVER_ENOMEM ENOMEM
68 #include "inet_pton.h"
69 #include "inet_ntop.h"
70 #include "curl_threads.h"
73 #define _MPRINTF_REPLACE /* use our functions only */
74 #include <curl/mprintf.h>
76 #include "curl_memory.h"
77 /* The last #include file should be: */
80 /***********************************************************************
81 * Only for threaded name resolves builds
82 **********************************************************************/
83 #ifdef CURLRES_THREADED
86 * Curl_resolver_global_init()
87 * Called from curl_global_init() to initialize global resolver environment.
90 int Curl_resolver_global_init(void)
96 * Curl_resolver_global_cleanup()
97 * Called from curl_global_cleanup() to destroy global resolver environment.
100 void Curl_resolver_global_cleanup(void)
105 * Curl_resolver_init()
106 * Called from curl_easy_init() -> Curl_open() to initialize resolver
107 * URL-state specific environment ('resolver' member of the UrlState
108 * structure). Does nothing here.
110 CURLcode Curl_resolver_init(void **resolver)
117 * Curl_resolver_cleanup()
118 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
119 * URL-state specific environment ('resolver' member of the UrlState
120 * structure). Does nothing here.
122 void Curl_resolver_cleanup(void *resolver)
128 * Curl_resolver_duphandle()
129 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
130 * environment ('resolver' member of the UrlState structure). Does nothing
133 int Curl_resolver_duphandle(void **to, void *from)
140 static void destroy_async_data(struct Curl_async *);
143 * Cancel all possibly still on-going resolves for this connection.
145 void Curl_resolver_cancel(struct connectdata *conn)
147 destroy_async_data(&conn->async);
150 /* This function is used to init a threaded resolve */
151 static bool init_resolve_thread(struct connectdata *conn,
152 const char *hostname, int port,
153 const struct addrinfo *hints);
156 /* Data for synchronization between resolver thread and its parent */
157 struct thread_sync_data {
161 char * hostname; /* hostname to resolve, Curl_async.hostname
166 #ifdef HAVE_GETADDRINFO
167 struct addrinfo hints;
169 struct thread_data *td; /* for thread-self cleanup */
173 curl_thread_t thread_hnd;
174 unsigned int poll_interval;
176 struct thread_sync_data tsd;
179 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
181 return &(((struct thread_data *)conn->async.os_specific)->tsd);
184 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
186 /* Destroy resolver thread synchronization data */
188 void destroy_thread_sync_data(struct thread_sync_data * tsd)
191 Curl_mutex_destroy(tsd->mtx);
199 Curl_freeaddrinfo(tsd->res);
201 memset(tsd,0,sizeof(*tsd));
204 /* Initialize resolver thread synchronization data */
206 int init_thread_sync_data(struct thread_data * td,
207 const char * hostname,
209 const struct addrinfo *hints)
211 struct thread_sync_data *tsd = &td->tsd;
213 memset(tsd, 0, sizeof(*tsd));
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_mutex_acquire(tsd->mtx);
289 /* too late, gotta clean up the mess */
290 Curl_mutex_release(tsd->mtx);
291 destroy_thread_sync_data(tsd);
296 Curl_mutex_release(tsd->mtx);
302 #else /* HAVE_GETADDRINFO */
305 * gethostbyname_thread() resolves a name and then exits.
307 static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
309 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
310 struct thread_data *td = tsd->td;
312 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
315 tsd->sock_error = SOCKERRNO;
316 if(tsd->sock_error == 0)
317 tsd->sock_error = RESOLVER_ENOMEM;
320 Curl_mutex_acquire(tsd->mtx);
322 /* too late, gotta clean up the mess */
323 Curl_mutex_release(tsd->mtx);
324 destroy_thread_sync_data(tsd);
329 Curl_mutex_release(tsd->mtx);
335 #endif /* HAVE_GETADDRINFO */
338 * destroy_async_data() cleans up async resolver data and thread handle.
340 static void destroy_async_data (struct Curl_async *async)
342 if(async->os_specific) {
343 struct thread_data *td = (struct thread_data*) async->os_specific;
347 * if the thread is still blocking in the resolve syscall, detach it and
348 * let the thread do the cleanup...
350 Curl_mutex_acquire(td->tsd.mtx);
353 Curl_mutex_release(td->tsd.mtx);
356 Curl_thread_destroy(td->thread_hnd);
359 if(td->thread_hnd != curl_thread_t_null)
360 Curl_thread_join(&td->thread_hnd);
362 destroy_thread_sync_data(&td->tsd);
364 free(async->os_specific);
367 async->os_specific = NULL;
370 free(async->hostname);
372 async->hostname = NULL;
376 * init_resolve_thread() starts a new thread that performs the actual
377 * resolve. This function returns before the resolve is done.
379 * Returns FALSE in case of failure, otherwise TRUE.
381 static bool init_resolve_thread (struct connectdata *conn,
382 const char *hostname, int port,
383 const struct addrinfo *hints)
385 struct thread_data *td = calloc(1, sizeof(struct thread_data));
386 int err = RESOLVER_ENOMEM;
388 conn->async.os_specific = (void*) td;
392 conn->async.port = port;
393 conn->async.done = FALSE;
394 conn->async.status = 0;
395 conn->async.dns = NULL;
396 td->thread_hnd = curl_thread_t_null;
398 if(!init_thread_sync_data(td, hostname, port, hints))
401 Curl_safefree(conn->async.hostname);
402 conn->async.hostname = strdup(hostname);
403 if(!conn->async.hostname)
406 #ifdef HAVE_GETADDRINFO
407 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
409 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
412 if(!td->thread_hnd) {
422 destroy_async_data(&conn->async);
430 * resolver_error() calls failf() with the appropriate message after a resolve
434 static CURLcode resolver_error(struct connectdata *conn)
436 const char *host_or_proxy;
439 if(conn->bits.httpproxy) {
440 host_or_proxy = "proxy";
441 result = CURLE_COULDNT_RESOLVE_PROXY;
444 host_or_proxy = "host";
445 result = CURLE_COULDNT_RESOLVE_HOST;
448 failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
449 conn->async.hostname);
455 * Curl_resolver_wait_resolv()
457 * waits for a resolve to finish. This function should be avoided since using
458 * this risk getting the multi interface to "hang".
460 * If 'entry' is non-NULL, make it point to the resolved dns entry
462 * This is the version for resolves-in-a-thread.
464 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
465 struct Curl_dns_entry **entry)
467 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
468 CURLcode result = CURLE_OK;
470 DEBUGASSERT(conn && td);
472 /* wait for the thread to resolve the name */
473 if(Curl_thread_join(&td->thread_hnd))
474 result = getaddrinfo_complete(conn);
478 conn->async.done = TRUE;
481 *entry = conn->async.dns;
484 /* a name was not resolved, report error */
485 result = resolver_error(conn);
487 destroy_async_data(&conn->async);
490 connclose(conn, "asynch resolve failed");
496 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
497 * name resolve request has completed. It should also make sure to time-out if
498 * the operation seems to take too long.
500 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
501 struct Curl_dns_entry **entry)
503 struct SessionHandle *data = conn->data;
504 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
511 return CURLE_COULDNT_RESOLVE_HOST;
514 Curl_mutex_acquire(td->tsd.mtx);
516 Curl_mutex_release(td->tsd.mtx);
519 getaddrinfo_complete(conn);
521 if(!conn->async.dns) {
522 CURLcode result = resolver_error(conn);
523 destroy_async_data(&conn->async);
526 destroy_async_data(&conn->async);
527 *entry = conn->async.dns;
530 /* poll for name lookup done with exponential backoff up to 250ms */
531 long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
535 if(td->poll_interval == 0)
536 /* Start at 1ms poll interval */
537 td->poll_interval = 1;
538 else if(elapsed >= td->interval_end)
539 /* Back-off exponentially if last interval expired */
540 td->poll_interval *= 2;
542 if(td->poll_interval > 250)
543 td->poll_interval = 250;
545 td->interval_end = elapsed + td->poll_interval;
546 Curl_expire(conn->data, td->poll_interval);
552 int Curl_resolver_getsock(struct connectdata *conn,
553 curl_socket_t *socks,
562 #ifndef HAVE_GETADDRINFO
564 * Curl_getaddrinfo() - for platforms without getaddrinfo
566 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
567 const char *hostname,
573 *waitp = 0; /* default to synchronous response */
575 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
576 /* This is a dotted IP address 123.123.123.123-style */
577 return Curl_ip2addr(AF_INET, &in, hostname, port);
579 /* fire up a new resolver thread! */
580 if(init_resolve_thread(conn, hostname, port, NULL)) {
581 *waitp = 1; /* expect asynchronous response */
585 /* fall-back to blocking version */
586 return Curl_ipv4_resolve_r(hostname, port);
589 #else /* !HAVE_GETADDRINFO */
592 * Curl_resolver_getaddrinfo() - for getaddrinfo
594 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
595 const char *hostname,
599 struct addrinfo hints;
607 #endif /* CURLRES_IPV6 */
609 *waitp = 0; /* default to synchronous response */
611 /* First check if this is an IPv4 address string */
612 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
613 /* This is a dotted IP address 123.123.123.123-style */
614 return Curl_ip2addr(AF_INET, &in, hostname, port);
617 /* check if this is an IPv6 address string */
618 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
619 /* This is an IPv6 address literal */
620 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
623 * Check if a limited name resolve has been requested.
625 switch(conn->ip_version) {
626 case CURL_IPRESOLVE_V4:
629 case CURL_IPRESOLVE_V6:
637 if((pf != PF_INET) && !Curl_ipv6works())
638 /* The stack seems to be a non-IPv6 one */
641 #endif /* CURLRES_IPV6 */
643 memset(&hints, 0, sizeof(hints));
644 hints.ai_family = pf;
645 hints.ai_socktype = conn->socktype;
647 snprintf(sbuf, sizeof(sbuf), "%d", port);
649 /* fire up a new resolver thread! */
650 if(init_resolve_thread(conn, hostname, port, &hints)) {
651 *waitp = 1; /* expect asynchronous response */
655 /* fall-back to blocking version */
656 infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
657 hostname, Curl_strerror(conn, ERRNO));
659 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
661 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
662 hostname, port, Curl_strerror(conn, SOCKERRNO));
668 #endif /* !HAVE_GETADDRINFO */
670 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
675 return CURLE_NOT_BUILT_IN;
679 CURLcode Curl_set_dns_interface(struct SessionHandle *data,
684 return CURLE_NOT_BUILT_IN;
687 CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
688 const char *local_ip4)
692 return CURLE_NOT_BUILT_IN;
695 CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
696 const char *local_ip6)
700 return CURLE_NOT_BUILT_IN;
703 #endif /* CURLRES_THREADED */