/* * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. * * Curl_resolv() checks initially and multi_runsingle() checks each time * it discovers the handle in the state WAITRESOLVE whether the hostname * has already been resolved and the address has already been stored in * the DNS cache. This short circuits waiting for a lot of pending * lookups for the same hostname requested by different handles. * * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. */ struct Curl_dns_entry * Curl_fetch_addr(struct connectdata *conn, const char *hostname, int port, int *stale) { char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; struct SessionHandle *data = conn->data; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) return dns; entry_len = strlen(entry_id); /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); /* free the allocated entry_id again */ free(entry_id); /* See whether the returned entry is stale. Done before we release lock */ *stale = remove_entry_if_stale(data, dns); if(*stale) dns = NULL; /* the memory deallocation is being handled by the hash */ return dns; }
/* * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. * * When calling Curl_resolv() has resulted in a response with a returned * address, we call this function to store the information in the dns * cache etc * * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. */ struct Curl_dns_entry * Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr, const char *hostname, int port) { char *entry_id; size_t entry_len; struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; time_t now; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if (!entry_id) return NULL; entry_len = strlen(entry_id); /* Create a new cache entry */ dns = calloc(sizeof(struct Curl_dns_entry), 1); if (!dns) { free(entry_id); return NULL; } dns->inuse = 0; /* init to not used */ dns->addr = addr; /* this is the address(es) */ /* Store the resolved data in our DNS cache. This function may return a pointer to an existing struct already present in the hash, and it may return the same argument we pass in. Make no assumptions. */ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, (void *)dns); if (!dns2) { /* Major badness, run away. */ free(dns); free(entry_id); return NULL; } time(&now); dns = dns2; dns->timestamp = now; /* used now */ dns->inuse++; /* mark entry as in-use */ /* free the allocated entry_id again */ free(entry_id); return dns; }
static struct Curl_dns_entry * cache_resolv_response(struct SessionHandle *data, Curl_addrinfo *addr, char *hostname, int port) { char *entry_id; size_t entry_len; struct Curl_dns_entry *dns; time_t now; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port, &entry_len); /* If we can't create the entry id, fail */ if (!entry_id) return NULL; /* Create a new cache entry */ dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); if (!dns) { Curl_freeaddrinfo(addr); free(entry_id); return NULL; } dns->inuse = 0; /* init to not used */ dns->addr = addr; /* this is the address(es) */ /* Store the resolved data in our DNS cache. This function may return a pointer to an existing struct already present in the hash, and it may return the same argument we pass in. Make no assumptions. */ dns = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns); if(!dns) { /* Major badness, run away. When this happens, the 'dns' data has already been cleared up by Curl_hash_add(). */ free(entry_id); return NULL; } time(&now); dns->timestamp = now; /* used now */ dns->inuse++; /* mark entry as in-use */ /* free the allocated entry_id again */ free(entry_id); return dns; }
/* * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. * * When calling Curl_resolv() has resulted in a response with a returned * address, we call this function to store the information in the dns * cache etc * * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. */ struct Curl_dns_entry * Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr, const char *hostname, int port) { char *entry_id; size_t entry_len; struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) return NULL; entry_len = strlen(entry_id); /* Create a new cache entry */ dns = calloc(1, sizeof(struct Curl_dns_entry)); if(!dns) { free(entry_id); return NULL; } dns->inuse = 0; /* init to not used */ dns->addr = addr; /* this is the address(es) */ time(&dns->timestamp); if(dns->timestamp == 0) dns->timestamp = 1; /* zero indicates that entry isn't in hash table */ /* Store the resolved data in our DNS cache. */ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1, (void *)dns); if(!dns2) { free(dns); free(entry_id); return NULL; } dns = dns2; dns->inuse++; /* mark entry as in-use */ /* free the allocated entry_id */ free(entry_id); return dns; }
/* lookup address, returns entry if found and not stale */ static struct Curl_dns_entry * fetch_addr(struct connectdata *conn, const char *hostname, int port) { char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; struct SessionHandle *data = conn->data; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) return dns; entry_len = strlen(entry_id); /* See if its already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1); if(dns && (data->set.dns_cache_timeout != -1)) { /* See whether the returned entry is stale. Done before we release lock */ struct hostcache_prune_data user; time(&user.now); user.cache_timeout = data->set.dns_cache_timeout; if(hostcache_timestamp_remove(&user, dns)) { infof(data, "Hostname in DNS cache was stale, zapped\n"); dns = NULL; /* the memory deallocation is being handled by the hash */ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1); } } /* free the allocated entry_id again */ free(entry_id); return dns; }
/* Resolve a name and return a pointer in the 'entry' argument if one is available. Return codes: -1 = error, no pointer 0 = OK, pointer provided 1 = waiting for response, no pointer */ int Curl_resolv(struct connectdata *conn, char *hostname, int port, struct Curl_dns_entry **entry) { char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; int wait; struct SessionHandle *data = conn->data; CURLcode result; /* default to failure */ int rc = -1; *entry = NULL; #ifdef HAVE_SIGSETJMP /* 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 timed out"); return -1; } #endif /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port, &entry_len); /* If we can't create the entry id, fail */ if (!entry_id) return -1; 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); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); /* free the allocated entry_id again */ free(entry_id); if (!dns) { /* The entry was not in the cache. Resolve it to IP address */ /* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero value indicating that we need to wait for the response to the resolve call */ Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait); if (!addr) { if(wait) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ /* First, check that we haven't received the info by now */ result = Curl_is_resolved(conn, &dns); if(result) /* error detected */ return -1; if(dns) rc = 0; /* pointer provided */ else rc = 1; /* no info yet */ } } else { if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ dns = cache_resolv_response(data, addr, hostname, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); else rc = 0; } } else { dns->inuse++; /* we use it! */ rc = 0; } *entry = dns; return rc; }
int Curl_resolv(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry) { char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; struct SessionHandle *data = conn->data; CURLcode result; int rc = CURLRESOLV_ERROR; /* default to failure */ *entry = NULL; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) return rc; entry_len = strlen(entry_id); 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->dns.hostcache, entry_id, entry_len+1); /* free the allocated entry_id again */ free(entry_id); /* See whether the returned entry is stale. Done before we release lock */ if( remove_entry_if_stale(data, dns) ) dns = NULL; /* the memory deallocation is being handled by the hash */ if(dns) { dns->inuse++; /* we use it! */ rc = CURLRESOLV_RESOLVED; } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) { /* The entry was not in the cache. Resolve it to IP address */ Curl_addrinfo *addr; int respwait; /* Check what IP specifics the app has requested and if we can provide it. * If not, bail out. */ if(!Curl_ipvalid(conn)) return CURLRESOLV_ERROR; /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a non-zero value indicating that we need to wait for the response to the resolve call */ addr = Curl_getaddrinfo(conn, #ifdef DEBUGBUILD (data->set.str[STRING_DEVICE] && !strcmp(data->set.str[STRING_DEVICE], "LocalHost"))?"localhost": #endif hostname, port, &respwait); if(!addr) { if(respwait) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ /* First, check that we haven't received the info by now */ result = Curl_is_resolved(conn, &dns); if(result) /* error detected */ return CURLRESOLV_ERROR; if(dns) rc = CURLRESOLV_RESOLVED; /* pointer provided */ else rc = CURLRESOLV_PENDING; /* no info yet */ } } else { if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); else rc = CURLRESOLV_RESOLVED; } } *entry = dns; return rc; }
CURLcode Curl_loadhostpairs(struct SessionHandle *data) { struct curl_slist *hostp; char hostname[256]; char address[256]; int port; for(hostp = data->change.resolve; hostp; hostp = hostp->next) { if(!hostp->data) continue; if(hostp->data[0] == '-') { char *entry_id; size_t entry_len; if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n", hostp->data); continue; } /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) { return CURLE_OUT_OF_MEMORY; } entry_len = strlen(entry_id); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* delete entry, ignore if it didn't exist */ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); /* free the allocated entry_id again */ free(entry_id); } else { struct Curl_dns_entry *dns; Curl_addrinfo *addr; char *entry_id; size_t entry_len; if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, address)) { infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n", hostp->data); continue; } addr = Curl_str2addr(address, port); if(!addr) { infof(data, "Address in '%s' found illegal!\n", hostp->data); continue; } /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } entry_len = strlen(entry_id); 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->dns.hostcache, entry_id, entry_len+1); /* free the allocated entry_id again */ free(entry_id); if(!dns) { /* if not in the cache already, put this host in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); if(dns) { dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */ /* release the returned reference; the cache itself will keep the * entry alive: */ dns->inuse--; } } else /* this is a duplicate, free it again */ Curl_freeaddrinfo(addr); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } infof(data, "Added %s:%d:%s to DNS cache\n", hostname, port, address); } } data->change.resolve = NULL; /* dealt with now */ return CURLE_OK; }
CURLcode Curl_loadhostpairs(struct SessionHandle *data) { struct curl_slist *hostp; char hostname[256]; char address[256]; int port; for(hostp = data->change.resolve; hostp; hostp = hostp->next ) { if(!hostp->data) continue; if(hostp->data[0] == '-') { /* TODO: mark an entry for removal */ } else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, address)) { struct Curl_dns_entry *dns; Curl_addrinfo *addr; char *entry_id; size_t entry_len; addr = Curl_str2addr(address, port); if(!addr) { infof(data, "Resolve %s found illegal!\n", hostp->data); continue; } /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if(!entry_id) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } entry_len = strlen(entry_id); 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->dns.hostcache, entry_id, entry_len+1); /* free the allocated entry_id again */ free(entry_id); if(!dns) /* if not in the cache already, put this host in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); else /* this is a duplicate, free it again */ Curl_freeaddrinfo(addr); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) { Curl_freeaddrinfo(addr); return CURLE_OUT_OF_MEMORY; } infof(data, "Added %s:%d:%s to DNS cache\n", hostname, port, address); } } data->change.resolve = NULL; /* dealt with now */ return CURLE_OK; }
int Curl_resolv(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry) { char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; int wait; struct SessionHandle *data = conn->data; CURLcode result; int rc; *entry = NULL; #ifdef HAVE_SIGSETJMP /* 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) { if (sigsetjmp(curl_jmpenv, 1)) { /* this is coming from a siglongjmp() */ failf(data, "name lookup timed out"); return CURLRESOLV_ERROR; } } #endif /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if (!entry_id) return CURLRESOLV_ERROR; entry_len = strlen(entry_id); 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->dns.hostcache, entry_id, entry_len+1); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); /* free the allocated entry_id again */ free(entry_id); /* See whether the returned entry is stale. Deliberately done after the locked block */ if ( remove_entry_if_stale(data,dns) ) dns = NULL; /* the memory deallocation is being handled by the hash */ rc = CURLRESOLV_ERROR; /* default to failure */ if (!dns) { /* The entry was not in the cache. Resolve it to IP address */ Curl_addrinfo *addr; /* Check what IP specifics the app has requested and if we can provide it. * If not, bail out. */ if(!Curl_ipvalid(data)) return CURLRESOLV_ERROR; /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero value indicating that we need to wait for the response to the resolve call */ addr = Curl_getaddrinfo(conn, hostname, port, &wait); if (!addr) { if(wait) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ /* First, check that we haven't received the info by now */ result = Curl_is_resolved(conn, &dns); if(result) /* error detected */ return CURLRESOLV_ERROR; if(dns) rc = CURLRESOLV_RESOLVED; /* pointer provided */ else rc = CURLRESOLV_PENDING; /* no info yet */ } } else { if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ dns = Curl_cache_addr(data, addr, hostname, port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(addr); else rc = CURLRESOLV_RESOLVED; } } else { if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns->inuse++; /* we use it! */ if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); rc = CURLRESOLV_RESOLVED; } *entry = dns; return rc; }