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