/* Destroy resolver thread synchronization data */ static void destroy_thread_sync_data(struct thread_sync_data * tsd) { if(tsd->mtx) { Curl_mutex_destroy(tsd->mtx); free(tsd->mtx); } if(tsd->hostname) free(tsd->hostname); if(tsd->res) Curl_freeaddrinfo(tsd->res); memset(tsd,0,sizeof(*tsd)); }
/* * destroy_async_data() cleans up async resolver data. */ static void destroy_async_data(struct Curl_async *async) { free(async->hostname); if(async->os_specific) { struct ResolverResults *res = (struct ResolverResults *)async->os_specific; if(res) { if(res->temp_ai) { Curl_freeaddrinfo(res->temp_ai); res->temp_ai = NULL; } free(res); } async->os_specific = NULL; } async->hostname = NULL; }
/* * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been * made, the struct may be destroyed due to pruning. It is important that only * one unlock is made for each Curl_resolv() call. */ void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) { DEBUGASSERT(dns && (dns->inuse>0)); if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns->inuse--; /* only free if nobody is using AND it is not in hostcache (timestamp == 0) */ if (dns->inuse == 0 && dns->timestamp == 0) { Curl_freeaddrinfo(dns->addr); free(dns); } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); }
/* * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread() * or getaddrinfo_thread() when we got the name resolved (or not!). * * If the status argument is CURL_ASYNC_SUCCESS, this function takes * ownership of the Curl_addrinfo passed, storing the resolved data * in the DNS cache. * * The storage operation locks and unlocks the DNS cache. */ CURLcode Curl_addrinfo_callback(struct connectdata *conn, int status, struct Curl_addrinfo *ai) { struct Curl_dns_entry *dns = NULL; CURLcode result = CURLE_OK; conn->async.status = status; if(CURL_ASYNC_SUCCESS == status) { if(ai) { struct SessionHandle *data = conn->data; if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = Curl_cache_addr(data, ai, conn->async.hostname, conn->async.port); if(!dns) { /* failed to store, cleanup and return error */ Curl_freeaddrinfo(ai); result = CURLE_OUT_OF_MEMORY; } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } else { result = CURLE_OUT_OF_MEMORY; } } conn->async.dns = dns; /* Set async.done TRUE last in this function since it may be used multi- threaded and once this is TRUE the other thread may read fields from the async struct */ conn->async.done = TRUE; /* IPv4: The input hostent struct will be freed by ares when we return from this function */ return result; }
/* 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; }
CURLcode Curl_doh_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dnsp) { struct Curl_easy *data = conn->data; *dnsp = NULL; /* defaults to no response */ if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) { failf(data, "Could not DOH-resolve: %s", conn->async.hostname); return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else if(!data->req.doh.pending) { DOHcode rc; DOHcode rc2; struct dohentry de; struct Curl_dns_entry *dns; struct Curl_addrinfo *ai; /* remove DOH handles from multi handle and close them */ curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy); Curl_close(data->req.doh.probe[0].easy); curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy); Curl_close(data->req.doh.probe[1].easy); /* parse the responses, create the struct and return it! */ init_dohentry(&de); rc = doh_decode(data->req.doh.probe[0].serverdoh.memory, data->req.doh.probe[0].serverdoh.size, data->req.doh.probe[0].dnstype, &de); free(data->req.doh.probe[0].serverdoh.memory); if(rc) { infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc), type2name(data->req.doh.probe[0].dnstype), data->req.doh.host); } rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory, data->req.doh.probe[1].serverdoh.size, data->req.doh.probe[1].dnstype, &de); free(data->req.doh.probe[1].serverdoh.memory); if(rc2) { infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2), type2name(data->req.doh.probe[1].dnstype), data->req.doh.host); } if(!rc || !rc2) { infof(data, "DOH Host name: %s\n", data->req.doh.host); showdoh(data, &de); ai = doh2ai(&de, data->req.doh.host, data->req.doh.port); if(!ai) { de_cleanup(&de); return CURLE_OUT_OF_MEMORY; } 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, ai, data->req.doh.host, data->req.doh.port); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); de_cleanup(&de); if(!dns) /* returned failure, bail out nicely */ Curl_freeaddrinfo(ai); else { conn->async.dns = dns; *dnsp = dns; return CURLE_OK; } } de_cleanup(&de); return CURLE_COULDNT_RESOLVE_HOST; } return CURLE_OK; }
static Curl_addrinfo * doh2ai(const struct dohentry *de, const char *hostname, int port) { Curl_addrinfo *ai; Curl_addrinfo *prevai = NULL; Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 *addr6; #endif CURLcode result = CURLE_OK; int i; if(!de) /* no input == no output! */ return NULL; for(i = 0; i < de->numaddr; i++) { size_t ss_size; CURL_SA_FAMILY_T addrtype; if(de->addr[i].type == DNS_TYPE_AAAA) { #ifndef ENABLE_IPV6 /* we can't handle IPv6 addresses */ continue; #else ss_size = sizeof(struct sockaddr_in6); addrtype = AF_INET6; #endif } else { ss_size = sizeof(struct sockaddr_in); addrtype = AF_INET; } ai = calloc(1, sizeof(Curl_addrinfo)); if(!ai) { result = CURLE_OUT_OF_MEMORY; break; } ai->ai_canonname = strdup(hostname); if(!ai->ai_canonname) { result = CURLE_OUT_OF_MEMORY; free(ai); break; } ai->ai_addr = calloc(1, ss_size); if(!ai->ai_addr) { result = CURLE_OUT_OF_MEMORY; free(ai->ai_canonname); free(ai); break; } if(!firstai) /* store the pointer we want to return from this function */ firstai = ai; if(prevai) /* make the previous entry point to this */ prevai->ai_next = ai; ai->ai_family = addrtype; /* we return all names as STREAM, so when using this address for TFTP the type must be ignored and conn->socktype be used instead! */ ai->ai_socktype = SOCK_STREAM; ai->ai_addrlen = (curl_socklen_t)ss_size; /* leave the rest of the struct filled with zero */ switch(ai->ai_family) { case AF_INET: addr = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); addr->sin_family = (CURL_SA_FAMILY_T)addrtype; addr->sin_port = htons((unsigned short)port); break; #ifdef ENABLE_IPV6 case AF_INET6: addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; addr6->sin6_port = htons((unsigned short)port); break; #endif } prevai = ai; } if(result) { Curl_freeaddrinfo(firstai); firstai = NULL; } return firstai; }
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; }
int Curl_resolv(struct connectdata *conn, const char *hostname, int port, struct Curl_dns_entry **entry) { struct Curl_dns_entry *dns = NULL; struct SessionHandle *data = conn->data; CURLcode result; int rc = CURLRESOLV_ERROR; /* default to failure */ *entry = NULL; if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); dns = fetch_addr(conn, hostname, port); if(dns) { infof(data, "Hostname %s was found in DNS cache\n", hostname); 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_resolver_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] == '-') { /* 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; }
Curl_addrinfo * Curl_he2ai(const struct hostent *he, int port) { Curl_addrinfo *ai; Curl_addrinfo *prevai = NULL; Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 *addr6; #endif CURLcode result = CURLE_OK; int i; char *curr; if(!he) /* no input == no output! */ return NULL; DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); for(i=0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; #ifdef ENABLE_IPV6 if(he->h_addrtype == AF_INET6) ss_size = sizeof (struct sockaddr_in6); else #endif ss_size = sizeof (struct sockaddr_in); if((ai = calloc(1, sizeof(Curl_addrinfo))) == NULL) { result = CURLE_OUT_OF_MEMORY; break; } if((ai->ai_canonname = strdup(he->h_name)) == NULL) { result = CURLE_OUT_OF_MEMORY; free(ai); break; } if((ai->ai_addr = calloc(1, ss_size)) == NULL) { result = CURLE_OUT_OF_MEMORY; free(ai->ai_canonname); free(ai); break; } if(!firstai) /* store the pointer we want to return from this function */ firstai = ai; if(prevai) /* make the previous entry point to this */ prevai->ai_next = ai; ai->ai_family = he->h_addrtype; /* we return all names as STREAM, so when using this address for TFTP the type must be ignored and conn->socktype be used instead! */ ai->ai_socktype = SOCK_STREAM; ai->ai_addrlen = (curl_socklen_t)ss_size; /* leave the rest of the struct filled with zero */ switch (ai->ai_family) { case AF_INET: addr = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr->sin_addr, curr, sizeof(struct in_addr)); addr->sin_family = (unsigned short)(he->h_addrtype); addr->sin_port = htons((unsigned short)port); break; #ifdef ENABLE_IPV6 case AF_INET6: addr6 = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr)); addr6->sin6_family = (unsigned short)(he->h_addrtype); addr6->sin6_port = htons((unsigned short)port); break; #endif } prevai = ai; } if(result != CURLE_OK) { Curl_freeaddrinfo(firstai); firstai = NULL; } return firstai; }
int Curl_getaddrinfo_ex(const char *nodename, const char *servname, const struct addrinfo *hints, Curl_addrinfo **result) { const struct addrinfo *ai; struct addrinfo *aihead; Curl_addrinfo *cafirst = NULL; Curl_addrinfo *calast = NULL; Curl_addrinfo *ca; size_t ss_size; int error; *result = NULL; /* assume failure */ error = getaddrinfo(nodename, servname, hints, &aihead); if(error) return error; /* traverse the addrinfo list */ for(ai = aihead; ai != NULL; ai = ai->ai_next) { /* ignore elements with unsupported address family, */ /* settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); #ifdef ENABLE_IPV6 else if(ai->ai_family == AF_INET6) ss_size = sizeof(struct sockaddr_in6); #endif else continue; /* ignore elements without required address info */ if((ai->ai_addr == NULL) || !(ai->ai_addrlen > 0)) continue; /* ignore elements with bogus address size */ if((size_t)ai->ai_addrlen < ss_size) continue; if((ca = malloc(sizeof(Curl_addrinfo))) == NULL) { error = EAI_MEMORY; break; } /* copy each structure member individually, member ordering, */ /* size, or padding might be different for each platform. */ ca->ai_flags = ai->ai_flags; ca->ai_family = ai->ai_family; ca->ai_socktype = ai->ai_socktype; ca->ai_protocol = ai->ai_protocol; ca->ai_addrlen = (curl_socklen_t)ss_size; ca->ai_addr = NULL; ca->ai_canonname = NULL; ca->ai_next = NULL; if((ca->ai_addr = malloc(ss_size)) == NULL) { error = EAI_MEMORY; free(ca); break; } memcpy(ca->ai_addr, ai->ai_addr, ss_size); if(ai->ai_canonname != NULL) { if((ca->ai_canonname = strdup(ai->ai_canonname)) == NULL) { error = EAI_MEMORY; free(ca->ai_addr); free(ca); break; } } /* if the return list is empty, this becomes the first element */ if(!cafirst) cafirst = ca; /* add this element last in the return list */ if(calast) calast->ai_next = ca; calast = ca; } /* destroy the addrinfo list */ if(aihead) freeaddrinfo(aihead); /* if we failed, also destroy the Curl_addrinfo list */ if(error) { Curl_freeaddrinfo(cafirst); cafirst = NULL; } else if(!cafirst) { #ifdef EAI_NONAME /* rfc3493 conformant */ error = EAI_NONAME; #else /* rfc3493 obsoleted */ error = EAI_NODATA; #endif #ifdef USE_WINSOCK SET_SOCKERRNO(error); #endif } *result = cafirst; /* This is not a CURLcode */ return error; }
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; }