Update tag value for tizen 2.0 build
[external/curl.git] / lib / hostares.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
27 #ifdef HAVE_LIMITS_H
28 #include <limits.h>
29 #endif
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
35 #endif
36 #ifdef HAVE_NETDB_H
37 #include <netdb.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #ifdef HAVE_STDLIB_H
43 #include <stdlib.h>     /* required for free() prototypes */
44 #endif
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>     /* for the close() proto */
47 #endif
48 #ifdef __VMS
49 #include <in.h>
50 #include <inet.h>
51 #include <stdlib.h>
52 #endif
53
54 #ifdef HAVE_PROCESS_H
55 #include <process.h>
56 #endif
57
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #undef in_addr_t
60 #define in_addr_t unsigned long
61 #endif
62
63 #include "urldata.h"
64 #include "sendf.h"
65 #include "hostip.h"
66 #include "hash.h"
67 #include "share.h"
68 #include "strerror.h"
69 #include "url.h"
70 #include "multiif.h"
71 #include "inet_pton.h"
72 #include "connect.h"
73 #include "select.h"
74 #include "progress.h"
75
76 #define _MPRINTF_REPLACE /* use our functions only */
77 #include <curl/mprintf.h>
78
79 #include "curl_memory.h"
80 /* The last #include file should be: */
81 #include "memdebug.h"
82
83 /***********************************************************************
84  * Only for ares-enabled builds
85  **********************************************************************/
86
87 #ifdef CURLRES_ARES
88
89 /*
90  * Curl_resolv_fdset() is called when someone from the outside world (using
91  * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
92  * ares. The caller must make sure that this function is only called when we
93  * have a working ares channel.
94  *
95  * Returns: CURLE_OK always!
96  */
97
98 int Curl_resolv_getsock(struct connectdata *conn,
99                         curl_socket_t *socks,
100                         int numsocks)
101
102 {
103   struct timeval maxtime;
104   struct timeval timebuf;
105   struct timeval *timeout;
106   int max = ares_getsock(conn->data->state.areschannel,
107                          (ares_socket_t *)socks, numsocks);
108
109
110   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
111   maxtime.tv_usec = 0;
112
113   timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
114
115   Curl_expire(conn->data,
116               (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
117
118   return max;
119 }
120
121 /*
122  * waitperform()
123  *
124  * 1) Ask ares what sockets it currently plays with, then
125  * 2) wait for the timeout period to check for action on ares' sockets.
126  * 3) tell ares to act on all the sockets marked as "with action"
127  *
128  * return number of sockets it worked on
129  */
130
131 static int waitperform(struct connectdata *conn, int timeout_ms)
132 {
133   struct SessionHandle *data = conn->data;
134   int nfds;
135   int bitmask;
136   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
137   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
138   int i;
139   int num = 0;
140
141   bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
142
143   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
144     pfd[i].events = 0;
145     pfd[i].revents = 0;
146     if(ARES_GETSOCK_READABLE(bitmask, i)) {
147       pfd[i].fd = socks[i];
148       pfd[i].events |= POLLRDNORM|POLLIN;
149     }
150     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
151       pfd[i].fd = socks[i];
152       pfd[i].events |= POLLWRNORM|POLLOUT;
153     }
154     if(pfd[i].events != 0)
155       num++;
156     else
157       break;
158   }
159
160   if(num)
161     nfds = Curl_poll(pfd, num, timeout_ms);
162   else
163     nfds = 0;
164
165   if(!nfds)
166     /* Call ares_process() unconditonally here, even if we simply timed out
167        above, as otherwise the ares name resolve won't timeout! */
168     ares_process_fd(data->state.areschannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
169   else {
170     /* move through the descriptors and ask for processing on them */
171     for(i=0; i < num; i++)
172       ares_process_fd(data->state.areschannel,
173                       pfd[i].revents & (POLLRDNORM|POLLIN)?
174                       pfd[i].fd:ARES_SOCKET_BAD,
175                       pfd[i].revents & (POLLWRNORM|POLLOUT)?
176                       pfd[i].fd:ARES_SOCKET_BAD);
177   }
178   return nfds;
179 }
180
181 /*
182  * Curl_is_resolved() is called repeatedly to check if a previous name resolve
183  * request has completed. It should also make sure to time-out if the
184  * operation seems to take too long.
185  *
186  * Returns normal CURLcode errors.
187  */
188 CURLcode Curl_is_resolved(struct connectdata *conn,
189                           struct Curl_dns_entry **dns)
190 {
191   struct SessionHandle *data = conn->data;
192
193   *dns = NULL;
194
195   waitperform(conn, 0);
196
197   if(conn->async.done) {
198     /* we're done, kill the ares handle */
199     if(!conn->async.dns) {
200       failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
201             ares_strerror(conn->async.status));
202       return CURLE_COULDNT_RESOLVE_HOST;
203     }
204     *dns = conn->async.dns;
205   }
206
207   return CURLE_OK;
208 }
209
210 /*
211  * Curl_wait_for_resolv() waits for a resolve to finish. This function should
212  * be avoided since using this risk getting the multi interface to "hang".
213  *
214  * If 'entry' is non-NULL, make it point to the resolved dns entry
215  *
216  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
217  * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
218  */
219 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
220                               struct Curl_dns_entry **entry)
221 {
222   CURLcode rc=CURLE_OK;
223   struct SessionHandle *data = conn->data;
224   long timeout;
225   struct timeval now = Curl_tvnow();
226
227   timeout = Curl_timeleft(conn, &now, TRUE);
228   if(!timeout)
229     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
230
231   /* Wait for the name resolve query to complete. */
232   for(;;) {
233     struct timeval *tvp, tv, store;
234     long timediff;
235     int itimeout;
236     int timeout_ms;
237
238     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
239
240     store.tv_sec = itimeout/1000;
241     store.tv_usec = (itimeout%1000)*1000;
242
243     tvp = ares_timeout(data->state.areschannel, &store, &tv);
244
245     /* use the timeout period ares returned to us above if less than one
246        second is left, otherwise just use 1000ms to make sure the progress
247        callback gets called frequent enough */
248     if(!tvp->tv_sec)
249       timeout_ms = (int)(tvp->tv_usec/1000);
250     else
251       timeout_ms = 1000;
252
253     waitperform(conn, timeout_ms);
254
255     if(conn->async.done)
256       break;
257
258     if(Curl_pgrsUpdate(conn)) {
259       rc = CURLE_ABORTED_BY_CALLBACK;
260       timeout = -1; /* trigger the cancel below */
261     }
262     else {
263       struct timeval now2 = Curl_tvnow();
264       timediff = Curl_tvdiff(now2, now); /* spent time */
265       timeout -= timediff?timediff:1; /* always deduct at least 1 */
266       now = now2; /* for next loop */
267     }
268     if(timeout < 0) {
269       /* our timeout, so we cancel the ares operation */
270       ares_cancel(data->state.areschannel);
271       break;
272     }
273   }
274
275   /* Operation complete, if the lookup was successful we now have the entry
276      in the cache. */
277
278   if(entry)
279     *entry = conn->async.dns;
280
281   if(!conn->async.dns) {
282     /* a name was not resolved */
283     if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
284       if (conn->bits.httpproxy) {
285         failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
286         rc = CURLE_COULDNT_RESOLVE_PROXY;
287       }
288       else {
289         failf(data, "Resolving host timed out: %s", conn->host.dispname);
290         rc = CURLE_COULDNT_RESOLVE_HOST;
291       }
292     }
293     else if(conn->async.done) {
294       if (conn->bits.httpproxy) {
295         failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
296               ares_strerror(conn->async.status));
297         rc = CURLE_COULDNT_RESOLVE_PROXY;
298       }
299       else {
300         failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
301               ares_strerror(conn->async.status));
302         rc = CURLE_COULDNT_RESOLVE_HOST;
303       }
304     }
305     else
306       rc = CURLE_OPERATION_TIMEDOUT;
307
308     /* close the connection, since we can't return failure here without
309        cleaning up this connection properly */
310     conn->bits.close = TRUE;
311   }
312
313   return rc;
314 }
315
316 /*
317  * ares_query_completed_cb() is the callback that ares will call when
318  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
319  * when using ares, is completed either successfully or with failure.
320  */
321 static void ares_query_completed_cb(void *arg,  /* (struct connectdata *) */
322                                     int status,
323 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
324                                     int timeouts,
325 #endif
326                                     struct hostent *hostent)
327 {
328   struct connectdata *conn = (struct connectdata *)arg;
329   struct Curl_addrinfo * ai = NULL;
330
331 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
332   (void)timeouts; /* ignored */
333 #endif
334
335   if (status == CURL_ASYNC_SUCCESS) {
336     ai = Curl_he2ai(hostent, conn->async.port);
337   }
338
339   (void)Curl_addrinfo_callback(arg, status, ai);
340 }
341
342 /*
343  * Curl_getaddrinfo() - when using ares
344  *
345  * Returns name information about the given hostname and port number. If
346  * successful, the 'hostent' is returned and the forth argument will point to
347  * memory we need to free after use. That memory *MUST* be freed with
348  * Curl_freeaddrinfo(), nothing else.
349  */
350 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
351                                 const char *hostname,
352                                 int port,
353                                 int *waitp)
354 {
355   char *bufp;
356   struct SessionHandle *data = conn->data;
357   struct in_addr in;
358   int family = PF_INET;
359 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
360   struct in6_addr in6;
361 #endif /* CURLRES_IPV6 */
362
363   *waitp = 0; /* default to synchronous response */
364
365   /* First check if this is an IPv4 address string */
366   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
367     /* This is a dotted IP address 123.123.123.123-style */
368     return Curl_ip2addr(AF_INET, &in, hostname, port);
369   }
370
371 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
372   /* Otherwise, check if this is an IPv6 address string */
373   if (Curl_inet_pton (AF_INET6, hostname, &in6) > 0) {
374     /* This must be an IPv6 address literal.  */
375     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
376   }
377
378   switch(conn->ip_version) {
379   default:
380 #if ARES_VERSION >= 0x010601
381     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
382                            c-ares versions this just falls through and defaults
383                            to PF_INET */
384     break;
385 #endif
386   case CURL_IPRESOLVE_V4:
387     family = PF_INET;
388     break;
389   case CURL_IPRESOLVE_V6:
390     family = PF_INET6;
391     break;
392   }
393 #endif /* CURLRES_IPV6 */
394
395   bufp = strdup(hostname);
396
397   if(bufp) {
398     Curl_safefree(conn->async.hostname);
399     conn->async.hostname = bufp;
400     conn->async.port = port;
401     conn->async.done = FALSE; /* not done */
402     conn->async.status = 0;   /* clear */
403     conn->async.dns = NULL;   /* clear */
404
405     /* areschannel is already setup in the Curl_open() function */
406     ares_gethostbyname(data->state.areschannel, hostname, family,
407                        (ares_host_callback)ares_query_completed_cb, conn);
408
409     *waitp = 1; /* expect asynchronous response */
410   }
411   return NULL; /* no struct yet */
412 }
413 #endif /* CURLRES_ARES */