remove the CVSish $Id$ lines
[platform/upstream/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   /* now, see if there's a connect timeout or a regular timeout to
228      use instead of the default one */
229   if(conn->data->set.connecttimeout)
230     timeout = conn->data->set.connecttimeout;
231   else if(conn->data->set.timeout)
232     timeout = conn->data->set.timeout;
233   else
234     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
235
236   /* Wait for the name resolve query to complete. */
237   while(1) {
238     struct timeval *tvp, tv, store;
239     long timediff;
240     int itimeout;
241     int timeout_ms;
242
243     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
244
245     store.tv_sec = itimeout/1000;
246     store.tv_usec = (itimeout%1000)*1000;
247
248     tvp = ares_timeout(data->state.areschannel, &store, &tv);
249
250     /* use the timeout period ares returned to us above if less than one
251        second is left, otherwise just use 1000ms to make sure the progress
252        callback gets called frequent enough */
253     if(!tvp->tv_sec)
254       timeout_ms = tvp->tv_usec/1000;
255     else
256       timeout_ms = 1000;
257
258     waitperform(conn, timeout_ms);
259
260     if(conn->async.done)
261       break;
262
263     if(Curl_pgrsUpdate(conn)) {
264       rc = CURLE_ABORTED_BY_CALLBACK;
265       timeout = -1; /* trigger the cancel below */
266     }
267     else {
268       timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
269       timeout -= timediff?timediff:1; /* always deduct at least 1 */
270     }
271     if(timeout < 0) {
272       /* our timeout, so we cancel the ares operation */
273       ares_cancel(data->state.areschannel);
274       break;
275     }
276   }
277
278   /* Operation complete, if the lookup was successful we now have the entry
279      in the cache. */
280
281   if(entry)
282     *entry = conn->async.dns;
283
284   if(!conn->async.dns) {
285     /* a name was not resolved */
286     if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
287       if (conn->bits.httpproxy) {
288         failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname);
289         rc = CURLE_COULDNT_RESOLVE_PROXY;
290       }
291       else {
292         failf(data, "Resolving host timed out: %s", conn->host.dispname);
293         rc = CURLE_COULDNT_RESOLVE_HOST;
294       }
295     }
296     else if(conn->async.done) {
297       if (conn->bits.httpproxy) {
298         failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname,
299               ares_strerror(conn->async.status));
300         rc = CURLE_COULDNT_RESOLVE_PROXY;
301       }
302       else {
303         failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
304               ares_strerror(conn->async.status));
305         rc = CURLE_COULDNT_RESOLVE_HOST;
306       }
307     }
308     else
309       rc = CURLE_OPERATION_TIMEDOUT;
310
311     /* close the connection, since we can't return failure here without
312        cleaning up this connection properly */
313     conn->bits.close = TRUE;
314   }
315
316   return rc;
317 }
318
319 /*
320  * ares_query_completed_cb() is the callback that ares will call when
321  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
322  * when using ares, is completed either successfully or with failure.
323  */
324 static void ares_query_completed_cb(void *arg,  /* (struct connectdata *) */
325                                     int status,
326 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
327                                     int timeouts,
328 #endif
329                                     struct hostent *hostent)
330 {
331   struct connectdata *conn = (struct connectdata *)arg;
332   struct Curl_addrinfo * ai = NULL;
333
334 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
335   (void)timeouts; /* ignored */
336 #endif
337
338   if (status == CURL_ASYNC_SUCCESS) {
339     ai = Curl_he2ai(hostent, conn->async.port);
340   }
341
342   (void)Curl_addrinfo_callback(arg, status, ai);
343 }
344
345 /*
346  * Curl_getaddrinfo() - when using ares
347  *
348  * Returns name information about the given hostname and port number. If
349  * successful, the 'hostent' is returned and the forth argument will point to
350  * memory we need to free after use. That memory *MUST* be freed with
351  * Curl_freeaddrinfo(), nothing else.
352  */
353 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
354                                 const char *hostname,
355                                 int port,
356                                 int *waitp)
357 {
358   char *bufp;
359   struct SessionHandle *data = conn->data;
360   struct in_addr in;
361   int family = PF_INET;
362 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
363   struct in6_addr in6;
364 #endif /* CURLRES_IPV6 */
365
366   *waitp = 0; /* default to synchronous response */
367
368   /* First check if this is an IPv4 address string */
369   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
370     /* This is a dotted IP address 123.123.123.123-style */
371     return Curl_ip2addr(AF_INET, &in, hostname, port);
372   }
373
374 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
375   /* Otherwise, check if this is an IPv6 address string */
376   if (Curl_inet_pton (AF_INET6, hostname, &in6) > 0) {
377     /* This must be an IPv6 address literal.  */
378     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
379   }
380
381   switch(data->set.ip_version) {
382   default:
383 #if ARES_VERSION >= 0x010601
384     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
385                            c-ares versions this just falls through and defaults
386                            to PF_INET */
387     break;
388 #endif
389   case CURL_IPRESOLVE_V4:
390     family = PF_INET;
391     break;
392   case CURL_IPRESOLVE_V6:
393     family = PF_INET6;
394     break;
395   }
396 #endif /* CURLRES_IPV6 */
397
398   bufp = strdup(hostname);
399
400   if(bufp) {
401     Curl_safefree(conn->async.hostname);
402     conn->async.hostname = bufp;
403     conn->async.port = port;
404     conn->async.done = FALSE; /* not done */
405     conn->async.status = 0;   /* clear */
406     conn->async.dns = NULL;   /* clear */
407
408     /* areschannel is already setup in the Curl_open() function */
409     ares_gethostbyname(data->state.areschannel, hostname, family,
410                        (ares_host_callback)ares_query_completed_cb, conn);
411
412     *waitp = 1; /* expect asynchronous response */
413   }
414   return NULL; /* no struct yet */
415 }
416 #endif /* CURLRES_ARES */