diff --git a/include/curl/curl.h b/include/curl/curl.h index 08630b54ac4f655aa992c0a3020c68ab93ca7db6..04171ba9aa03a0a097c792be4576688a8f1a77cd 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -902,10 +902,15 @@ typedef enum { /* Different data locks for a single share */ typedef enum { CURL_LOCK_DATA_NONE = 0, - CURL_LOCK_DATA_COOKIE = 1, - CURL_LOCK_DATA_DNS = 2, - CURL_LOCK_DATA_SSL_SESSION = 3, - CURL_LOCK_DATA_CONNECT = 4, + /* CURL_LOCK_DATA_SHARE is used internaly to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_LAST } curl_lock_data; diff --git a/lib/Makefile.am b/lib/Makefile.am index 7fb16637004540f70ee07c083993676d699f59ee..342a29c46ad506c3846328d0c364d72bc0428ef6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -66,7 +66,7 @@ getpass.c netrc.c telnet.h getinfo.c getinfo.h transfer.c strequal.c \ strequal.h easy.c security.h security.c krb4.c krb4.h memdebug.c \ memdebug.h inet_ntoa_r.h http_chunks.c http_chunks.h strtok.c strtok.h \ connect.c connect.h llist.c llist.h hash.c hash.h multi.c \ -content_encoding.c content_encoding.h share.h +content_encoding.c content_encoding.h share.c share.h noinst_HEADERS = setup.h transfer.h diff --git a/lib/connect.c b/lib/connect.c index 8844253265d5bbfc2e9e25f52222675edacf89d9..da0a5b9511f308f49e96ecfbc1e298a75e5af030 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -248,7 +248,7 @@ static CURLcode bindlocal(struct connectdata *conn, if ( h ) { Curl_addrinfo *addr = h->addr; - Curl_resolv_unlock(h); + Curl_resolv_unlock(data, h); /* we don't need it anymore after this function has returned */ #ifdef ENABLE_IPV6 diff --git a/lib/ftp.c b/lib/ftp.c index 8cff5c66a4acb69c92c567d8326353f352d80c04..c7246446c7f48e40a55824ef9b38aa137fc74dc6 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1233,7 +1233,7 @@ CURLcode ftp_use_port(struct connectdata *conn) if(h) /* when we return from here, we can forget about this */ - Curl_resolv_unlock(h); + Curl_resolv_unlock(data, h); if ( h || sa_filled_in) { if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) { @@ -1497,7 +1497,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn, &conninfo, connected); - Curl_resolv_unlock(addr); /* we're done using this address */ + Curl_resolv_unlock(data, addr); /* we're done using this address */ /* * When this is used from the multi interface, this might've returned with diff --git a/lib/hostip.c b/lib/hostip.c index d5f32fd49d5f31876bea67bb631708d2da117b8c..b5fda2a235b538b8cc0e04a94349d29af23b5dfe 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -64,6 +64,7 @@ #include "sendf.h" #include "hostip.h" #include "hash.h" +#include "share.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -211,6 +212,10 @@ void Curl_scan_cache_used(void *user, void *ptr) #define HOSTCACHE_RETURN(dns) \ { \ free(entry_id); \ + if(data->share) \ + { \ + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); \ + } \ return dns; \ } @@ -245,7 +250,12 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, /* If we can't create the entry id, fail */ if (!entry_id) return NULL; - + + if(data->share) + { + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + } + /* See if its already in our dns cache */ dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); @@ -284,6 +294,19 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, HOSTCACHE_RETURN(dns); } +void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) +{ + if(data->share) + { + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + } + dns->inuse--; + if(data->share) + { + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + } +} + /* * This is a wrapper function for freeing name information in a protocol * independent way. This takes care of using the appropriate underlaying diff --git a/lib/hostip.h b/lib/hostip.h index 396508e7731d3b7d4f52bd7b0dfe9906ce7dc611..fc1474c395af15d4fdbf156f563d548aeecf380d 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -59,7 +59,7 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, int port); /* unlock a previously resolved dns entry */ -#define Curl_resolv_unlock(dns) dns->inuse-- +void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns); /* for debugging purposes only: */ void Curl_scan_cache_used(void *user, void *ptr); diff --git a/lib/share.c b/lib/share.c index dcf7e90bbcccd505a85c651aa9f1e1738e5356c6..8e5ec0ea04ea201c7d025f1428e5a15c550623b8 100644 --- a/lib/share.c +++ b/lib/share.c @@ -65,12 +65,53 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) /* this is a type this share will share */ type = va_arg(param, int); share->specifier |= (1<<type); + switch( type ) + { + case CURL_LOCK_DATA_DNS: + if (!share->hostcache) { + share->hostcache = Curl_hash_alloc(7, Curl_freednsinfo); + } + break; + + case CURL_LOCK_DATA_COOKIE: + break; + + case CURL_LOCK_DATA_SSL_SESSION: + break; + + case CURL_LOCK_DATA_CONNECT: + break; + + default: + return CURLSHE_BAD_OPTION; + } break; case CURLSHOPT_UNSHARE: /* this is a type this share will no longer share */ type = va_arg(param, int); share->specifier &= ~(1<<type); + switch( type ) + { + case CURL_LOCK_DATA_DNS: + if (share->hostcache) { + Curl_hash_destroy(share->hostcache); + share->hostcache = NULL; + } + break; + + case CURL_LOCK_DATA_COOKIE: + break; + + case CURL_LOCK_DATA_SSL_SESSION: + break; + + case CURL_LOCK_DATA_CONNECT: + break; + + default: + return CURLSHE_BAD_OPTION; + } break; case CURLSHOPT_LOCKFUNC: @@ -108,7 +149,7 @@ CURLSHcode curl_share_cleanup(CURLSH *sh) CURLSHcode -Curl_share_acquire_lock(struct SessionHandle *data, curl_lock_data type) +Curl_share_lock(struct SessionHandle *data, curl_lock_data type, curl_lock_access access) { struct Curl_share *share = data->share; @@ -116,8 +157,7 @@ Curl_share_acquire_lock(struct SessionHandle *data, curl_lock_data type) return CURLSHE_INVALID; if(share->specifier & (1<<type)) { - share->lockfunc (data, type, CURL_LOCK_ACCESS_SINGLE, share->clientdata); - share->locked |= (1<<type); + share->lockfunc (data, type, access, share->clientdata); } /* else if we don't share this, pretend successful lock */ @@ -125,7 +165,7 @@ Curl_share_acquire_lock(struct SessionHandle *data, curl_lock_data type) } CURLSHcode -Curl_share_release_lock(struct SessionHandle *data, curl_lock_data type) +Curl_share_unlock(struct SessionHandle *data, curl_lock_data type) { struct Curl_share *share = data->share; @@ -134,7 +174,6 @@ Curl_share_release_lock(struct SessionHandle *data, curl_lock_data type) if(share->specifier & (1<<type)) { share->unlockfunc (data, type, share->clientdata); - share->locked &= ~(1<<type); } return CURLSHE_OK; diff --git a/lib/share.h b/lib/share.h index ea6f2f1ceb684d1805998b31919460c3bd03e913..76e6244d7eaccb186d9d4fc1569d7c67bdc998b5 100644 --- a/lib/share.h +++ b/lib/share.h @@ -30,15 +30,24 @@ /* this struct is libcurl-private, don't export details */ struct Curl_share { unsigned int specifier; - unsigned int locked; - unsigned int dirty; + volatile unsigned int dirty; curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; + + curl_hash *hostcache; }; -CURLSHcode Curl_share_aquire_lock (struct SessionHandle *, curl_lock_data); -CURLSHcode Curl_share_release_lock (struct SessionHandle *, curl_lock_data); +CURLSHcode Curl_share_lock ( + struct SessionHandle *, + curl_lock_data, + curl_lock_access + ); + +CURLSHcode Curl_share_unlock ( + struct SessionHandle *, + curl_lock_data + ); #endif /* __CURL_SHARE_H */ diff --git a/lib/url.c b/lib/url.c index 628f3b12a35453f7e286c01b10b9668e9bc4a21c..0836b24ab5519f94596bc8d0e0b4a1ac259a009c 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1078,10 +1078,33 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) struct Curl_share *set; set = va_arg(param, struct Curl_share *); if(data->share) + { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + /* checking the dns cache stuff */ + if(data->share->hostcache == data->hostcache) + { + data->hostcache = NULL; + } + data->share->dirty--; - + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + } + data->share = set; + + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + data->share->dirty++; + + if( data->hostcache ) + { + Curl_hash_destroy(data->hostcache); + data->hostcache = data->share->hostcache; + } + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } break; @@ -1518,7 +1541,7 @@ static int handleSock5Proxy( socksreq[6] = ((char*)hp->h_addr_list[0])[2]; socksreq[7] = ((char*)hp->h_addr_list[0])[3]; - Curl_resolv_unlock(dns); /* not used anymore from now on */ + Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */ } else { failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", @@ -2901,7 +2924,7 @@ CURLcode Curl_done(struct connectdata *conn) } if(conn->connect_addr) - Curl_resolv_unlock(conn->connect_addr); /* done with this */ + Curl_resolv_unlock(conn->data, conn->connect_addr); /* done with this */ #if defined(MALLOCDEBUG) && defined(AGGRESIVE_TEST) /* scan for DNS cache entries still marked as in use */