1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, 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.
22 ***************************************************************************/
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <stdlib.h> /* required for free() prototypes */
47 #include <unistd.h> /* for the close() proto */
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
65 #define in_addr_t unsigned long
76 #include "inet_pton.h"
80 #define _MPRINTF_REPLACE /* use our functions only */
81 #include <curl/mprintf.h>
83 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
84 #include "inet_ntoa_r.h"
89 /* The last #include file should be: */
92 /***********************************************************************
93 * Only for ares-enabled builds
94 **********************************************************************/
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.
104 * Returns: CURLE_OK always!
107 int Curl_resolv_getsock(struct connectdata *conn,
108 curl_socket_t *socks,
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);
119 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
122 timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
124 Curl_expire(conn->data,
125 (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
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"
137 * return number of sockets it worked on
140 static int ares_waitperform(struct connectdata *conn, int timeout_ms)
142 struct SessionHandle *data = conn->data;
145 int socks[ARES_GETSOCK_MAXNUM];
146 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
150 bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
152 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
155 if(ARES_GETSOCK_READABLE(bitmask, i)) {
156 pfd[i].fd = socks[i];
157 pfd[i].events |= POLLRDNORM|POLLIN;
159 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
160 pfd[i].fd = socks[i];
161 pfd[i].events |= POLLWRNORM|POLLOUT;
163 if(pfd[i].events != 0)
170 nfds = Curl_poll(pfd, num, timeout_ms);
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);
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);
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.
195 * Returns normal CURLcode errors.
197 CURLcode Curl_is_resolved(struct connectdata *conn,
198 struct Curl_dns_entry **dns)
200 struct SessionHandle *data = conn->data;
204 ares_waitperform(conn, 0);
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;
213 *dns = conn->async.dns;
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".
223 * If 'entry' is non-NULL, make it point to the resolved dns entry
225 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
226 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
228 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
229 struct Curl_dns_entry **entry)
231 CURLcode rc=CURLE_OK;
232 struct SessionHandle *data = conn->data;
234 struct timeval now = Curl_tvnow();
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;
243 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
245 /* Wait for the name resolve query to complete. */
247 struct timeval *tvp, tv, store;
251 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
253 store.tv_sec = itimeout/1000;
254 store.tv_usec = (itimeout%1000)*1000;
256 tvp = ares_timeout(data->state.areschannel, &store, &tv);
258 /* use the timeout period ares returned to us above */
259 ares_waitperform(conn, (int)(tvp->tv_sec * 1000 + tvp->tv_usec/1000));
264 timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
265 timeout -= timediff?timediff:1; /* always deduct at least 1 */
267 /* our timeout, so we cancel the ares operation */
268 ares_cancel(data->state.areschannel);
273 /* Operation complete, if the lookup was successful we now have the entry
277 *entry = conn->async.dns;
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;
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;
291 rc = CURLE_OPERATION_TIMEDOUT;
293 /* close the connection, since we can't return failure here without
294 cleaning up this connection properly */
295 conn->bits.close = TRUE;
301 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
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
308 * The input parameters ARE NOT checked for validity but they are expected
309 * to have been checked already when this is called.
311 Curl_addrinfo *Curl_ip2addr6(struct in6_addr *in,
312 const char *hostname, int port)
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
324 struct in6_addr *addrentry;
326 struct hostent hostentry;
327 char *h_addr_list[2];
328 struct in6_addr addrentry;
331 struct namebuf6 *buf = malloc(sizeof (struct namebuf6) + strlen(hostname));
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];
344 h->h_addrtype = AF_INET6;
346 /* Now store the dotted version of the address */
347 strcpy (h->h_name, hostname);
349 #if defined(VMS) && defined(__INITIAL_POINTER_SIZE) && \
350 (__INITIAL_POINTER_SIZE == 64)
351 #pragma pointer_size restore
352 #pragma message enable PTRMISMATCH
355 ai = Curl_he2ai(h, port);
361 #endif /* CURLRES_IPV6 */
366 * Curl_getaddrinfo() - when using ares
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.
373 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
374 const char *hostname,
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 */
384 #endif /* CURLRES_IPV6 */
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);
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);
398 switch(data->set.ip_version) {
399 case CURL_IPRESOLVE_V4:
402 default: /* by default we try ipv6, as PF_UNSPEC isn't supported by (c-)ares */
403 case CURL_IPRESOLVE_V6:
407 #endif /* CURLRES_IPV6 */
409 bufp = strdup(hostname);
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 */
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);
423 *waitp = TRUE; /* please wait for the response */
425 return NULL; /* no struct yet */
427 #endif /* CURLRES_ARES */