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.
21 * $Id: hostares.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
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 */