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