1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <stdlib.h> /* required for free() prototypes */
46 #include <unistd.h> /* for the close() proto */
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #define in_addr_t unsigned long
71 #include "inet_pton.h"
76 #define _MPRINTF_REPLACE /* use our functions only */
77 #include <curl/mprintf.h>
79 #include "curl_memory.h"
80 /* The last #include file should be: */
83 /***********************************************************************
84 * Only for ares-enabled builds
85 **********************************************************************/
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.
95 * Returns: CURLE_OK always!
98 int Curl_resolv_getsock(struct connectdata *conn,
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);
110 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
113 timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
115 Curl_expire(conn->data,
116 (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
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"
128 * return number of sockets it worked on
131 static int waitperform(struct connectdata *conn, int timeout_ms)
133 struct SessionHandle *data = conn->data;
136 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
137 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
141 bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
143 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
146 if(ARES_GETSOCK_READABLE(bitmask, i)) {
147 pfd[i].fd = socks[i];
148 pfd[i].events |= POLLRDNORM|POLLIN;
150 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
151 pfd[i].fd = socks[i];
152 pfd[i].events |= POLLWRNORM|POLLOUT;
154 if(pfd[i].events != 0)
161 nfds = Curl_poll(pfd, num, timeout_ms);
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);
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);
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.
186 * Returns normal CURLcode errors.
188 CURLcode Curl_is_resolved(struct connectdata *conn,
189 struct Curl_dns_entry **dns)
191 struct SessionHandle *data = conn->data;
195 waitperform(conn, 0);
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;
204 *dns = conn->async.dns;
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".
214 * If 'entry' is non-NULL, make it point to the resolved dns entry
216 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
217 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
219 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
220 struct Curl_dns_entry **entry)
222 CURLcode rc=CURLE_OK;
223 struct SessionHandle *data = conn->data;
225 struct timeval now = Curl_tvnow();
227 timeout = Curl_timeleft(conn, &now, TRUE);
229 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
231 /* Wait for the name resolve query to complete. */
233 struct timeval *tvp, tv, store;
238 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
240 store.tv_sec = itimeout/1000;
241 store.tv_usec = (itimeout%1000)*1000;
243 tvp = ares_timeout(data->state.areschannel, &store, &tv);
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 */
249 timeout_ms = (int)(tvp->tv_usec/1000);
253 waitperform(conn, timeout_ms);
258 if(Curl_pgrsUpdate(conn)) {
259 rc = CURLE_ABORTED_BY_CALLBACK;
260 timeout = -1; /* trigger the cancel below */
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 */
269 /* our timeout, so we cancel the ares operation */
270 ares_cancel(data->state.areschannel);
275 /* Operation complete, if the lookup was successful we now have the entry
279 *entry = conn->async.dns;
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;
289 failf(data, "Resolving host timed out: %s", conn->host.dispname);
290 rc = CURLE_COULDNT_RESOLVE_HOST;
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;
300 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
301 ares_strerror(conn->async.status));
302 rc = CURLE_COULDNT_RESOLVE_HOST;
306 rc = CURLE_OPERATION_TIMEDOUT;
308 /* close the connection, since we can't return failure here without
309 cleaning up this connection properly */
310 conn->bits.close = TRUE;
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.
321 static void ares_query_completed_cb(void *arg, /* (struct connectdata *) */
323 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
326 struct hostent *hostent)
328 struct connectdata *conn = (struct connectdata *)arg;
329 struct Curl_addrinfo * ai = NULL;
331 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
332 (void)timeouts; /* ignored */
335 if (status == CURL_ASYNC_SUCCESS) {
336 ai = Curl_he2ai(hostent, conn->async.port);
339 (void)Curl_addrinfo_callback(arg, status, ai);
343 * Curl_getaddrinfo() - when using ares
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.
350 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
351 const char *hostname,
356 struct SessionHandle *data = conn->data;
358 int family = PF_INET;
359 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
361 #endif /* CURLRES_IPV6 */
363 *waitp = 0; /* default to synchronous response */
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);
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);
378 switch(conn->ip_version) {
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
386 case CURL_IPRESOLVE_V4:
389 case CURL_IPRESOLVE_V6:
393 #endif /* CURLRES_IPV6 */
395 bufp = strdup(hostname);
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 */
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);
409 *waitp = 1; /* expect asynchronous response */
411 return NULL; /* no struct yet */
413 #endif /* CURLRES_ARES */