1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, 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.2 2007/03/15 19:22:13 andy Exp $
22 ***************************************************************************/
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <stdlib.h> /* required for free() prototypes */
50 #include <unistd.h> /* for the close() proto */
66 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
68 #define in_addr_t unsigned long
79 #include "connect.h" /* for the Curl_sockerrno() proto */
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
85 #include "inet_ntoa_r.h"
90 /* The last #include file should be: */
93 /***********************************************************************
94 * Only for ares-enabled builds
95 **********************************************************************/
100 * Curl_resolv_fdset() is called when someone from the outside world (using
101 * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
102 * ares. The caller must make sure that this function is only called when we
103 * have a working ares channel.
105 * Returns: CURLE_OK always!
108 int Curl_resolv_getsock(struct connectdata
*conn
,
109 curl_socket_t
*socks
,
113 struct timeval maxtime
;
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 ares_timeout(conn
->data
->state
.areschannel
, &maxtime
, &timeout
);
124 Curl_expire(conn
->data
,
125 (timeout
.tv_sec
* 1000) + (timeout
.tv_usec
/1000) );
131 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
132 * request has completed. It should also make sure to time-out if the
133 * operation seems to take too long.
135 * Returns normal CURLcode errors.
137 CURLcode
Curl_is_resolved(struct connectdata
*conn
,
138 struct Curl_dns_entry
**dns
)
140 fd_set read_fds
, write_fds
;
141 struct timeval tv
={0,0};
142 struct SessionHandle
*data
= conn
->data
;
148 nfds
= ares_fds(data
->state
.areschannel
, &read_fds
, &write_fds
);
150 (void)select(nfds
, &read_fds
, &write_fds
, NULL
,
151 (struct timeval
*)&tv
);
153 /* Call ares_process() unconditonally here, even if we simply timed out
154 above, as otherwise the ares name resolve won't timeout! */
155 ares_process(data
->state
.areschannel
, &read_fds
, &write_fds
);
159 if(conn
->async
.done
) {
160 /* we're done, kill the ares handle */
161 if(!conn
->async
.dns
) {
162 failf(data
, "Could not resolve host: %s (%s)", conn
->host
.dispname
,
163 ares_strerror(conn
->async
.status
));
164 return CURLE_COULDNT_RESOLVE_HOST
;
166 *dns
= conn
->async
.dns
;
173 * Curl_wait_for_resolv() waits for a resolve to finish. This function should
174 * be avoided since using this risk getting the multi interface to "hang".
176 * If 'entry' is non-NULL, make it point to the resolved dns entry
178 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
179 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
181 CURLcode
Curl_wait_for_resolv(struct connectdata
*conn
,
182 struct Curl_dns_entry
**entry
)
184 CURLcode rc
=CURLE_OK
;
185 struct SessionHandle
*data
= conn
->data
;
186 long timeout
= CURL_TIMEOUT_RESOLVE
; /* default name resolve timeout */
188 /* now, see if there's a connect timeout or a regular timeout to
189 use instead of the default one */
190 if(conn
->data
->set
.connecttimeout
)
191 timeout
= conn
->data
->set
.connecttimeout
;
192 else if(conn
->data
->set
.timeout
)
193 timeout
= conn
->data
->set
.timeout
;
195 /* We convert the number of seconds into number of milliseconds here: */
196 if(timeout
< 2147483)
197 /* maximum amount of seconds that can be multiplied with 1000 and
198 still fit within 31 bits */
201 timeout
= 0x7fffffff; /* ridiculous amount of time anyway */
203 /* Wait for the name resolve query to complete. */
206 fd_set read_fds
, write_fds
;
207 struct timeval
*tvp
, tv
, store
;
209 struct timeval now
= Curl_tvnow();
212 store
.tv_sec
= (int)timeout
/1000;
213 store
.tv_usec
= (timeout
%1000)*1000;
217 nfds
= ares_fds(data
->state
.areschannel
, &read_fds
, &write_fds
);
219 /* no file descriptors means we're done waiting */
221 tvp
= ares_timeout(data
->state
.areschannel
, &store
, &tv
);
222 count
= select(nfds
, &read_fds
, &write_fds
, NULL
, tvp
);
223 if (count
< 0 && Curl_sockerrno() != EINVAL
)
226 ares_process(data
->state
.areschannel
, &read_fds
, &write_fds
);
228 timediff
= Curl_tvdiff(Curl_tvnow(), now
); /* spent time */
229 timeout
-= timediff
?timediff
:1; /* always deduct at least 1 */
231 /* our timeout, so we cancel the ares operation */
232 ares_cancel(data
->state
.areschannel
);
237 /* Operation complete, if the lookup was successful we now have the entry
241 *entry
= conn
->async
.dns
;
243 if(!conn
->async
.dns
) {
244 /* a name was not resolved */
245 if((timeout
< 0) || (conn
->async
.status
== ARES_ETIMEOUT
)) {
246 failf(data
, "Resolving host timed out: %s", conn
->host
.dispname
);
247 rc
= CURLE_OPERATION_TIMEDOUT
;
249 else if(conn
->async
.done
) {
250 failf(data
, "Could not resolve host: %s (%s)", conn
->host
.dispname
,
251 ares_strerror(conn
->async
.status
));
252 rc
= CURLE_COULDNT_RESOLVE_HOST
;
255 rc
= CURLE_OPERATION_TIMEDOUT
;
257 /* close the connection, since we can't return failure here without
258 cleaning up this connection properly */
259 conn
->bits
.close
= TRUE
;
266 * Curl_getaddrinfo() - when using ares
268 * Returns name information about the given hostname and port number. If
269 * successful, the 'hostent' is returned and the forth argument will point to
270 * memory we need to free after use. That memory *MUST* be freed with
271 * Curl_freeaddrinfo(), nothing else.
273 Curl_addrinfo
*Curl_getaddrinfo(struct connectdata
*conn
,
274 const char *hostname
,
279 struct SessionHandle
*data
= conn
->data
;
280 in_addr_t in
= inet_addr(hostname
);
284 if (in
!= CURL_INADDR_NONE
) {
285 /* This is a dotted IP address 123.123.123.123-style */
286 return Curl_ip2addr(in
, hostname
, port
);
289 bufp
= strdup(hostname
);
292 Curl_safefree(conn
->async
.hostname
);
293 conn
->async
.hostname
= bufp
;
294 conn
->async
.port
= port
;
295 conn
->async
.done
= FALSE
; /* not done */
296 conn
->async
.status
= 0; /* clear */
297 conn
->async
.dns
= NULL
; /* clear */
299 /* areschannel is already setup in the Curl_open() function */
300 ares_gethostbyname(data
->state
.areschannel
, hostname
, PF_INET
,
301 (ares_host_callback
)Curl_addrinfo4_callback
, conn
);
303 *waitp
= TRUE
; /* please wait for the response */
305 return NULL
; /* no struct yet */
307 #endif /* CURLRES_ARES */