From 2cff2518631fc538c127c1e94c47c743e2a64661 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 5 Nov 2002 10:51:41 +0000 Subject: [PATCH] Curl_resolv() now returns a different struct, and it contains a reference counter so that the caller needs to decrease that counter when done with the returned data. If compiled with MALLOCDEBUG I've added some extra checking that the counter is decreased before a handle is closed etc. --- lib/connect.c | 17 ++++-- lib/connect.h | 2 +- lib/ftp.c | 11 ++-- lib/hash.c | 18 +++--- lib/hash.h | 9 ++- lib/hostip.c | 150 ++++++++++++++++++++++++++++---------------------- lib/hostip.h | 25 ++++++--- lib/url.c | 36 ++++++++---- lib/url.h | 2 +- lib/urldata.h | 9 ++- 10 files changed, 164 insertions(+), 115 deletions(-) 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