Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / asyn-thread.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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 #include "connect.h"
72
73 #define _MPRINTF_REPLACE /* use our functions only */
74 #include <curl/mprintf.h>
75
76 #include "curl_memory.h"
77 /* The last #include file should be: */
78 #include "memdebug.h"
79
80 /***********************************************************************
81  * Only for threaded name resolves builds
82  **********************************************************************/
83 #ifdef CURLRES_THREADED
84
85 /*
86  * Curl_resolver_global_init()
87  * Called from curl_global_init() to initialize global resolver environment.
88  * Does nothing here.
89  */
90 int Curl_resolver_global_init(void)
91 {
92   return CURLE_OK;
93 }
94
95 /*
96  * Curl_resolver_global_cleanup()
97  * Called from curl_global_cleanup() to destroy global resolver environment.
98  * Does nothing here.
99  */
100 void Curl_resolver_global_cleanup(void)
101 {
102 }
103
104 /*
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.
109  */
110 CURLcode Curl_resolver_init(void **resolver)
111 {
112   (void)resolver;
113   return CURLE_OK;
114 }
115
116 /*
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.
121  */
122 void Curl_resolver_cleanup(void *resolver)
123 {
124   (void)resolver;
125 }
126
127 /*
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
131  * here.
132  */
133 int Curl_resolver_duphandle(void **to, void *from)
134 {
135   (void)to;
136   (void)from;
137   return CURLE_OK;
138 }
139
140 static void destroy_async_data(struct Curl_async *);
141
142 /*
143  * Cancel all possibly still on-going resolves for this connection.
144  */
145 void Curl_resolver_cancel(struct connectdata *conn)
146 {
147   destroy_async_data(&conn->async);
148 }
149
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);
154
155
156 /* Data for synchronization between resolver thread and its parent */
157 struct thread_sync_data {
158   curl_mutex_t * mtx;
159   int done;
160
161   char * hostname;        /* hostname to resolve, Curl_async.hostname
162                              duplicate */
163   int port;
164   int sock_error;
165   Curl_addrinfo *res;
166 #ifdef HAVE_GETADDRINFO
167   struct addrinfo hints;
168 #endif
169   struct thread_data *td; /* for thread-self cleanup */
170 };
171
172 struct thread_data {
173   curl_thread_t thread_hnd;
174   unsigned int poll_interval;
175   long interval_end;
176   struct thread_sync_data tsd;
177 };
178
179 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
180 {
181   return &(((struct thread_data *)conn->async.os_specific)->tsd);
182 }
183
184 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
185
186 /* Destroy resolver thread synchronization data */
187 static
188 void destroy_thread_sync_data(struct thread_sync_data * tsd)
189 {
190   if(tsd->mtx) {
191     Curl_mutex_destroy(tsd->mtx);
192     free(tsd->mtx);
193   }
194
195   if(tsd->hostname)
196     free(tsd->hostname);
197
198   if(tsd->res)
199     Curl_freeaddrinfo(tsd->res);
200
201   memset(tsd,0,sizeof(*tsd));
202 }
203
204 /* Initialize resolver thread synchronization data */
205 static
206 int init_thread_sync_data(struct thread_data * td,
207                            const char * hostname,
208                            int port,
209                            const struct addrinfo *hints)
210 {
211   struct thread_sync_data *tsd = &td->tsd;
212
213   memset(tsd, 0, sizeof(*tsd));
214
215   tsd->td = td;
216   tsd->port = port;
217 #ifdef HAVE_GETADDRINFO
218   DEBUGASSERT(hints);
219   tsd->hints = *hints;
220 #else
221   (void) hints;
222 #endif
223
224   tsd->mtx = malloc(sizeof(curl_mutex_t));
225   if(tsd->mtx == NULL)
226     goto err_exit;
227
228   Curl_mutex_init(tsd->mtx);
229
230   tsd->sock_error = CURL_ASYNC_SUCCESS;
231
232   /* Copying hostname string because original can be destroyed by parent
233    * thread during gethostbyname execution.
234    */
235   tsd->hostname = strdup(hostname);
236   if(!tsd->hostname)
237     goto err_exit;
238
239   return 1;
240
241  err_exit:
242   /* Memory allocation failed */
243   destroy_thread_sync_data(tsd);
244   return 0;
245 }
246
247 static int getaddrinfo_complete(struct connectdata *conn)
248 {
249   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
250   int rc;
251
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.
255   */
256   tsd->res = NULL;
257
258   return rc;
259 }
260
261
262 #ifdef HAVE_GETADDRINFO
263
264 /*
265  * getaddrinfo_thread() resolves a name and then exits.
266  *
267  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
268  * and wait on it.
269  */
270 static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
271 {
272   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
273   struct thread_data *td = tsd->td;
274   char service[12];
275   int rc;
276
277   snprintf(service, sizeof(service), "%d", tsd->port);
278
279   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
280
281   if(rc != 0) {
282     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
283     if(tsd->sock_error == 0)
284       tsd->sock_error = RESOLVER_ENOMEM;
285   }
286
287   Curl_mutex_acquire(tsd->mtx);
288   if(tsd->done) {
289     /* too late, gotta clean up the mess */
290     Curl_mutex_release(tsd->mtx);
291     destroy_thread_sync_data(tsd);
292     free(td);
293   }
294   else {
295     tsd->done = 1;
296     Curl_mutex_release(tsd->mtx);
297   }
298
299   return 0;
300 }
301
302 #else /* HAVE_GETADDRINFO */
303
304 /*
305  * gethostbyname_thread() resolves a name and then exits.
306  */
307 static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
308 {
309   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
310   struct thread_data *td = tsd->td;
311
312   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
313
314   if(!tsd->res) {
315     tsd->sock_error = SOCKERRNO;
316     if(tsd->sock_error == 0)
317       tsd->sock_error = RESOLVER_ENOMEM;
318   }
319
320   Curl_mutex_acquire(tsd->mtx);
321   if(tsd->done) {
322     /* too late, gotta clean up the mess */
323     Curl_mutex_release(tsd->mtx);
324     destroy_thread_sync_data(tsd);
325     free(td);
326   }
327   else {
328     tsd->done = 1;
329     Curl_mutex_release(tsd->mtx);
330   }
331
332   return 0;
333 }
334
335 #endif /* HAVE_GETADDRINFO */
336
337 /*
338  * destroy_async_data() cleans up async resolver data and thread handle.
339  */
340 static void destroy_async_data (struct Curl_async *async)
341 {
342   if(async->os_specific) {
343     struct thread_data *td = (struct thread_data*) async->os_specific;
344     int done;
345
346     /*
347      * if the thread is still blocking in the resolve syscall, detach it and
348      * let the thread do the cleanup...
349      */
350     Curl_mutex_acquire(td->tsd.mtx);
351     done = td->tsd.done;
352     td->tsd.done = 1;
353     Curl_mutex_release(td->tsd.mtx);
354
355     if(!done) {
356       Curl_thread_destroy(td->thread_hnd);
357     }
358     else {
359       if(td->thread_hnd != curl_thread_t_null)
360         Curl_thread_join(&td->thread_hnd);
361
362       destroy_thread_sync_data(&td->tsd);
363
364       free(async->os_specific);
365     }
366   }
367   async->os_specific = NULL;
368
369   if(async->hostname)
370     free(async->hostname);
371
372   async->hostname = NULL;
373 }
374
375 /*
376  * init_resolve_thread() starts a new thread that performs the actual
377  * resolve. This function returns before the resolve is done.
378  *
379  * Returns FALSE in case of failure, otherwise TRUE.
380  */
381 static bool init_resolve_thread (struct connectdata *conn,
382                                  const char *hostname, int port,
383                                  const struct addrinfo *hints)
384 {
385   struct thread_data *td = calloc(1, sizeof(struct thread_data));
386   int err = RESOLVER_ENOMEM;
387
388   conn->async.os_specific = (void*) td;
389   if(!td)
390     goto err_exit;
391
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;
397
398   if(!init_thread_sync_data(td, hostname, port, hints))
399     goto err_exit;
400
401   Curl_safefree(conn->async.hostname);
402   conn->async.hostname = strdup(hostname);
403   if(!conn->async.hostname)
404     goto err_exit;
405
406 #ifdef HAVE_GETADDRINFO
407   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
408 #else
409   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
410 #endif
411
412   if(!td->thread_hnd) {
413 #ifndef _WIN32_WCE
414     err = errno;
415 #endif
416     goto err_exit;
417   }
418
419   return TRUE;
420
421  err_exit:
422   destroy_async_data(&conn->async);
423
424   SET_ERRNO(err);
425
426   return FALSE;
427 }
428
429 /*
430  * resolver_error() calls failf() with the appropriate message after a resolve
431  * error
432  */
433
434 static CURLcode resolver_error(struct connectdata *conn)
435 {
436   const char *host_or_proxy;
437   CURLcode result;
438
439   if(conn->bits.httpproxy) {
440     host_or_proxy = "proxy";
441     result = CURLE_COULDNT_RESOLVE_PROXY;
442   }
443   else {
444     host_or_proxy = "host";
445     result = CURLE_COULDNT_RESOLVE_HOST;
446   }
447
448   failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
449         conn->async.hostname);
450
451   return result;
452 }
453
454 /*
455  * Curl_resolver_wait_resolv()
456  *
457  * waits for a resolve to finish. This function should be avoided since using
458  * this risk getting the multi interface to "hang".
459  *
460  * If 'entry' is non-NULL, make it point to the resolved dns entry
461  *
462  * This is the version for resolves-in-a-thread.
463  */
464 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
465                                    struct Curl_dns_entry **entry)
466 {
467   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
468   CURLcode result = CURLE_OK;
469
470   DEBUGASSERT(conn && td);
471
472   /* wait for the thread to resolve the name */
473   if(Curl_thread_join(&td->thread_hnd))
474     result = getaddrinfo_complete(conn);
475   else
476     DEBUGASSERT(0);
477
478   conn->async.done = TRUE;
479
480   if(entry)
481     *entry = conn->async.dns;
482
483   if(!conn->async.dns)
484     /* a name was not resolved, report error */
485     result = resolver_error(conn);
486
487   destroy_async_data(&conn->async);
488
489   if(!conn->async.dns)
490     connclose(conn, "asynch resolve failed");
491
492   return result;
493 }
494
495 /*
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.
499  */
500 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
501                                    struct Curl_dns_entry **entry)
502 {
503   struct SessionHandle *data = conn->data;
504   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
505   int done = 0;
506
507   *entry = NULL;
508
509   if(!td) {
510     DEBUGASSERT(td);
511     return CURLE_COULDNT_RESOLVE_HOST;
512   }
513
514   Curl_mutex_acquire(td->tsd.mtx);
515   done = td->tsd.done;
516   Curl_mutex_release(td->tsd.mtx);
517
518   if(done) {
519     getaddrinfo_complete(conn);
520
521     if(!conn->async.dns) {
522       CURLcode result = resolver_error(conn);
523       destroy_async_data(&conn->async);
524       return result;
525     }
526     destroy_async_data(&conn->async);
527     *entry = conn->async.dns;
528   }
529   else {
530     /* poll for name lookup done with exponential backoff up to 250ms */
531     long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
532     if(elapsed < 0)
533       elapsed = 0;
534
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;
541
542     if(td->poll_interval > 250)
543       td->poll_interval = 250;
544
545     td->interval_end = elapsed + td->poll_interval;
546     Curl_expire(conn->data, td->poll_interval);
547   }
548
549   return CURLE_OK;
550 }
551
552 int Curl_resolver_getsock(struct connectdata *conn,
553                           curl_socket_t *socks,
554                           int numsocks)
555 {
556   (void)conn;
557   (void)socks;
558   (void)numsocks;
559   return 0;
560 }
561
562 #ifndef HAVE_GETADDRINFO
563 /*
564  * Curl_getaddrinfo() - for platforms without getaddrinfo
565  */
566 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
567                                          const char *hostname,
568                                          int port,
569                                          int *waitp)
570 {
571   struct in_addr in;
572
573   *waitp = 0; /* default to synchronous response */
574
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);
578
579   /* fire up a new resolver thread! */
580   if(init_resolve_thread(conn, hostname, port, NULL)) {
581     *waitp = 1; /* expect asynchronous response */
582     return NULL;
583   }
584
585   /* fall-back to blocking version */
586   return Curl_ipv4_resolve_r(hostname, port);
587 }
588
589 #else /* !HAVE_GETADDRINFO */
590
591 /*
592  * Curl_resolver_getaddrinfo() - for getaddrinfo
593  */
594 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
595                                          const char *hostname,
596                                          int port,
597                                          int *waitp)
598 {
599   struct addrinfo hints;
600   struct in_addr in;
601   Curl_addrinfo *res;
602   int error;
603   char sbuf[12];
604   int pf = PF_INET;
605 #ifdef CURLRES_IPV6
606   struct in6_addr in6;
607 #endif /* CURLRES_IPV6 */
608
609   *waitp = 0; /* default to synchronous response */
610
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);
615
616 #ifdef CURLRES_IPV6
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);
621
622   /*
623    * Check if a limited name resolve has been requested.
624    */
625   switch(conn->ip_version) {
626   case CURL_IPRESOLVE_V4:
627     pf = PF_INET;
628     break;
629   case CURL_IPRESOLVE_V6:
630     pf = PF_INET6;
631     break;
632   default:
633     pf = PF_UNSPEC;
634     break;
635   }
636
637   if((pf != PF_INET) && !Curl_ipv6works())
638     /* The stack seems to be a non-IPv6 one */
639     pf = PF_INET;
640
641 #endif /* CURLRES_IPV6 */
642
643   memset(&hints, 0, sizeof(hints));
644   hints.ai_family = pf;
645   hints.ai_socktype = conn->socktype;
646
647   snprintf(sbuf, sizeof(sbuf), "%d", port);
648
649   /* fire up a new resolver thread! */
650   if(init_resolve_thread(conn, hostname, port, &hints)) {
651     *waitp = 1; /* expect asynchronous response */
652     return NULL;
653   }
654
655   /* fall-back to blocking version */
656   infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
657         hostname, Curl_strerror(conn, ERRNO));
658
659   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
660   if(error) {
661     infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
662           hostname, port, Curl_strerror(conn, SOCKERRNO));
663     return NULL;
664   }
665   return res;
666 }
667
668 #endif /* !HAVE_GETADDRINFO */
669
670 CURLcode Curl_set_dns_servers(struct SessionHandle *data,
671                               char *servers)
672 {
673   (void)data;
674   (void)servers;
675   return CURLE_NOT_BUILT_IN;
676
677 }
678
679 CURLcode Curl_set_dns_interface(struct SessionHandle *data,
680                                 const char *interf)
681 {
682   (void)data;
683   (void)interf;
684   return CURLE_NOT_BUILT_IN;
685 }
686
687 CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
688                                 const char *local_ip4)
689 {
690   (void)data;
691   (void)local_ip4;
692   return CURLE_NOT_BUILT_IN;
693 }
694
695 CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
696                                 const char *local_ip6)
697 {
698   (void)data;
699   (void)local_ip6;
700   return CURLE_NOT_BUILT_IN;
701 }
702
703 #endif /* CURLRES_THREADED */