66fb510f4fc5742e055d96d965855bb4be859d23
[platform/upstream/curl.git] / lib / asyn-thread.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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 http://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_NETDB_H
29 #include <netdb.h>
30 #endif
31 #ifdef HAVE_ARPA_INET_H
32 #include <arpa/inet.h>
33 #endif
34 #ifdef __VMS
35 #include <in.h>
36 #include <inet.h>
37 #endif
38
39 #if defined(USE_THREADS_POSIX)
40 #  ifdef HAVE_PTHREAD_H
41 #    include <pthread.h>
42 #  endif
43 #elif defined(USE_THREADS_WIN32)
44 #  ifdef HAVE_PROCESS_H
45 #    include <process.h>
46 #  endif
47 #endif
48
49 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
50 #undef in_addr_t
51 #define in_addr_t unsigned long
52 #endif
53
54 #ifdef HAVE_GETADDRINFO
55 #  define RESOLVER_ENOMEM  EAI_MEMORY
56 #else
57 #  define RESOLVER_ENOMEM  ENOMEM
58 #endif
59
60 #include "urldata.h"
61 #include "sendf.h"
62 #include "hostip.h"
63 #include "hash.h"
64 #include "share.h"
65 #include "strerror.h"
66 #include "url.h"
67 #include "multiif.h"
68 #include "inet_pton.h"
69 #include "inet_ntop.h"
70 #include "curl_threads.h"
71
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
74
75 #include "curl_memory.h"
76 /* The last #include file should be: */
77 #include "memdebug.h"
78
79 /***********************************************************************
80  * Only for threaded name resolves builds
81  **********************************************************************/
82 #ifdef CURLRES_THREADED
83
84 /*
85  * Curl_resolver_global_init()
86  * Called from curl_global_init() to initialize global resolver environment.
87  * Does nothing here.
88  */
89 int Curl_resolver_global_init(void)
90 {
91   return CURLE_OK;
92 }
93
94 /*
95  * Curl_resolver_global_cleanup()
96  * Called from curl_global_cleanup() to destroy global resolver environment.
97  * Does nothing here.
98  */
99 void Curl_resolver_global_cleanup(void)
100 {
101 }
102
103 /*
104  * Curl_resolver_init()
105  * Called from curl_easy_init() -> Curl_open() to initialize resolver
106  * URL-state specific environment ('resolver' member of the UrlState
107  * structure).  Does nothing here.
108  */
109 CURLcode Curl_resolver_init(void **resolver)
110 {
111   (void)resolver;
112   return CURLE_OK;
113 }
114
115 /*
116  * Curl_resolver_cleanup()
117  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
118  * URL-state specific environment ('resolver' member of the UrlState
119  * structure).  Does nothing here.
120  */
121 void Curl_resolver_cleanup(void *resolver)
122 {
123   (void)resolver;
124 }
125
126 /*
127  * Curl_resolver_duphandle()
128  * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
129  * environment ('resolver' member of the UrlState structure).  Does nothing
130  * here.
131  */
132 int Curl_resolver_duphandle(void **to, void *from)
133 {
134   (void)to;
135   (void)from;
136   return CURLE_OK;
137 }
138
139 static void destroy_async_data(struct Curl_async *);
140
141 /*
142  * Cancel all possibly still on-going resolves for this connection.
143  */
144 void Curl_resolver_cancel(struct connectdata *conn)
145 {
146   destroy_async_data(&conn->async);
147 }
148
149 /* This function is used to init a threaded resolve */
150 static bool init_resolve_thread(struct connectdata *conn,
151                                 const char *hostname, int port,
152                                 const struct addrinfo *hints);
153
154
155 /* Data for synchronization between resolver thread and its parent */
156 struct thread_sync_data {
157   curl_mutex_t * mtx;
158   int done;
159
160   char * hostname;        /* hostname to resolve, Curl_async.hostname
161                              duplicate */
162   int port;
163   int sock_error;
164   Curl_addrinfo *res;
165 #ifdef HAVE_GETADDRINFO
166   struct addrinfo hints;
167 #endif
168 };
169
170 struct thread_data {
171   curl_thread_t thread_hnd;
172   unsigned int poll_interval;
173   long interval_end;
174   struct thread_sync_data tsd;
175 };
176
177 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
178 {
179   return &(((struct thread_data *)conn->async.os_specific)->tsd);
180 }
181
182 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
183
184 /* Destroy resolver thread synchronization data */
185 static
186 void destroy_thread_sync_data(struct thread_sync_data * tsd)
187 {
188   if(tsd->mtx) {
189     Curl_mutex_destroy(tsd->mtx);
190     free(tsd->mtx);
191   }
192
193   if(tsd->hostname)
194     free(tsd->hostname);
195
196   if(tsd->res)
197     Curl_freeaddrinfo(tsd->res);
198
199   memset(tsd,0,sizeof(*tsd));
200 }
201
202 /* Initialize resolver thread synchronization data */
203 static
204 int init_thread_sync_data(struct thread_sync_data * tsd,
205                            const char * hostname,
206                            int port,
207                            const struct addrinfo *hints)
208 {
209   memset(tsd, 0, sizeof(*tsd));
210
211   tsd->port = port;
212 #ifdef CURLRES_IPV6
213   DEBUGASSERT(hints);
214   tsd->hints = *hints;
215 #else
216   (void) hints;
217 #endif
218
219   tsd->mtx = malloc(sizeof(curl_mutex_t));
220   if(tsd->mtx == NULL)
221     goto err_exit;
222
223   Curl_mutex_init(tsd->mtx);
224
225   tsd->sock_error = CURL_ASYNC_SUCCESS;
226
227   /* Copying hostname string because original can be destroyed by parent
228    * thread during gethostbyname execution.
229    */
230   tsd->hostname = strdup(hostname);
231   if(!tsd->hostname)
232     goto err_exit;
233
234   return 1;
235
236  err_exit:
237   /* Memory allocation failed */
238   destroy_thread_sync_data(tsd);
239   return 0;
240 }
241
242 static int getaddrinfo_complete(struct connectdata *conn)
243 {
244   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
245   int rc;
246
247   rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
248   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
249      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
250   */
251   tsd->res = NULL;
252
253   return rc;
254 }
255
256
257 #ifdef HAVE_GETADDRINFO
258
259 /*
260  * getaddrinfo_thread() resolves a name and then exits.
261  *
262  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
263  * and wait on it.
264  */
265 static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
266 {
267   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
268   char   service [NI_MAXSERV];
269   int rc;
270
271   snprintf(service, sizeof(service), "%d", tsd->port);
272
273   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
274
275   if(rc != 0) {
276     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
277     if(tsd->sock_error == 0)
278       tsd->sock_error = RESOLVER_ENOMEM;
279   }
280
281   Curl_mutex_acquire(tsd->mtx);
282   tsd->done = 1;
283   Curl_mutex_release(tsd->mtx);
284
285   return 0;
286 }
287
288 #else /* HAVE_GETADDRINFO */
289
290 /*
291  * gethostbyname_thread() resolves a name and then exits.
292  */
293 static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
294 {
295   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
296
297   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
298
299   if(!tsd->res) {
300     tsd->sock_error = SOCKERRNO;
301     if(tsd->sock_error == 0)
302       tsd->sock_error = RESOLVER_ENOMEM;
303   }
304
305   Curl_mutex_acquire(tsd->mtx);
306   tsd->done = 1;
307   Curl_mutex_release(tsd->mtx);
308
309   return 0;
310 }
311
312 #endif /* HAVE_GETADDRINFO */
313
314 /*
315  * destroy_async_data() cleans up async resolver data and thread handle.
316  */
317 static void destroy_async_data (struct Curl_async *async)
318 {
319   if(async->hostname)
320     free(async->hostname);
321
322   if(async->os_specific) {
323     struct thread_data *td = (struct thread_data*) async->os_specific;
324
325     if(td->thread_hnd != curl_thread_t_null)
326       Curl_thread_join(&td->thread_hnd);
327
328     destroy_thread_sync_data(&td->tsd);
329
330     free(async->os_specific);
331   }
332   async->hostname = NULL;
333   async->os_specific = NULL;
334 }
335
336 /*
337  * init_resolve_thread() starts a new thread that performs the actual
338  * resolve. This function returns before the resolve is done.
339  *
340  * Returns FALSE in case of failure, otherwise TRUE.
341  */
342 static bool init_resolve_thread (struct connectdata *conn,
343                                  const char *hostname, int port,
344                                  const struct addrinfo *hints)
345 {
346   struct thread_data *td = calloc(1, sizeof(struct thread_data));
347   int err = RESOLVER_ENOMEM;
348
349   conn->async.os_specific = (void*) td;
350   if(!td)
351     goto err_exit;
352
353   conn->async.port = port;
354   conn->async.done = FALSE;
355   conn->async.status = 0;
356   conn->async.dns = NULL;
357   td->thread_hnd = curl_thread_t_null;
358
359   if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
360     goto err_exit;
361
362   Curl_safefree(conn->async.hostname);
363   conn->async.hostname = strdup(hostname);
364   if(!conn->async.hostname)
365     goto err_exit;
366
367 #ifdef HAVE_GETADDRINFO
368   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
369 #else
370   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
371 #endif
372
373   if(!td->thread_hnd) {
374 #ifndef _WIN32_WCE
375     err = errno;
376 #endif
377     goto err_exit;
378   }
379
380   return TRUE;
381
382  err_exit:
383   destroy_async_data(&conn->async);
384
385   SET_ERRNO(err);
386
387   return FALSE;
388 }
389
390 /*
391  * resolver_error() calls failf() with the appropriate message after a resolve
392  * error
393  */
394
395 static CURLcode resolver_error(struct connectdata *conn)
396 {
397   const char *host_or_proxy;
398   CURLcode rc;
399   if(conn->bits.httpproxy) {
400     host_or_proxy = "proxy";
401     rc = CURLE_COULDNT_RESOLVE_PROXY;
402   }
403   else {
404     host_or_proxy = "host";
405     rc = CURLE_COULDNT_RESOLVE_HOST;
406   }
407
408   failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
409         conn->async.hostname);
410   return rc;
411 }
412
413 /*
414  * Curl_resolver_wait_resolv()
415  *
416  * waits for a resolve to finish. This function should be avoided since using
417  * this risk getting the multi interface to "hang".
418  *
419  * If 'entry' is non-NULL, make it point to the resolved dns entry
420  *
421  * This is the version for resolves-in-a-thread.
422  */
423 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
424                                    struct Curl_dns_entry **entry)
425 {
426   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
427   CURLcode rc = CURLE_OK;
428
429   DEBUGASSERT(conn && td);
430
431   /* wait for the thread to resolve the name */
432   if(Curl_thread_join(&td->thread_hnd))
433     rc = getaddrinfo_complete(conn);
434   else
435     DEBUGASSERT(0);
436
437   conn->async.done = TRUE;
438
439   if(entry)
440     *entry = conn->async.dns;
441
442   if(!conn->async.dns)
443     /* a name was not resolved, report error */
444     rc = resolver_error(conn);
445
446   destroy_async_data(&conn->async);
447
448   if(!conn->async.dns)
449     conn->bits.close = TRUE;
450
451   return (rc);
452 }
453
454 /*
455  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
456  * name resolve request has completed. It should also make sure to time-out if
457  * the operation seems to take too long.
458  */
459 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
460                                    struct Curl_dns_entry **entry)
461 {
462   struct SessionHandle *data = conn->data;
463   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
464   int done = 0;
465
466   *entry = NULL;
467
468   if(!td) {
469     DEBUGASSERT(td);
470     return CURLE_COULDNT_RESOLVE_HOST;
471   }
472
473   Curl_mutex_acquire(td->tsd.mtx);
474   done = td->tsd.done;
475   Curl_mutex_release(td->tsd.mtx);
476
477   if(done) {
478     getaddrinfo_complete(conn);
479
480     if(!conn->async.dns) {
481       CURLcode rc = resolver_error(conn);
482       destroy_async_data(&conn->async);
483       return rc;
484     }
485     destroy_async_data(&conn->async);
486     *entry = conn->async.dns;
487   }
488   else {
489     /* poll for name lookup done with exponential backoff up to 250ms */
490     long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
491     if(elapsed < 0)
492       elapsed = 0;
493
494     if(td->poll_interval == 0)
495       /* Start at 1ms poll interval */
496       td->poll_interval = 1;
497     else if(elapsed >= td->interval_end)
498       /* Back-off exponentially if last interval expired  */
499       td->poll_interval *= 2;
500
501     if(td->poll_interval > 250)
502       td->poll_interval = 250;
503
504     td->interval_end = elapsed + td->poll_interval;
505     Curl_expire(conn->data, td->poll_interval);
506   }
507
508   return CURLE_OK;
509 }
510
511 int Curl_resolver_getsock(struct connectdata *conn,
512                           curl_socket_t *socks,
513                           int numsocks)
514 {
515   (void)conn;
516   (void)socks;
517   (void)numsocks;
518   return 0;
519 }
520
521 #ifndef HAVE_GETADDRINFO
522 /*
523  * Curl_getaddrinfo() - for platforms without getaddrinfo
524  */
525 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
526                                          const char *hostname,
527                                          int port,
528                                          int *waitp)
529 {
530   struct in_addr in;
531
532   *waitp = 0; /* default to synchronous response */
533
534   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
535     /* This is a dotted IP address 123.123.123.123-style */
536     return Curl_ip2addr(AF_INET, &in, hostname, port);
537
538   /* fire up a new resolver thread! */
539   if(init_resolve_thread(conn, hostname, port, NULL)) {
540     *waitp = 1; /* expect asynchronous response */
541     return NULL;
542   }
543
544   /* fall-back to blocking version */
545   return Curl_ipv4_resolve_r(hostname, port);
546 }
547
548 #else /* !HAVE_GETADDRINFO */
549
550 /*
551  * Curl_resolver_getaddrinfo() - for getaddrinfo
552  */
553 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
554                                          const char *hostname,
555                                          int port,
556                                          int *waitp)
557 {
558   struct addrinfo hints;
559   struct in_addr in;
560   Curl_addrinfo *res;
561   int error;
562   char sbuf[NI_MAXSERV];
563   int pf = PF_INET;
564 #ifdef CURLRES_IPV6
565   struct in6_addr in6;
566 #endif /* CURLRES_IPV6 */
567
568   *waitp = 0; /* default to synchronous response */
569
570   /* First check if this is an IPv4 address string */
571   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
572     /* This is a dotted IP address 123.123.123.123-style */
573     return Curl_ip2addr(AF_INET, &in, hostname, port);
574
575 #ifdef CURLRES_IPV6
576   /* check if this is an IPv6 address string */
577   if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
578     /* This is an IPv6 address literal */
579     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
580
581   /*
582    * Check if a limited name resolve has been requested.
583    */
584   switch(conn->ip_version) {
585   case CURL_IPRESOLVE_V4:
586     pf = PF_INET;
587     break;
588   case CURL_IPRESOLVE_V6:
589     pf = PF_INET6;
590     break;
591   default:
592     pf = PF_UNSPEC;
593     break;
594   }
595
596   if((pf != PF_INET) && !Curl_ipv6works())
597     /* the stack seems to be a non-ipv6 one */
598     pf = PF_INET;
599
600 #endif /* CURLRES_IPV6 */
601
602   memset(&hints, 0, sizeof(hints));
603   hints.ai_family = pf;
604   hints.ai_socktype = conn->socktype;
605
606   snprintf(sbuf, sizeof(sbuf), "%d", port);
607
608   /* fire up a new resolver thread! */
609   if(init_resolve_thread(conn, hostname, port, &hints)) {
610     *waitp = 1; /* expect asynchronous response */
611     return NULL;
612   }
613
614   /* fall-back to blocking version */
615   infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
616         hostname, Curl_strerror(conn, ERRNO));
617
618   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
619   if(error) {
620     infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
621           hostname, port, Curl_strerror(conn, SOCKERRNO));
622     return NULL;
623   }
624   return res;
625 }
626
627 #endif /* !HAVE_GETADDRINFO */
628
629 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
630                               char *servers)
631 {
632   (void)data;
633   (void)servers;
634   return CURLE_NOT_BUILT_IN;
635
636 }
637
638 #endif /* CURLRES_THREADED */