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 /* 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;
234 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
236 /* Wait for the name resolve query to complete. */
238 struct timeval *tvp, tv, store;
243 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
245 store.tv_sec = itimeout/1000;
246 store.tv_usec = (itimeout%1000)*1000;
248 tvp = ares_timeout(data->state.areschannel, &store, &tv);
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 */
254 timeout_ms = tvp->tv_usec/1000;
258 waitperform(conn, timeout_ms);
263 if(Curl_pgrsUpdate(conn)) {
264 rc = CURLE_ABORTED_BY_CALLBACK;
265 timeout = -1; /* trigger the cancel below */
268 timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
269 timeout -= timediff?timediff:1; /* always deduct at least 1 */
272 /* our timeout, so we cancel the ares operation */
273 ares_cancel(data->state.areschannel);
278 /* Operation complete, if the lookup was successful we now have the entry
282 *entry = conn->async.dns;
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;
292 failf(data, "Resolving host timed out: %s", conn->host.dispname);
293 rc = CURLE_COULDNT_RESOLVE_HOST;
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;
303 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
304 ares_strerror(conn->async.status));
305 rc = CURLE_COULDNT_RESOLVE_HOST;
309 rc = CURLE_OPERATION_TIMEDOUT;
311 /* close the connection, since we can't return failure here without
312 cleaning up this connection properly */
313 conn->bits.close = TRUE;
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.
324 static void ares_query_completed_cb(void *arg, /* (struct connectdata *) */
326 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
329 struct hostent *hostent)
331 struct connectdata *conn = (struct connectdata *)arg;
332 struct Curl_addrinfo * ai = NULL;
334 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
335 (void)timeouts; /* ignored */
338 if (status == CURL_ASYNC_SUCCESS) {
339 ai = Curl_he2ai(hostent, conn->async.port);
342 (void)Curl_addrinfo_callback(arg, status, ai);
346 * Curl_getaddrinfo() - when using ares
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.
353 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
354 const char *hostname,
359 struct SessionHandle *data = conn->data;
361 int family = PF_INET;
362 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
364 #endif /* CURLRES_IPV6 */
366 *waitp = 0; /* default to synchronous response */
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);
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);
381 switch(data->set.ip_version) {
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
389 case CURL_IPRESOLVE_V4:
392 case CURL_IPRESOLVE_V6:
396 #endif /* CURLRES_IPV6 */
398 bufp = strdup(hostname);
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 */
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);
412 *waitp = 1; /* expect asynchronous response */
414 return NULL; /* no struct yet */
416 #endif /* CURLRES_ARES */