1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2009, 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: hostares.c,v 1.46 2009-01-31 20:25:56 bagder Exp $
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_PROCESS_H
56 #include <process.h>
57 #endif
58 
59 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #undef in_addr_t
61 #define in_addr_t unsigned long
62 #endif
63 
64 #include "urldata.h"
65 #include "sendf.h"
66 #include "hostip.h"
67 #include "hash.h"
68 #include "share.h"
69 #include "strerror.h"
70 #include "url.h"
71 #include "multiif.h"
72 #include "inet_pton.h"
73 #include "connect.h"
74 #include "select.h"
75 
76 #define _MPRINTF_REPLACE /* use our functions only */
77 #include <curl/mprintf.h>
78 
79 #include "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 
Curl_resolv_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)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                          (int *)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  * ares_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 
ares_waitperform(struct connectdata * conn,int timeout_ms)131 static int ares_waitperform(struct connectdata *conn, int timeout_ms)
132 {
133   struct SessionHandle *data = conn->data;
134   int nfds;
135   int bitmask;
136   int 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  */
Curl_is_resolved(struct connectdata * conn,struct Curl_dns_entry ** dns)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   ares_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  */
Curl_wait_for_resolv(struct connectdata * conn,struct Curl_dns_entry ** entry)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 
242     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
243 
244     store.tv_sec = itimeout/1000;
245     store.tv_usec = (itimeout%1000)*1000;
246 
247     tvp = ares_timeout(data->state.areschannel, &store, &tv);
248 
249     /* use the timeout period ares returned to us above */
250     ares_waitperform(conn, (int)(tvp->tv_sec * 1000 + tvp->tv_usec/1000));
251 
252     if(conn->async.done)
253       break;
254 
255     timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
256     timeout -= timediff?timediff:1; /* always deduct at least 1 */
257     if(timeout < 0) {
258       /* our timeout, so we cancel the ares operation */
259       ares_cancel(data->state.areschannel);
260       break;
261     }
262   }
263 
264   /* Operation complete, if the lookup was successful we now have the entry
265      in the cache. */
266 
267   if(entry)
268     *entry = conn->async.dns;
269 
270   if(!conn->async.dns) {
271     /* a name was not resolved */
272     if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
273       failf(data, "Resolving host timed out: %s", conn->host.dispname);
274       rc = CURLE_COULDNT_RESOLVE_HOST;
275     }
276     else if(conn->async.done) {
277       failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
278             ares_strerror(conn->async.status));
279       rc = CURLE_COULDNT_RESOLVE_HOST;
280     }
281     else
282       rc = CURLE_OPERATION_TIMEDOUT;
283 
284     /* close the connection, since we can't return failure here without
285        cleaning up this connection properly */
286     conn->bits.close = TRUE;
287   }
288 
289   return rc;
290 }
291 
292 
293 /*
294  * Curl_getaddrinfo() - when using ares
295  *
296  * Returns name information about the given hostname and port number. If
297  * successful, the 'hostent' is returned and the forth argument will point to
298  * memory we need to free after use. That memory *MUST* be freed with
299  * Curl_freeaddrinfo(), nothing else.
300  */
Curl_getaddrinfo(struct connectdata * conn,const char * hostname,int port,int * waitp)301 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
302                                 const char *hostname,
303                                 int port,
304                                 int *waitp)
305 {
306   char *bufp;
307   struct SessionHandle *data = conn->data;
308   struct in_addr in;
309   int family = PF_INET;
310 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
311   struct in6_addr in6;
312 #endif /* CURLRES_IPV6 */
313   *waitp = FALSE;
314 
315   /* First check if this is an IPv4 address string */
316   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
317     /* This is a dotted IP address 123.123.123.123-style */
318     return Curl_ip2addr(AF_INET, &in, hostname, port);
319   }
320 
321 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
322   /* Otherwise, check if this is an IPv6 address string */
323   if (Curl_inet_pton (AF_INET6, hostname, &in6) > 0) {
324     /* This must be an IPv6 address literal.  */
325     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
326   }
327 
328   switch(data->set.ip_version) {
329   default:
330 #if ARES_VERSION >= 0x010601
331     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
332                            c-ares versions this just falls through and defaults
333                            to PF_INET */
334     break;
335 #endif
336   case CURL_IPRESOLVE_V4:
337     family = PF_INET;
338     break;
339   case CURL_IPRESOLVE_V6:
340     family = PF_INET6;
341     break;
342   }
343 #endif /* CURLRES_IPV6 */
344 
345   bufp = strdup(hostname);
346 
347   if(bufp) {
348     Curl_safefree(conn->async.hostname);
349     conn->async.hostname = bufp;
350     conn->async.port = port;
351     conn->async.done = FALSE; /* not done */
352     conn->async.status = 0;   /* clear */
353     conn->async.dns = NULL;   /* clear */
354 
355     /* areschannel is already setup in the Curl_open() function */
356     ares_gethostbyname(data->state.areschannel, hostname, family,
357                        (ares_host_callback)Curl_addrinfo4_callback, conn);
358 
359     *waitp = TRUE; /* please wait for the response */
360   }
361   return NULL; /* no struct yet */
362 }
363 #endif /* CURLRES_ARES */
364