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;
172 curl_thread_t thread_hnd;
173 unsigned int poll_interval;
175 struct thread_sync_data tsd;
178 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
180 return &(((struct thread_data *)conn->async.os_specific)->tsd);
183 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
185 /* Destroy resolver thread synchronization data */
187 void destroy_thread_sync_data(struct thread_sync_data * tsd)
190 Curl_mutex_destroy(tsd->mtx);
198 Curl_freeaddrinfo(tsd->res);
200 memset(tsd,0,sizeof(*tsd));
203 /* Initialize resolver thread synchronization data */
205 int init_thread_sync_data(struct thread_sync_data * tsd,
206 const char * hostname,
208 const struct addrinfo *hints)
210 memset(tsd, 0, sizeof(*tsd));
213 #ifdef HAVE_GETADDRINFO
220 tsd->mtx = malloc(sizeof(curl_mutex_t));
224 Curl_mutex_init(tsd->mtx);
226 tsd->sock_error = CURL_ASYNC_SUCCESS;
228 /* Copying hostname string because original can be destroyed by parent
229 * thread during gethostbyname execution.
231 tsd->hostname = strdup(hostname);
238 /* Memory allocation failed */
239 destroy_thread_sync_data(tsd);
243 static int getaddrinfo_complete(struct connectdata *conn)
245 struct thread_sync_data *tsd = conn_thread_sync_data(conn);
248 rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
249 /* The tsd->res structure has been copied to async.dns and perhaps the DNS
250 cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it.
258 #ifdef HAVE_GETADDRINFO
261 * getaddrinfo_thread() resolves a name and then exits.
263 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
266 static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
268 struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
272 snprintf(service, sizeof(service), "%d", tsd->port);
274 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
277 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
278 if(tsd->sock_error == 0)
279 tsd->sock_error = RESOLVER_ENOMEM;
282 Curl_mutex_acquire(tsd->mtx);
284 Curl_mutex_release(tsd->mtx);
289 #else /* HAVE_GETADDRINFO */
292 * gethostbyname_thread() resolves a name and then exits.
294 static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
296 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
298 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
301 tsd->sock_error = SOCKERRNO;
302 if(tsd->sock_error == 0)
303 tsd->sock_error = RESOLVER_ENOMEM;
306 Curl_mutex_acquire(tsd->mtx);
308 Curl_mutex_release(tsd->mtx);
313 #endif /* HAVE_GETADDRINFO */
316 * destroy_async_data() cleans up async resolver data and thread handle.
318 static void destroy_async_data (struct Curl_async *async)
321 free(async->hostname);
323 if(async->os_specific) {
324 struct thread_data *td = (struct thread_data*) async->os_specific;
326 if(td->thread_hnd != curl_thread_t_null)
327 Curl_thread_join(&td->thread_hnd);
329 destroy_thread_sync_data(&td->tsd);
331 free(async->os_specific);
333 async->hostname = NULL;
334 async->os_specific = NULL;
338 * init_resolve_thread() starts a new thread that performs the actual
339 * resolve. This function returns before the resolve is done.
341 * Returns FALSE in case of failure, otherwise TRUE.
343 static bool init_resolve_thread (struct connectdata *conn,
344 const char *hostname, int port,
345 const struct addrinfo *hints)
347 struct thread_data *td = calloc(1, sizeof(struct thread_data));
348 int err = RESOLVER_ENOMEM;
350 conn->async.os_specific = (void*) td;
354 conn->async.port = port;
355 conn->async.done = FALSE;
356 conn->async.status = 0;
357 conn->async.dns = NULL;
358 td->thread_hnd = curl_thread_t_null;
360 if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
363 Curl_safefree(conn->async.hostname);
364 conn->async.hostname = strdup(hostname);
365 if(!conn->async.hostname)
368 #ifdef HAVE_GETADDRINFO
369 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
371 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
374 if(!td->thread_hnd) {
384 destroy_async_data(&conn->async);
392 * resolver_error() calls failf() with the appropriate message after a resolve
396 static CURLcode resolver_error(struct connectdata *conn)
398 const char *host_or_proxy;
400 if(conn->bits.httpproxy) {
401 host_or_proxy = "proxy";
402 rc = CURLE_COULDNT_RESOLVE_PROXY;
405 host_or_proxy = "host";
406 rc = CURLE_COULDNT_RESOLVE_HOST;
409 failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
410 conn->async.hostname);
415 * Curl_resolver_wait_resolv()
417 * waits for a resolve to finish. This function should be avoided since using
418 * this risk getting the multi interface to "hang".
420 * If 'entry' is non-NULL, make it point to the resolved dns entry
422 * This is the version for resolves-in-a-thread.
424 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
425 struct Curl_dns_entry **entry)
427 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
428 CURLcode rc = CURLE_OK;
430 DEBUGASSERT(conn && td);
432 /* wait for the thread to resolve the name */
433 if(Curl_thread_join(&td->thread_hnd))
434 rc = getaddrinfo_complete(conn);
438 conn->async.done = TRUE;
441 *entry = conn->async.dns;
444 /* a name was not resolved, report error */
445 rc = resolver_error(conn);
447 destroy_async_data(&conn->async);
450 connclose(conn, "asynch resolve failed");
456 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
457 * name resolve request has completed. It should also make sure to time-out if
458 * the operation seems to take too long.
460 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
461 struct Curl_dns_entry **entry)
463 struct SessionHandle *data = conn->data;
464 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
471 return CURLE_COULDNT_RESOLVE_HOST;
474 Curl_mutex_acquire(td->tsd.mtx);
476 Curl_mutex_release(td->tsd.mtx);
479 getaddrinfo_complete(conn);
481 if(!conn->async.dns) {
482 CURLcode rc = resolver_error(conn);
483 destroy_async_data(&conn->async);
486 destroy_async_data(&conn->async);
487 *entry = conn->async.dns;
490 /* poll for name lookup done with exponential backoff up to 250ms */
491 long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
495 if(td->poll_interval == 0)
496 /* Start at 1ms poll interval */
497 td->poll_interval = 1;
498 else if(elapsed >= td->interval_end)
499 /* Back-off exponentially if last interval expired */
500 td->poll_interval *= 2;
502 if(td->poll_interval > 250)
503 td->poll_interval = 250;
505 td->interval_end = elapsed + td->poll_interval;
506 Curl_expire(conn->data, td->poll_interval);
512 int Curl_resolver_getsock(struct connectdata *conn,
513 curl_socket_t *socks,
522 #ifndef HAVE_GETADDRINFO
524 * Curl_getaddrinfo() - for platforms without getaddrinfo
526 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
527 const char *hostname,
533 *waitp = 0; /* default to synchronous response */
535 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
536 /* This is a dotted IP address 123.123.123.123-style */
537 return Curl_ip2addr(AF_INET, &in, hostname, port);
539 /* fire up a new resolver thread! */
540 if(init_resolve_thread(conn, hostname, port, NULL)) {
541 *waitp = 1; /* expect asynchronous response */
545 /* fall-back to blocking version */
546 return Curl_ipv4_resolve_r(hostname, port);
549 #else /* !HAVE_GETADDRINFO */
552 * Curl_resolver_getaddrinfo() - for getaddrinfo
554 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
555 const char *hostname,
559 struct addrinfo hints;
567 #endif /* CURLRES_IPV6 */
569 *waitp = 0; /* default to synchronous response */
571 /* First check if this is an IPv4 address string */
572 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
573 /* This is a dotted IP address 123.123.123.123-style */
574 return Curl_ip2addr(AF_INET, &in, hostname, port);
577 /* check if this is an IPv6 address string */
578 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
579 /* This is an IPv6 address literal */
580 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
583 * Check if a limited name resolve has been requested.
585 switch(conn->ip_version) {
586 case CURL_IPRESOLVE_V4:
589 case CURL_IPRESOLVE_V6:
597 if((pf != PF_INET) && !Curl_ipv6works())
598 /* the stack seems to be a non-ipv6 one */
601 #endif /* CURLRES_IPV6 */
603 memset(&hints, 0, sizeof(hints));
604 hints.ai_family = pf;
605 hints.ai_socktype = conn->socktype;
607 snprintf(sbuf, sizeof(sbuf), "%d", port);
609 /* fire up a new resolver thread! */
610 if(init_resolve_thread(conn, hostname, port, &hints)) {
611 *waitp = 1; /* expect asynchronous response */
615 /* fall-back to blocking version */
616 infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
617 hostname, Curl_strerror(conn, ERRNO));
619 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
621 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
622 hostname, port, Curl_strerror(conn, SOCKERRNO));
628 #endif /* !HAVE_GETADDRINFO */
630 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
635 return CURLE_NOT_BUILT_IN;
639 CURLcode Curl_set_dns_interface(struct SessionHandle *data,
644 return CURLE_NOT_BUILT_IN;
647 CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
648 const char *local_ip4)
652 return CURLE_NOT_BUILT_IN;
655 CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
656 const char *local_ip6)
660 return CURLE_NOT_BUILT_IN;
663 #endif /* CURLRES_THREADED */