Update tag value for tizen 2.0 build
[external/curl.git] / lib / hostthre.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, 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 #include "urldata.h"
68 #include "sendf.h"
69 #include "hostip.h"
70 #include "hash.h"
71 #include "share.h"
72 #include "strerror.h"
73 #include "url.h"
74 #include "multiif.h"
75 #include "inet_pton.h"
76 #include "inet_ntop.h"
77 #include "curl_threads.h"
78
79 #define _MPRINTF_REPLACE /* use our functions only */
80 #include <curl/mprintf.h>
81
82 #include "curl_memory.h"
83 /* The last #include file should be: */
84 #include "memdebug.h"
85
86 /***********************************************************************
87  * Only for threaded name resolves builds
88  **********************************************************************/
89 #ifdef CURLRES_THREADED
90
91 /* This function is used to init a threaded resolve */
92 static bool init_resolve_thread(struct connectdata *conn,
93                                 const char *hostname, int port,
94                                 const struct addrinfo *hints);
95
96
97 /* Data for synchronization between resolver thread and its parent */
98 struct thread_sync_data {
99   curl_mutex_t * mtx;
100   int done;
101
102   char * hostname;        /* hostname to resolve, Curl_async.hostname
103                              duplicate */
104   int port;
105   int sock_error;
106   Curl_addrinfo *res;
107 #ifdef HAVE_GETADDRINFO
108   struct addrinfo hints;
109 #endif
110 };
111
112 struct thread_data {
113   curl_thread_t thread_hnd;
114   curl_socket_t dummy_sock;
115   unsigned int poll_interval;
116   int interval_end;
117   struct thread_sync_data tsd;
118 };
119
120 static struct thread_sync_data * conn_thread_sync_data(struct connectdata *conn)
121 {
122   return &(((struct thread_data *)conn->async.os_specific)->tsd);
123 }
124
125 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
126
127 /* Destroy resolver thread synchronization data */
128 static
129 void destroy_thread_sync_data(struct thread_sync_data * tsd)
130 {
131   if (tsd->mtx) {
132     Curl_mutex_destroy(tsd->mtx);
133     free(tsd->mtx);
134   }
135
136   if(tsd->hostname)
137     free(tsd->hostname);
138
139   if (tsd->res)
140     Curl_freeaddrinfo(tsd->res);
141
142   memset(tsd,0,sizeof(*tsd));
143 }
144
145 /* Initialize resolver thread synchronization data */
146 static
147 int init_thread_sync_data(struct thread_sync_data * tsd,
148                            const char * hostname,
149                            int port,
150                            const struct addrinfo *hints)
151 {
152   memset(tsd, 0, sizeof(*tsd));
153
154   tsd->port = port;
155 #ifdef CURLRES_IPV6
156   DEBUGASSERT(hints);
157   tsd->hints = *hints;
158 #else
159   (void) hints;
160 #endif
161
162   tsd->mtx = malloc(sizeof(curl_mutex_t));
163   if (tsd->mtx == NULL) goto err_exit;
164
165   Curl_mutex_init(tsd->mtx);
166
167   tsd->sock_error = CURL_ASYNC_SUCCESS;
168
169   /* Copying hostname string because original can be destroyed by parent
170    * thread during gethostbyname execution.
171    */
172   tsd->hostname = strdup(hostname);
173   if (!tsd->hostname) goto err_exit;
174
175   return 1;
176
177  err_exit:
178   /* Memory allocation failed */
179   destroy_thread_sync_data(tsd);
180   return 0;
181 }
182
183 static int getaddrinfo_complete(struct connectdata *conn)
184 {
185   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
186   int rc;
187
188   rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
189   /* The tsd->res structure has been copied to async.dns and perhaps the DNS cache.
190      Set our copy to NULL so destroy_thread_sync_data doesn't free it.
191    */
192   tsd->res = NULL;
193
194   return rc;
195 }
196
197
198 #ifdef HAVE_GETADDRINFO
199
200 /*
201  * getaddrinfo_thread() resolves a name and then exits.
202  *
203  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
204  * and wait on it.
205  */
206 static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
207 {
208   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
209   char   service [NI_MAXSERV];
210   int rc;
211
212   snprintf(service, sizeof(service), "%d", tsd->port);
213
214   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
215
216   if (rc != 0) {
217     tsd->sock_error = SOCKERRNO;
218     if (tsd->sock_error == 0)
219       tsd->sock_error = ENOMEM;
220   }
221
222   Curl_mutex_acquire(tsd->mtx);
223   tsd->done = 1;
224   Curl_mutex_release(tsd->mtx);
225
226   return 0;
227 }
228
229 #else /* HAVE_GETADDRINFO */
230
231 /*
232  * gethostbyname_thread() resolves a name and then exits.
233  */
234 static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
235 {
236   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
237
238   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
239
240   if (!tsd->res) {
241     tsd->sock_error = SOCKERRNO;
242     if (tsd->sock_error == 0)
243       tsd->sock_error = ENOMEM;
244   }
245
246   Curl_mutex_acquire(tsd->mtx);
247   tsd->done = 1;
248   Curl_mutex_release(tsd->mtx);
249
250   return 0;
251 }
252
253 #endif /* HAVE_GETADDRINFO */
254
255 /*
256  * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
257  * Complementary of ares_destroy.
258  */
259 void Curl_destroy_thread_data (struct Curl_async *async)
260 {
261   if(async->hostname)
262     free(async->hostname);
263
264   if(async->os_specific) {
265     struct thread_data *td = (struct thread_data*) async->os_specific;
266
267     if (td->dummy_sock != CURL_SOCKET_BAD)
268       sclose(td->dummy_sock);
269
270     if (td->thread_hnd != curl_thread_t_null)
271       Curl_thread_join(&td->thread_hnd);
272
273     destroy_thread_sync_data(&td->tsd);
274
275     free(async->os_specific);
276   }
277   async->hostname = NULL;
278   async->os_specific = NULL;
279 }
280
281 /*
282  * init_resolve_thread() starts a new thread that performs the actual
283  * resolve. This function returns before the resolve is done.
284  *
285  * Returns FALSE in case of failure, otherwise TRUE.
286  */
287 static bool init_resolve_thread (struct connectdata *conn,
288                                  const char *hostname, int port,
289                                  const struct addrinfo *hints)
290 {
291   struct thread_data *td = calloc(1, sizeof(struct thread_data));
292   int err = ENOMEM;
293
294   conn->async.os_specific = (void*) td;
295   if(!td)
296     goto err_exit;
297
298   conn->async.port = port;
299   conn->async.done = FALSE;
300   conn->async.status = 0;
301   conn->async.dns = NULL;
302   td->dummy_sock = CURL_SOCKET_BAD;
303   td->thread_hnd = curl_thread_t_null;
304
305   if (!init_thread_sync_data(&td->tsd, hostname, port, hints))
306     goto err_exit;
307
308   Curl_safefree(conn->async.hostname);
309   conn->async.hostname = strdup(hostname);
310   if(!conn->async.hostname)
311     goto err_exit;
312
313 #ifdef WIN32
314   /* This socket is only to keep Curl_resolv_fdset() and select() happy;
315    * should never become signalled for read since it's unbound but
316    * Windows needs at least 1 socket in select().
317    */
318   td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
319   if (td->dummy_sock == CURL_SOCKET_BAD)
320     goto err_exit;
321 #endif
322
323 #ifdef HAVE_GETADDRINFO
324   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
325 #else
326   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
327 #endif
328
329   if(!td->thread_hnd) {
330 #ifndef _WIN32_WCE
331     err = errno;
332 #endif
333     goto err_exit;
334   }
335
336   return TRUE;
337
338  err_exit:
339   Curl_destroy_thread_data(&conn->async);
340
341   SET_ERRNO(err);
342
343   return FALSE;
344 }
345
346
347 /*
348  * Curl_wait_for_resolv() waits for a resolve to finish. This function should
349  * be avoided since using this risk getting the multi interface to "hang".
350  *
351  * If 'entry' is non-NULL, make it point to the resolved dns entry
352  *
353  * This is the version for resolves-in-a-thread.
354  */
355 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
356                               struct Curl_dns_entry **entry)
357 {
358   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
359   struct SessionHandle *data = conn->data;
360   CURLcode rc = CURLE_OK;
361
362   DEBUGASSERT(conn && td);
363
364   /* wait for the thread to resolve the name */
365   if (Curl_thread_join(&td->thread_hnd)) {
366     rc = getaddrinfo_complete(conn);
367   } else {
368     DEBUGASSERT(0);
369   }
370
371   conn->async.done = TRUE;
372
373   if(entry)
374     *entry = conn->async.dns;
375
376   if(!conn->async.dns) {
377     /* a name was not resolved */
378     if (conn->bits.httpproxy) {
379       failf(data, "Could not resolve proxy: %s; %s",
380             conn->async.hostname, Curl_strerror(conn, conn->async.status));
381       rc = CURLE_COULDNT_RESOLVE_PROXY;
382     } else {
383       failf(data, "Could not resolve host: %s; %s",
384             conn->async.hostname, Curl_strerror(conn, conn->async.status));
385       rc = CURLE_COULDNT_RESOLVE_HOST;
386     }
387   }
388
389   Curl_destroy_thread_data(&conn->async);
390
391   if(!conn->async.dns)
392     conn->bits.close = TRUE;
393
394   return (rc);
395 }
396
397 /*
398  * Curl_is_resolved() is called repeatedly to check if a previous name resolve
399  * request has completed. It should also make sure to time-out if the
400  * operation seems to take too long.
401  */
402 CURLcode Curl_is_resolved(struct connectdata *conn,
403                           struct Curl_dns_entry **entry)
404 {
405   struct SessionHandle *data = conn->data;
406   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
407   int done = 0;
408
409   *entry = NULL;
410
411   if (!td) {
412     DEBUGASSERT(td);
413     return CURLE_COULDNT_RESOLVE_HOST;
414   }
415
416   Curl_mutex_acquire(td->tsd.mtx);
417   done = td->tsd.done;
418   Curl_mutex_release(td->tsd.mtx);
419
420   if (done) {
421     getaddrinfo_complete(conn);
422     Curl_destroy_thread_data(&conn->async);
423
424     if(!conn->async.dns) {
425       failf(data, "Could not resolve host: %s; %s",
426             conn->host.name, Curl_strerror(conn, conn->async.status));
427       return CURLE_COULDNT_RESOLVE_HOST;
428     }
429     *entry = conn->async.dns;
430   } else {
431     /* poll for name lookup done with exponential backoff up to 250ms */
432     int elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
433     if (elapsed < 0)
434       elapsed = 0;
435
436     if (td->poll_interval == 0)
437       /* Start at 1ms poll interval */
438       td->poll_interval = 1;
439     else if (elapsed >= td->interval_end)
440       /* Back-off exponentially if last interval expired  */
441       td->poll_interval *= 2;
442
443     if (td->poll_interval > 250)
444       td->poll_interval = 250;
445
446     td->interval_end = elapsed + td->poll_interval;
447     Curl_expire(conn->data, td->poll_interval);
448   }
449
450   return CURLE_OK;
451 }
452
453 int Curl_resolv_getsock(struct connectdata *conn,
454                         curl_socket_t *socks,
455                         int numsocks)
456 {
457   const struct thread_data *td =
458     (const struct thread_data *) conn->async.os_specific;
459
460   if(td && td->dummy_sock != CURL_SOCKET_BAD) {
461     if(numsocks) {
462       /* return one socket waiting for readable, even though this is just
463          a dummy */
464       socks[0] = td->dummy_sock;
465       return GETSOCK_READSOCK(0);
466     }
467   }
468   return 0;
469 }
470
471 #ifndef HAVE_GETADDRINFO
472 /*
473  * Curl_getaddrinfo() - for platforms without getaddrinfo
474  */
475 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
476                                 const char *hostname,
477                                 int port,
478                                 int *waitp)
479 {
480   struct in_addr in;
481
482   *waitp = 0; /* default to synchronous response */
483
484   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
485     /* This is a dotted IP address 123.123.123.123-style */
486     return Curl_ip2addr(AF_INET, &in, hostname, port);
487
488   /* fire up a new resolver thread! */
489   if(init_resolve_thread(conn, hostname, port, NULL)) {
490     *waitp = 1; /* expect asynchronous response */
491     return NULL;
492   }
493
494   /* fall-back to blocking version */
495   return Curl_ipv4_resolve_r(hostname, port);
496 }
497
498 #else /* !HAVE_GETADDRINFO */
499
500 /*
501  * Curl_getaddrinfo() - for getaddrinfo
502  */
503 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
504                                 const char *hostname,
505                                 int port,
506                                 int *waitp)
507 {
508   struct addrinfo hints;
509   Curl_addrinfo *res;
510   int error;
511   char sbuf[NI_MAXSERV];
512   int pf = PF_INET;
513   struct SessionHandle *data = conn->data;
514
515   *waitp = 0; /* default to synchronous response */
516
517 #ifndef CURLRES_IPV4
518   /*
519    * Check if a limited name resolve has been requested.
520    */
521   switch(conn->ip_version) {
522   case CURL_IPRESOLVE_V4:
523     pf = PF_INET;
524     break;
525   case CURL_IPRESOLVE_V6:
526     pf = PF_INET6;
527     break;
528   default:
529     pf = PF_UNSPEC;
530     break;
531   }
532
533   if (pf != PF_INET) {
534     /* see if we have an IPv6 stack */
535     curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
536     if(s == CURL_SOCKET_BAD) {
537       /* Some non-IPv6 stacks have been found to make very slow name resolves
538        * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
539        * the stack seems to be a non-ipv6 one. */
540
541       pf = PF_INET;
542     }
543     else {
544       /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
545        * possible checks. And close the socket again.
546        */
547       sclose(s);
548     }
549   }
550 #endif /* !CURLRES_IPV4 */
551
552   memset(&hints, 0, sizeof(hints));
553   hints.ai_family = pf;
554   hints.ai_socktype = conn->socktype;
555
556   snprintf(sbuf, sizeof(sbuf), "%d", port);
557
558   /* fire up a new resolver thread! */
559   if(init_resolve_thread(conn, hostname, port, &hints)) {
560     *waitp = 1; /* expect asynchronous response */
561     return NULL;
562   }
563
564   /* fall-back to blocking version */
565   infof(data, "init_resolve_thread() failed for %s; %s\n",
566         hostname, Curl_strerror(conn, ERRNO));
567
568   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
569   if(error) {
570     infof(data, "getaddrinfo() failed for %s:%d; %s\n",
571           hostname, port, Curl_strerror(conn, SOCKERRNO));
572     return NULL;
573   }
574   return res;
575 }
576
577 #endif /* !HAVE_GETADDRINFO */
578
579 #endif /* CURLRES_THREADED */