Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl-7.19.0 / lib / hostares.c
blob12c6d2c9573109fd421cce96d6f8ecd95b8c50eb
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
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 ***************************************************************************/
24 #include "setup.h"
26 #include <string.h>
28 #ifdef NEED_MALLOC_H
29 #include <malloc.h>
30 #endif
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef HAVE_STDLIB_H
44 #include <stdlib.h> /* required for free() prototypes */
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h> /* for the close() proto */
48 #endif
49 #ifdef VMS
50 #include <in.h>
51 #include <inet.h>
52 #include <stdlib.h>
53 #endif
55 #ifdef HAVE_SETJMP_H
56 #include <setjmp.h>
57 #endif
59 #ifdef HAVE_PROCESS_H
60 #include <process.h>
61 #endif
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
64 #undef in_addr_t
65 #define in_addr_t unsigned long
66 #endif
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "hostip.h"
71 #include "hash.h"
72 #include "share.h"
73 #include "strerror.h"
74 #include "url.h"
75 #include "multiif.h"
76 #include "inet_pton.h"
77 #include "connect.h"
78 #include "select.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"
85 #endif
87 #include "memory.h"
89 /* The last #include file should be: */
90 #include "memdebug.h"
92 /***********************************************************************
93 * Only for ares-enabled builds
94 **********************************************************************/
96 #ifdef CURLRES_ARES
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,
109 int numsocks)
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;
120 maxtime.tv_usec = 0;
122 timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
124 Curl_expire(conn->data,
125 (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
127 return max;
131 * ares_waitperform()
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;
143 int nfds;
144 int bitmask;
145 int socks[ARES_GETSOCK_MAXNUM];
146 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
147 int i;
148 int num = 0;
150 bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
152 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
153 pfd[i].events = 0;
154 pfd[i].revents = 0;
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)
164 num++;
165 else
166 break;
169 if(num)
170 nfds = Curl_poll(pfd, num, timeout_ms);
171 else
172 nfds = 0;
174 if(!nfds)
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);
178 else {
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);
187 return nfds;
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;
202 *dns = NULL;
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;
216 return CURLE_OK;
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;
233 long timeout;
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;
242 else
243 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
245 /* Wait for the name resolve query to complete. */
246 while(1) {
247 struct timeval *tvp, tv, store;
248 long timediff;
249 int itimeout;
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));
261 if(conn->async.done)
262 break;
264 timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
265 timeout -= timediff?timediff:1; /* always deduct at least 1 */
266 if(timeout < 0) {
267 /* our timeout, so we cancel the ares operation */
268 ares_cancel(data->state.areschannel);
269 break;
273 /* Operation complete, if the lookup was successful we now have the entry
274 in the cache. */
276 if(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;
290 else
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;
298 return rc;
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
306 * address/host.
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)
314 Curl_addrinfo *ai;
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
321 #endif
323 struct hostent *h;
324 struct in6_addr *addrentry;
325 struct namebuf6 {
326 struct hostent hostentry;
327 char *h_addr_list[2];
328 struct in6_addr addrentry;
329 char hostname[1];
331 struct namebuf6 *buf = malloc(sizeof (struct namebuf6) + strlen(hostname));
333 if(!buf)
334 return NULL;
336 h = &buf->hostentry;
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];
343 h->h_aliases = NULL;
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
353 #endif
355 ai = Curl_he2ai(h, port);
357 free(buf);
359 return ai;
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,
375 int port,
376 int *waitp)
378 char *bufp;
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 */
383 struct in6_addr in6;
384 #endif /* CURLRES_IPV6 */
385 *waitp = FALSE;
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:
400 family = PF_INET;
401 break;
402 default: /* by default we try ipv6, as PF_UNSPEC isn't supported by (c-)ares */
403 case CURL_IPRESOLVE_V6:
404 family = PF_INET6;
405 break;
407 #endif /* CURLRES_IPV6 */
409 bufp = strdup(hostname);
411 if(bufp) {
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 */