Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl-7.19.0 / lib / hostip.c
blob8fcd9ea154ed8831c920561c0a1602541d49faf0
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: hostip.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 #include "urldata.h"
64 #include "sendf.h"
65 #include "hostip.h"
66 #include "hash.h"
67 #include "share.h"
68 #include "strerror.h"
69 #include "url.h"
70 #include "inet_ntop.h"
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
75 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
76 #include "inet_ntoa_r.h"
77 #endif
79 #include "memory.h"
80 /* The last #include file should be: */
81 #include "memdebug.h"
84 * hostip.c explained
85 * ==================
87 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
88 * source file are these:
90 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
91 * that. The host may not be able to resolve IPv6, but we don't really have to
92 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
93 * defined.
95 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
96 * asynchronous name resolves. This can be Windows or *nix.
98 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
99 * Windows, and then the name resolve will be done in a new thread, and the
100 * supported API will be the same as for ares-builds.
102 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
103 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
104 * defined.
106 * The host*.c sources files are split up like this:
108 * hostip.c - method-independent resolver functions and utility functions
109 * hostasyn.c - functions for asynchronous name resolves
110 * hostsyn.c - functions for synchronous name resolves
111 * hostares.c - functions for ares-using name resolves
112 * hostthre.c - functions for threaded name resolves
113 * hostip4.c - ipv4-specific functions
114 * hostip6.c - ipv6-specific functions
116 * The hostip.h is the united header file for all this. It defines the
117 * CURLRES_* defines based on the config*.h and setup.h defines.
120 /* These two symbols are for the global DNS cache */
121 static struct curl_hash hostname_cache;
122 static int host_cache_initialized;
124 static void freednsentry(void *freethis);
127 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
128 * Global DNS cache is general badness. Do not use. This will be removed in
129 * a future version. Use the share interface instead!
131 * Returns a struct curl_hash pointer on success, NULL on failure.
133 struct curl_hash *Curl_global_host_cache_init(void)
135 int rc = 0;
136 if(!host_cache_initialized) {
137 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
138 Curl_str_key_compare, freednsentry);
139 if(!rc)
140 host_cache_initialized = 1;
142 return rc?NULL:&hostname_cache;
146 * Destroy and cleanup the global DNS cache
148 void Curl_global_host_cache_dtor(void)
150 if(host_cache_initialized) {
151 Curl_hash_clean(&hostname_cache);
152 host_cache_initialized = 0;
157 * Return # of adresses in a Curl_addrinfo struct
159 int Curl_num_addresses(const Curl_addrinfo *addr)
161 int i;
162 for (i = 0; addr; addr = addr->ai_next, i++)
163 ; /* empty loop */
164 return i;
168 * Curl_printable_address() returns a printable version of the 1st address
169 * given in the 'ip' argument. The result will be stored in the buf that is
170 * bufsize bytes big.
172 * If the conversion fails, it returns NULL.
174 const char *Curl_printable_address(const Curl_addrinfo *ip,
175 char *buf, size_t bufsize)
177 const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
178 int af = ip->ai_family;
179 #ifdef CURLRES_IPV6
180 const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
181 #else
182 const void *ip6 = NULL;
183 #endif
185 return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
189 * Return a hostcache id string for the providing host + port, to be used by
190 * the DNS caching.
192 static char *
193 create_hostcache_id(const char *server, int port)
195 /* create and return the new allocated entry */
196 return aprintf("%s:%d", server, port);
199 struct hostcache_prune_data {
200 long cache_timeout;
201 time_t now;
205 * This function is set as a callback to be called for every entry in the DNS
206 * cache when we want to prune old unused entries.
208 * Returning non-zero means remove the entry, return 0 to keep it in the
209 * cache.
211 static int
212 hostcache_timestamp_remove(void *datap, void *hc)
214 struct hostcache_prune_data *data =
215 (struct hostcache_prune_data *) datap;
216 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
218 if((data->now - c->timestamp < data->cache_timeout) ||
219 c->inuse) {
220 /* please don't remove */
221 return 0;
224 /* fine, remove */
225 return 1;
229 * Prune the DNS cache. This assumes that a lock has already been taken.
231 static void
232 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
234 struct hostcache_prune_data user;
236 user.cache_timeout = cache_timeout;
237 user.now = now;
239 Curl_hash_clean_with_criterium(hostcache,
240 (void *) &user,
241 hostcache_timestamp_remove);
245 * Library-wide function for pruning the DNS cache. This function takes and
246 * returns the appropriate locks.
248 void Curl_hostcache_prune(struct SessionHandle *data)
250 time_t now;
252 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
253 /* cache forever means never prune, and NULL hostcache means
254 we can't do it */
255 return;
257 if(data->share)
258 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
260 time(&now);
262 /* Remove outdated and unused entries from the hostcache */
263 hostcache_prune(data->dns.hostcache,
264 data->set.dns_cache_timeout,
265 now);
267 if(data->share)
268 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
272 * Check if the entry should be pruned. Assumes a locked cache.
274 static int
275 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
277 struct hostcache_prune_data user;
279 if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
280 /* cache forever means never prune, and NULL hostcache means
281 we can't do it */
282 return 0;
284 time(&user.now);
285 user.cache_timeout = data->set.dns_cache_timeout;
287 if( !hostcache_timestamp_remove(&user,dns) )
288 return 0;
290 Curl_hash_clean_with_criterium(data->dns.hostcache,
291 (void *) &user,
292 hostcache_timestamp_remove);
294 return 1;
298 #ifdef HAVE_SIGSETJMP
299 /* Beware this is a global and unique instance. This is used to store the
300 return address that we can jump back to from inside a signal handler. This
301 is not thread-safe stuff. */
302 sigjmp_buf curl_jmpenv;
303 #endif
307 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
309 * When calling Curl_resolv() has resulted in a response with a returned
310 * address, we call this function to store the information in the dns
311 * cache etc
313 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
315 struct Curl_dns_entry *
316 Curl_cache_addr(struct SessionHandle *data,
317 Curl_addrinfo *addr,
318 const char *hostname,
319 int port)
321 char *entry_id;
322 size_t entry_len;
323 struct Curl_dns_entry *dns;
324 struct Curl_dns_entry *dns2;
325 time_t now;
327 /* Create an entry id, based upon the hostname and port */
328 entry_id = create_hostcache_id(hostname, port);
329 /* If we can't create the entry id, fail */
330 if(!entry_id)
331 return NULL;
332 entry_len = strlen(entry_id);
334 /* Create a new cache entry */
335 dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1);
336 if(!dns) {
337 free(entry_id);
338 return NULL;
341 dns->inuse = 0; /* init to not used */
342 dns->addr = addr; /* this is the address(es) */
344 /* Store the resolved data in our DNS cache. This function may return a
345 pointer to an existing struct already present in the hash, and it may
346 return the same argument we pass in. Make no assumptions. */
347 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
348 (void *)dns);
349 if(!dns2) {
350 /* Major badness, run away. */
351 free(dns);
352 free(entry_id);
353 return NULL;
355 time(&now);
356 dns = dns2;
358 dns->timestamp = now; /* used now */
359 dns->inuse++; /* mark entry as in-use */
361 /* free the allocated entry_id again */
362 free(entry_id);
364 return dns;
368 * Curl_resolv() is the main name resolve function within libcurl. It resolves
369 * a name and returns a pointer to the entry in the 'entry' argument (if one
370 * is provided). This function might return immediately if we're using asynch
371 * resolves. See the return codes.
373 * The cache entry we return will get its 'inuse' counter increased when this
374 * function is used. You MUST call Curl_resolv_unlock() later (when you're
375 * done using this struct) to decrease the counter again.
377 * Return codes:
379 * CURLRESOLV_ERROR (-1) = error, no pointer
380 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
381 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
384 int Curl_resolv(struct connectdata *conn,
385 const char *hostname,
386 int port,
387 struct Curl_dns_entry **entry)
389 char *entry_id = NULL;
390 struct Curl_dns_entry *dns = NULL;
391 size_t entry_len;
392 struct SessionHandle *data = conn->data;
393 CURLcode result;
394 int rc = CURLRESOLV_ERROR; /* default to failure */
395 *entry = NULL;
397 #ifdef HAVE_SIGSETJMP
398 /* this allows us to time-out from the name resolver, as the timeout
399 will generate a signal and we will siglongjmp() from that here */
400 if(!data->set.no_signal) {
401 if(sigsetjmp(curl_jmpenv, 1)) {
402 /* this is coming from a siglongjmp() */
403 failf(data, "name lookup timed out");
404 return rc;
407 #endif
409 /* Create an entry id, based upon the hostname and port */
410 entry_id = create_hostcache_id(hostname, port);
411 /* If we can't create the entry id, fail */
412 if(!entry_id)
413 return rc;
415 entry_len = strlen(entry_id);
417 if(data->share)
418 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
420 /* See if its already in our dns cache */
421 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
423 /* See whether the returned entry is stale. Done before we release lock */
424 if( remove_entry_if_stale(data, dns) )
425 dns = NULL; /* the memory deallocation is being handled by the hash */
427 if(dns) {
428 dns->inuse++; /* we use it! */
429 rc = CURLRESOLV_RESOLVED;
432 if(data->share)
433 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
435 /* free the allocated entry_id again */
436 free(entry_id);
438 if(!dns) {
439 /* The entry was not in the cache. Resolve it to IP address */
441 Curl_addrinfo *addr;
442 int respwait;
444 /* Check what IP specifics the app has requested and if we can provide it.
445 * If not, bail out. */
446 if(!Curl_ipvalid(data))
447 return CURLRESOLV_ERROR;
449 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
450 non-zero value indicating that we need to wait for the response to the
451 resolve call */
452 addr = Curl_getaddrinfo(conn, hostname, port, &respwait);
454 if(!addr) {
455 if(respwait) {
456 /* the response to our resolve call will come asynchronously at
457 a later time, good or bad */
458 /* First, check that we haven't received the info by now */
459 result = Curl_is_resolved(conn, &dns);
460 if(result) /* error detected */
461 return CURLRESOLV_ERROR;
462 if(dns)
463 rc = CURLRESOLV_RESOLVED; /* pointer provided */
464 else
465 rc = CURLRESOLV_PENDING; /* no info yet */
468 else {
469 if(data->share)
470 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
472 /* we got a response, store it in the cache */
473 dns = Curl_cache_addr(data, addr, hostname, port);
475 if(data->share)
476 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
478 if(!dns)
479 /* returned failure, bail out nicely */
480 Curl_freeaddrinfo(addr);
481 else
482 rc = CURLRESOLV_RESOLVED;
486 *entry = dns;
488 return rc;
492 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
493 * made, the struct may be destroyed due to pruning. It is important that only
494 * one unlock is made for each Curl_resolv() call.
496 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
498 DEBUGASSERT(dns && (dns->inuse>0));
500 if(data->share)
501 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
503 dns->inuse--;
505 if(data->share)
506 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
510 * File-internal: free a cache dns entry.
512 static void freednsentry(void *freethis)
514 struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
516 Curl_freeaddrinfo(p->addr);
518 free(p);
522 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
524 struct curl_hash *Curl_mk_dnscache(void)
526 return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
529 #ifdef CURLRES_ADDRINFO_COPY
531 /* align on even 64bit boundaries */
532 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
535 * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
536 * returns a pointer to the malloc()ed copy. You need to call free() on the
537 * returned buffer when you're done with it.
539 Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
541 const struct hostent *orig = org;
543 return Curl_he2ai(orig, port);
545 #endif /* CURLRES_ADDRINFO_COPY */
547 /***********************************************************************
548 * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6
549 * enabled)
550 **********************************************************************/
552 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
554 * This is a function for freeing name information in a protocol independent
555 * way.
557 void Curl_freeaddrinfo(Curl_addrinfo *ai)
559 Curl_addrinfo *next;
561 /* walk over the list and free all entries */
562 while(ai) {
563 next = ai->ai_next;
564 if(ai->ai_canonname)
565 free(ai->ai_canonname);
566 free(ai);
567 ai = next;
571 struct namebuf {
572 struct hostent hostentry;
573 char *h_addr_list[2];
574 struct in_addr addrentry;
575 char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */
579 * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
580 * together with a pointer to the string version of the address, and it
581 * returns a Curl_addrinfo chain filled in correctly with information for this
582 * address/host.
584 * The input parameters ARE NOT checked for validity but they are expected
585 * to have been checked already when this is called.
587 Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port)
589 Curl_addrinfo *ai;
591 #if defined(VMS) && \
592 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
593 #pragma pointer_size save
594 #pragma pointer_size short
595 #pragma message disable PTRMISMATCH
596 #endif
598 struct hostent *h;
599 struct in_addr *addrentry;
600 struct namebuf buffer;
601 struct namebuf *buf = &buffer;
603 h = &buf->hostentry;
604 h->h_addr_list = &buf->h_addr_list[0];
605 addrentry = &buf->addrentry;
606 #ifdef _CRAYC
607 /* On UNICOS, s_addr is a bit field and for some reason assigning to it
608 * doesn't work. There must be a better fix than this ugly hack.
610 memcpy(addrentry, &num, SIZEOF_in_addr);
611 #else
612 addrentry->s_addr = num;
613 #endif
614 h->h_addr_list[0] = (char*)addrentry;
615 h->h_addr_list[1] = NULL;
616 h->h_addrtype = AF_INET;
617 h->h_length = sizeof(*addrentry);
618 h->h_name = &buf->h_name[0];
619 h->h_aliases = NULL;
621 /* Now store the dotted version of the address */
622 snprintf((char *)h->h_name, 16, "%s", hostname);
624 #if defined(VMS) && \
625 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
626 #pragma pointer_size restore
627 #pragma message enable PTRMISMATCH
628 #endif
630 ai = Curl_he2ai(h, port);
632 return ai;
636 * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
637 * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
638 * stacks, but for all hosts and environments.
640 * Curl_addrinfo defined in "lib/hostip.h"
642 * struct Curl_addrinfo {
643 * int ai_flags;
644 * int ai_family;
645 * int ai_socktype;
646 * int ai_protocol;
647 * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
648 * char *ai_canonname;
649 * struct sockaddr *ai_addr;
650 * struct Curl_addrinfo *ai_next;
651 * };
653 * hostent defined in <netdb.h>
655 * struct hostent {
656 * char *h_name;
657 * char **h_aliases;
658 * int h_addrtype;
659 * int h_length;
660 * char **h_addr_list;
661 * };
663 * for backward compatibility:
665 * #define h_addr h_addr_list[0]
668 Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
670 Curl_addrinfo *ai;
671 Curl_addrinfo *prevai = NULL;
672 Curl_addrinfo *firstai = NULL;
673 struct sockaddr_in *addr;
674 #ifdef CURLRES_IPV6
675 struct sockaddr_in6 *addr6;
676 #endif /* CURLRES_IPV6 */
677 int i;
678 struct in_addr *curr;
680 if(!he)
681 /* no input == no output! */
682 return NULL;
684 for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) {
686 int ss_size;
687 #ifdef CURLRES_IPV6
688 if (he->h_addrtype == AF_INET6)
689 ss_size = sizeof (struct sockaddr_in6);
690 else
691 #endif /* CURLRES_IPV6 */
692 ss_size = sizeof (struct sockaddr_in);
694 ai = calloc(1, sizeof(Curl_addrinfo) + ss_size);
696 if(!ai)
697 break;
699 if(!firstai)
700 /* store the pointer we want to return from this function */
701 firstai = ai;
703 if(prevai)
704 /* make the previous entry point to this */
705 prevai->ai_next = ai;
707 ai->ai_family = he->h_addrtype;
709 /* we return all names as STREAM, so when using this address for TFTP
710 the type must be ignored and conn->socktype be used instead! */
711 ai->ai_socktype = SOCK_STREAM;
713 ai->ai_addrlen = ss_size;
714 /* make the ai_addr point to the address immediately following this struct
715 and use that area to store the address */
716 ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo));
718 /* need to free this eventually */
719 ai->ai_canonname = strdup(he->h_name);
721 /* leave the rest of the struct filled with zero */
723 switch (ai->ai_family) {
724 case AF_INET:
725 addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
727 memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr));
728 addr->sin_family = (unsigned short)(he->h_addrtype);
729 addr->sin_port = htons((unsigned short)port);
730 break;
732 #ifdef CURLRES_IPV6
733 case AF_INET6:
734 addr6 = (struct sockaddr_in6 *)ai->ai_addr; /* storage area for this info */
736 memcpy((char *)&(addr6->sin6_addr), curr, sizeof(struct in6_addr));
737 addr6->sin6_family = (unsigned short)(he->h_addrtype);
738 addr6->sin6_port = htons((unsigned short)port);
739 break;
740 #endif /* CURLRES_IPV6 */
743 prevai = ai;
745 return firstai;
748 #endif /* CURLRES_IPV4 || CURLRES_ARES */