diff --git a/lib/connect.c b/lib/connect.c index 0a9e97368..4da03a1bc 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -206,7 +206,7 @@ static CURLcode bindlocal(struct connectdata *conn, *************************************************************/ if (strlen(data->set.device)<255) { struct sockaddr_in sa; - Curl_addrinfo *h=NULL; + struct Curl_dns_entry *h=NULL; size_t size; char myhost[256] = ""; in_addr_t in; @@ -247,12 +247,17 @@ static CURLcode bindlocal(struct connectdata *conn, if (INADDR_NONE != in) { if ( h ) { + Curl_addrinfo *addr = h->addr; + + h->inuse--; /* decrease the use-counter, we don't need it anymore + after this function has returned */ + memset((char *)&sa, 0, sizeof(sa)); #ifdef ENABLE_IPV6 - memcpy((char *)&sa.sin_addr, h->ai_addr, h->ai_addrlen); - sa.sin_family = h->ai_family; + memcpy((char *)&sa.sin_addr, addr->ai_addr, addr->ai_addrlen); + sa.sin_family = addr->ai_family; #else - memcpy((char *)&sa.sin_addr, h->h_addr, h->h_length); + memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length); sa.sin_family = AF_INET; #endif sa.sin_addr.s_addr = in; @@ -410,7 +415,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, */ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ - Curl_addrinfo *remotehost, /* use one in here */ + struct Curl_dns_entry *remotehost, /* use this one */ int port, /* connect to this */ int *sockconn, /* the connected socket */ Curl_ipconnect **addr, /* the one we used */ @@ -479,7 +484,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ struct addrinfo *ai; port =0; /* prevent compiler warning */ - for (ai = remotehost; ai; ai = ai->ai_next, aliasindex++) { + for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; diff --git a/lib/connect.h b/lib/connect.h index ceeb9fb4e..fdbecec0c 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -31,7 +31,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, bool *connected); CURLcode Curl_connecthost(struct connectdata *conn, - Curl_addrinfo *host, /* connect to this */ + struct Curl_dns_entry *host, /* connect to this */ int port, /* connect to this port number */ int *sockconn, /* not set if error is returned */ Curl_ipconnect **addr, /* the one we used */ diff --git a/lib/ftp.c b/lib/ftp.c index 9fe31b857..9e7e3a7d6 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1184,7 +1184,6 @@ CURLcode ftp_use_port(struct connectdata *conn) */ struct sockaddr_in sa; struct hostent *h=NULL; - char *hostdataptr=NULL; unsigned short porttouse; char myhost[256] = ""; bool sa_filled_in = FALSE; @@ -1215,6 +1214,9 @@ CURLcode ftp_use_port(struct connectdata *conn) sa_filled_in = TRUE; /* the sa struct is filled in */ } + if(h) + h->inuse--; /* when we return from here, we can forget about this */ + if ( h || sa_filled_in) { if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) { int size; @@ -1250,19 +1252,16 @@ CURLcode ftp_use_port(struct connectdata *conn) if ( listen(portsock, 1) < 0 ) { failf(data, "listen(2) failed on socket"); - free(hostdataptr); return CURLE_FTP_PORT_FAILED; } } else { failf(data, "bind(2) failed on socket"); - free(hostdataptr); return CURLE_FTP_PORT_FAILED; } } else { failf(data, "socket(2) failed (%s)"); - free(hostdataptr); return CURLE_FTP_PORT_FAILED; } } @@ -1332,7 +1331,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn, char *buf = data->state.buffer; /* this is our buffer */ int ftpcode; /* receive FTP response codes in this */ CURLcode result; - Curl_addrinfo *addr=NULL; + struct Curl_dns_entry *addr=NULL; Curl_ipconnect *conninfo; /* @@ -1480,6 +1479,8 @@ CURLcode ftp_use_pasv(struct connectdata *conn, &conninfo, connected); + addr->inuse--; /* we're done using this address */ + /* * When this is used from the multi interface, this might've returned with * the 'connected' set to FALSE and thus we are now awaiting a non-blocking diff --git a/lib/hash.c b/lib/hash.c index 7ccbe7958..56e7077d8 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -25,6 +25,7 @@ #include #include + #include "hash.h" #include "llist.h" @@ -128,7 +129,6 @@ _mk_hash_element (curl_hash_element **e, char *key, size_t key_len, const void * (*e)->key = strdup(key); (*e)->key_len = key_len; (*e)->ptr = (void *) p; - return 0; } /* }}} */ @@ -195,10 +195,10 @@ Curl_hash_delete(curl_hash *h, char *key, size_t key_len) } /* }}} */ -/* {{{ int curl_hash_find (curl_hash *, char *, size_t, void **) +/* {{{ int curl_hash_pick (curl_hash *, char *, size_t, void **) */ -int -Curl_hash_find(curl_hash *h, char *key, size_t key_len, void **p) +void * +Curl_hash_pick(curl_hash *h, char *key, size_t key_len) { curl_llist_element *le; curl_hash_element *he; @@ -209,12 +209,11 @@ Curl_hash_find(curl_hash *h, char *key, size_t key_len, void **p) le = CURL_LLIST_NEXT(le)) { he = CURL_LLIST_VALP(le); if (_hash_key_compare(he->key, he->key_len, key, key_len)) { - *p = he->ptr; - return 1; + return he->ptr; } } - return 0; + return NULL; } /* }}} */ @@ -222,7 +221,7 @@ Curl_hash_find(curl_hash *h, char *key, size_t key_len, void **p) */ void Curl_hash_apply(curl_hash *h, void *user, - void (*cb)(void *, curl_hash_element *)) + void (*cb)(void *user, void *ptr)) { curl_llist_element *le; int i; @@ -231,7 +230,8 @@ Curl_hash_apply(curl_hash *h, void *user, for (le = CURL_LLIST_HEAD(h->table[i]); le != NULL; le = CURL_LLIST_NEXT(le)) { - cb(user, (curl_hash_element *) CURL_LLIST_VALP(le)); + curl_hash_element *el = CURL_LLIST_VALP(le); + cb(user, el->ptr); } } } diff --git a/lib/hash.h b/lib/hash.h index 523e0ed6f..c3464c355 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -41,7 +41,7 @@ typedef struct _curl_hash { typedef struct _curl_hash_element { void *ptr; char *key; - size_t key_len; + size_t key_len; } curl_hash_element; @@ -49,15 +49,14 @@ void Curl_hash_init(curl_hash *, int, curl_hash_dtor); curl_hash *Curl_hash_alloc(int, curl_hash_dtor); int Curl_hash_add(curl_hash *, char *, size_t, const void *); int Curl_hash_delete(curl_hash *h, char *key, size_t key_len); -int Curl_hash_find(curl_hash *, char *, size_t, void **p); -void Curl_hash_apply(curl_hash *h, void *user, void (*cb)(void *, curl_hash_element *)); +void *Curl_hash_pick(curl_hash *, char *, size_t); +void Curl_hash_apply(curl_hash *h, void *user, + void (*cb)(void *user, void *ptr)); int Curl_hash_count(curl_hash *h); void Curl_hash_clean(curl_hash *h); void Curl_hash_clean_with_criterium(curl_hash *h, void *user, int (*comp)(void *, void *)); void Curl_hash_destroy(curl_hash *h); -#define Curl_hash_update Curl_hash_add - #endif /* diff --git a/lib/hostip.c b/lib/hostip.c index 1ad6951f3..21b9103dd 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -80,6 +80,11 @@ static curl_hash hostname_cache; static int host_cache_initialized; +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp); + void Curl_global_host_cache_init(void) { if (!host_cache_initialized) { @@ -101,11 +106,6 @@ void Curl_global_host_cache_dtor(void) } } -struct curl_dns_cache_entry { - Curl_addrinfo *addr; - time_t timestamp; -}; - /* count the number of characters that an integer takes up */ static int _num_chars(int i) { @@ -129,7 +129,7 @@ static int _num_chars(int i) /* Create a hostcache id */ static char * -_create_hostcache_id(char *server, int port, ssize_t *entry_len) +create_hostcache_id(char *server, int port, ssize_t *entry_len) { char *id = NULL; @@ -162,16 +162,19 @@ struct hostcache_prune_data { }; static int -_curl_hostcache_timestamp_remove(void *datap, void *hc) +hostcache_timestamp_remove(void *datap, void *hc) { struct hostcache_prune_data *data = (struct hostcache_prune_data *) datap; - struct curl_dns_cache_entry *c = (struct curl_dns_cache_entry *) hc; + struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; - if (data->now - c->timestamp < data->cache_timeout) { + if ((data->now - c->timestamp < data->cache_timeout) || + c->inuse) { + /* please don't remove */ return 0; } + /* fine, remove */ return 1; } @@ -185,14 +188,30 @@ hostcache_prune(curl_hash *hostcache, int cache_timeout, int now) Curl_hash_clean_with_criterium(hostcache, (void *) &user, - _curl_hostcache_timestamp_remove); + hostcache_timestamp_remove); } +#ifdef MALLOCDEBUG +/* Called from Curl_done() to check that there's no DNS cache entry with + a non-zero counter left. */ +void Curl_scan_cache_used(void *user, void *ptr) +{ + struct Curl_dns_entry *e = ptr; + (void)user; /* prevent compiler warning */ + if(e->inuse) { + fprintf(stderr, "*** WARNING: locked DNS cache entry detected: %s\n", + e->entry_id); + /* perform a segmentation fault to draw attention */ + *(void **)0 = 0; + } +} +#endif + /* Macro to save redundant free'ing of entry_id */ -#define _hostcache_return(__v) \ +#define HOSTCACHE_RETURN(dns) \ { \ free(entry_id); \ - return (__v); \ + return dns; \ } #ifdef HAVE_SIGSETJMP @@ -200,72 +219,69 @@ hostcache_prune(curl_hash *hostcache, int cache_timeout, int now) sigjmp_buf curl_jmpenv; #endif -Curl_addrinfo *Curl_resolv(struct SessionHandle *data, - char *hostname, - int port) +struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, + char *hostname, + int port) { char *entry_id = NULL; - struct curl_dns_cache_entry *p = NULL; + struct Curl_dns_entry *dns = NULL; ssize_t entry_len; time_t now; char *bufp; #ifdef HAVE_SIGSETJMP - if(sigsetjmp(curl_jmpenv, 1) != 0) { + /* this allows us to time-out from the name resolver, as the timeout + will generate a signal and we will siglongjmp() from that here */ + if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) { /* this is coming from a siglongjmp() */ failf(data, "name lookup time-outed"); return NULL; } #endif -#if 0 - /* If the host cache timeout is 0, we don't do DNS cach'ing - so fall through */ - if (data->set.dns_cache_timeout == 0) { - return Curl_getaddrinfo(data, hostname, port, &bufp); - } -#endif + /* Create an entry id, based upon the hostname and port */ + entry_len = strlen(hostname); + entry_id = create_hostcache_id(hostname, port, &entry_len); + /* If we can't create the entry id, fail */ + if (!entry_id) + return NULL; + + /* See if its already in our dns cache */ + dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); + if (!dns) { + Curl_addrinfo *addr = my_getaddrinfo(data, hostname, port, &bufp); + + if (!addr) { + HOSTCACHE_RETURN(NULL); + } + + /* Create a new cache entry */ + dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); + if (!dns) { + Curl_freeaddrinfo(addr); + HOSTCACHE_RETURN(NULL); + } + + dns->inuse = 0; + dns->addr = addr; + /* Save it in our host cache */ + Curl_hash_add(data->hostcache, entry_id, entry_len+1, (const void *) dns); + } time(&now); - /* Remove outdated entries from the hostcache */ + dns->timestamp = now; + dns->inuse++; /* mark entry as in-use */ +#ifdef MALLOCDEBUG + dns->entry_id = entry_id; +#endif + + /* Remove outdated and unused entries from the hostcache */ hostcache_prune(data->hostcache, data->set.dns_cache_timeout, now); - /* Create an entry id, based upon the hostname and port */ - entry_len = strlen(hostname); - entry_id = _create_hostcache_id(hostname, port, &entry_len); - /* If we can't create the entry id, don't cache, just fall-through - to the plain Curl_getaddrinfo() */ - if (!entry_id) { - return Curl_getaddrinfo(data, hostname, port, &bufp); - } - - /* See if its already in our dns cache */ - if (entry_id && - Curl_hash_find(data->hostcache, entry_id, entry_len+1, (void **) &p)) { - _hostcache_return(p->addr); - } - - /* Create a new cache entry */ - p = (struct curl_dns_cache_entry *) - malloc(sizeof(struct curl_dns_cache_entry)); - if (!p) { - _hostcache_return(NULL); - } - - p->addr = Curl_getaddrinfo(data, hostname, port, &bufp); - if (!p->addr) { - free(p); - _hostcache_return(NULL); - } - p->timestamp = now; - - /* Save it in our host cache */ - Curl_hash_update(data->hostcache, entry_id, entry_len+1, (const void *) p); - - _hostcache_return(p->addr); + HOSTCACHE_RETURN(dns); } /* @@ -275,7 +291,7 @@ Curl_addrinfo *Curl_resolv(struct SessionHandle *data, */ void Curl_freeaddrinfo(void *freethis) { - struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis; + struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis; #ifdef ENABLE_IPV6 freeaddrinfo(p->addr); @@ -333,10 +349,10 @@ void curl_freeaddrinfo(struct addrinfo *freethis, * memory we need to free after use. That meory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */ -Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp) +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) { struct addrinfo hints, *res; int error; @@ -509,10 +525,10 @@ static void hostcache_fixoffset(struct hostent *h, int offset) /* The original code to this function was once stolen from the Dancer source code, written by Bjorn Reese, it has since been patched and modified considerably. */ -Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp) +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) { struct hostent *h = NULL; in_addr_t in; diff --git a/lib/hostip.h b/lib/hostip.h index 512b27ebf..2593bc343 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -23,6 +23,7 @@ * $Id$ ***************************************************************************/ +#include "setup.h" #include "hash.h" struct addrinfo; @@ -35,15 +36,23 @@ curl_hash *Curl_global_host_cache_get(void); #define Curl_global_host_cache_use(__p) ((__p)->set.global_dns_cache) -Curl_addrinfo *Curl_resolv(struct SessionHandle *data, - char *hostname, - int port); +struct Curl_dns_entry { + Curl_addrinfo *addr; + time_t timestamp; + long inuse; /* use-counter, make very sure you decrease this + when you're done using the address you received */ +#ifdef MALLOCDEBUG + char *entry_id; +#endif +}; + +struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, + char *hostname, + int port); + +/* for debugging purposes only: */ +void Curl_scan_cache_used(void *user, void *ptr); -/* Get name info */ -Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp); /* free name info */ void Curl_freeaddrinfo(void *freethis); diff --git a/lib/url.c b/lib/url.c index d64e9cb2c..45a79b16f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1484,18 +1484,23 @@ static int handleSock5Proxy( socksreq[3] = 1; /* IPv4 = 1 */ { - Curl_addrinfo *hp; - hp = Curl_resolv(conn->data, conn->hostname, conn->remote_port); +#ifndef ENABLE_IPV6 + struct Curl_dns_entry *dns; + Curl_addrinfo *hp=NULL; + dns = Curl_resolv(conn->data, conn->hostname, conn->remote_port); /* * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ -#ifndef ENABLE_IPV6 + if(dns) + hp=dns->addr; if (hp && hp->h_addr_list[0]) { socksreq[4] = ((char*)hp->h_addr_list[0])[0]; socksreq[5] = ((char*)hp->h_addr_list[0])[1]; socksreq[6] = ((char*)hp->h_addr_list[0])[2]; socksreq[7] = ((char*)hp->h_addr_list[0])[3]; + + dns->inuse--; /* not used anymore from now on */ } else { failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", @@ -1548,7 +1553,7 @@ static int handleSock5Proxy( } static CURLcode ConnectPlease(struct connectdata *conn, - Curl_addrinfo *hostaddr, + struct Curl_dns_entry *hostaddr, bool *connected) { CURLcode result; @@ -1567,6 +1572,8 @@ static CURLcode ConnectPlease(struct connectdata *conn, /* All is cool, then we store the current information from the hostaddr struct to the serv_addr, as it might be needed later. The address returned from the function above is crucial here. */ + conn->connect_addr = hostaddr; + #ifdef ENABLE_IPV6 conn->serv_addr = addr; #else @@ -1597,7 +1604,7 @@ static CURLcode ConnectPlease(struct connectdata *conn, } static void verboseconnect(struct connectdata *conn, - Curl_addrinfo *hostaddr) + struct Curl_dns_entry *dns) { #ifdef HAVE_INET_NTOA_R char ntoa_buf[64]; @@ -1606,7 +1613,7 @@ static void verboseconnect(struct connectdata *conn, /* Figure out the ip-number and display the first host name it shows: */ #ifdef ENABLE_IPV6 - (void)hostaddr; /* not used in the IPv6 enabled version */ + (void)dns; /* not used in the IPv6 enabled version */ { char hbuf[NI_MAXHOST]; #ifdef NI_WITHSCOPEID @@ -1629,6 +1636,7 @@ static void verboseconnect(struct connectdata *conn, } #else { + Curl_addrinfo *hostaddr=dns->addr; struct in_addr in; (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr)); infof(data, "Connected to %s (%s) port %d\n", @@ -1654,7 +1662,7 @@ static void verboseconnect(struct connectdata *conn, * 'serv_addr' field in the connectdata struct for most of it. */ CURLcode Curl_protocol_connect(struct connectdata *conn, - Curl_addrinfo *hostaddr) + struct Curl_dns_entry *hostaddr) { struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; @@ -1689,7 +1697,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, struct connectdata *conn; struct connectdata *conn_temp; int urllen; - Curl_addrinfo *hostaddr; + struct Curl_dns_entry *hostaddr; #ifdef HAVE_ALARM unsigned int prev_alarm=0; #endif @@ -2811,7 +2819,6 @@ CURLcode Curl_connect(struct SessionHandle *data, return code; } - CURLcode Curl_done(struct connectdata *conn) { struct SessionHandle *data=conn->data; @@ -2829,7 +2836,16 @@ CURLcode Curl_done(struct connectdata *conn) free(conn->newurl); conn->newurl = NULL; } - + + if(conn->connect_addr) + conn->connect_addr->inuse--; /* done with this */ + +#ifdef MALLOCDEBUG + /* scan for DNS cache entries still marked as in use */ + Curl_hash_apply(data->hostcache, + NULL, Curl_scan_cache_used); +#endif + /* this calls the protocol-specific function pointer previously set */ if(conn->curl_done) result = conn->curl_done(conn); diff --git a/lib/url.h b/lib/url.h index 29b02c843..2da8ade11 100644 --- a/lib/url.h +++ b/lib/url.h @@ -36,5 +36,5 @@ CURLcode Curl_do_more(struct connectdata *); CURLcode Curl_done(struct connectdata *); CURLcode Curl_disconnect(struct connectdata *); CURLcode Curl_protocol_connect(struct connectdata *conn, - Curl_addrinfo *hostaddr); + struct Curl_dns_entry *dns); #endif diff --git a/lib/urldata.h b/lib/urldata.h index 5bf4ef04b..e521d325c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -26,8 +26,6 @@ /* This file is for lib internal stuff */ #include "setup.h" -#include "hostip.h" -#include "hash.h" #define PORT_FTP 21 #define PORT_TELNET 23 @@ -81,6 +79,8 @@ #include #include "http_chunks.h" /* for the structs and enum stuff */ +#include "hostip.h" +#include "hash.h" #ifdef HAVE_ZLIB_H #include /* for content-encoding 08/28/02 jhrg */ @@ -311,8 +311,11 @@ struct connectdata { #define PROT_FTPS (1<<9) #define PROT_SSL (1<<10) /* protocol requires SSL */ + /* the particular host we use, in two different ways */ + struct Curl_dns_entry *connect_addr; + #ifdef ENABLE_IPV6 - struct addrinfo *serv_addr; /* the particular host we use */ + struct addrinfo *serv_addr; #else struct sockaddr_in serv_addr; #endif