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