Newer
Older
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
* $Id$
*****************************************************************************/
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#else
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h> /* required for free() prototypes */
#endif
#ifdef VMS
#include <inet.h>
#include <stdlib.h>
#endif
#endif
#include "urldata.h"
#include "sendf.h"
#include "hash.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
static curl_hash hostname_cache;
static int host_cache_initialized;
void Curl_global_host_cache_init(void)
if (!host_cache_initialized) {
curl_hash_init(&hostname_cache, 7, Curl_freeaddrinfo);
host_cache_initialized = 1;
}
curl_hash *Curl_global_host_cache_get(void)
return &hostname_cache;
void Curl_global_host_cache_dtor(void)
{
if (host_cache_initialized) {
curl_hash_clean(&hostname_cache);
Sterling Hughes
committed
struct curl_dns_cache_entry {
Curl_addrinfo *addr;
time_t timestamp;
Sterling Hughes
committed
};
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
static int _num_chars(int i)
{
int chars = 0;
do {
chars++;
i = (int) i / 10;
} while (i > 1);
return chars;
}
static char *
_create_hostcache_id(char *server, int server_len, int port)
{
char *id = NULL;
int id_len;
id_len = server_len + _num_chars(port);
id = malloc(id_len + 1);
if (!id) {
return NULL;
}
id_len = sprintf(id, "%s:%d", server, port);
if (id_len <= 0) {
free(id);
return NULL;
}
return id;
}
Curl_addrinfo *Curl_resolv(struct SessionHandle *data,
char *hostname,
int port,
char **bufp)
{
char *cache_id = NULL;
Sterling Hughes
committed
struct curl_dns_cache_entry *p = NULL;
size_t hostname_len;
time_t now;
/* 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);
}
hostname_len = strlen(hostname);
Sterling Hughes
committed
cache_id = _create_hostcache_id(hostname, hostname_len, port);
Sterling Hughes
committed
time(&now);
/* See if its already in our dns cache */
if (cache_id && curl_hash_find(data->hostcache, hostname, hostname_len+1, (void **) &p)) {
Sterling Hughes
committed
/* Do we need to check for a cache timeout? */
if (data->set.dns_cache_timeout != -1) {
/* Return if the entry has not timed out */
if ((now - p->timestamp) < data->set.dns_cache_timeout) {
return p->addr;
}
}
else {
return p->addr;
}
}
/* Create a new cache entry */
p = (struct curl_dns_cache_entry *)
malloc(sizeof(struct curl_dns_cache_entry));
if (!p)
Sterling Hughes
committed
return NULL;
p->addr = Curl_getaddrinfo(data, hostname, port, bufp);
if (!p->addr) {
Sterling Hughes
committed
}
p->timestamp = now;
/* Save it in our host cache */
curl_hash_update(data->hostcache, hostname, hostname_len+1, (const void *) p);
Sterling Hughes
committed
return p->addr;
/*
* This is a wrapper function for freeing name information in a protocol
* independent way. This takes care of using the appropriate underlaying
* proper function.
*/
void Curl_freeaddrinfo(void *freethis)
{
Sterling Hughes
committed
struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis;
#ifdef ENABLE_IPV6
Sterling Hughes
committed
freeaddrinfo(p->addr);
#else
Sterling Hughes
committed
free(p->addr);
#endif
Sterling Hughes
committed
free(p);
}
#ifdef MALLOCDEBUG
/* These two are strictly for memory tracing and are using the same
* style as the family otherwise present in memdebug.c. I put these ones
* here since they require a bunch of struct types I didn't wanna include
* in memdebug.c
*/
int curl_getaddrinfo(char *hostname, char *service,
struct addrinfo *hints,
struct addrinfo **result,
int line, const char *source)
{
int res=(getaddrinfo)(hostname, service, hints, result);
Daniel Stenberg
committed
if(0 == res) {
Daniel Stenberg
committed
if(logfile)
fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
source, line, (void *)*result);
Daniel Stenberg
committed
}
else {
if(logfile)
fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
source, line);
}
Daniel Stenberg
committed
void curl_freeaddrinfo(struct addrinfo *freethis,
int line, const char *source)
{
(freeaddrinfo)(freethis);
Daniel Stenberg
committed
if(logfile)
fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
source, line, (void *)freethis);
}
#endif
/*
* Return name information about the given hostname and port number. If
* successful, the 'addrinfo' is returned and the forth argument will point to
* 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)
{
struct addrinfo hints, *res;
int error;
char sbuf[NI_MAXSERV];
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
snprintf(sbuf, sizeof(sbuf), "%d", port);
error = getaddrinfo(hostname, sbuf, &hints, &res);
if (error) {
infof(data, "getaddrinfo(3) failed for %s\n", hostname);
*bufp=(char *)res; /* make it point to the result struct */
#else /* following code is IPv4-only */
#ifndef HAVE_GETHOSTBYNAME_R
Daniel Stenberg
committed
/**
Daniel Stenberg
committed
* Performs a "deep" copy of a hostent into a buffer (returns a pointer to the
* copy). Make absolutely sure the destination buffer is big enough!
Daniel Stenberg
committed
*
* Keith McGuigan
Daniel Stenberg
committed
* 10/3/2001 */
Daniel Stenberg
committed
static struct hostent* pack_hostent(char* buf, struct hostent* orig)
{
char* bufptr;
struct hostent* copy;
int i;
char* str;
int len;
bufptr = buf;
copy = (struct hostent*)bufptr;
bufptr += sizeof(struct hostent);
copy->h_name = bufptr;
len = strlen(orig->h_name) + 1;
strncpy(bufptr, orig->h_name, len);
bufptr += len;
Daniel Stenberg
committed
/* we align on even 64bit boundaries for safety */
#define MEMALIGN(x) (((unsigned long)(x)&0xfffffff8)+8)
/* This must be aligned properly to work on many CPU architectures! */
copy->h_aliases = (char**)MEMALIGN(bufptr);
Daniel Stenberg
committed
/* Figure out how many aliases there are */
for (i = 0; orig->h_aliases[i] != NULL; ++i);
/* Reserve room for the array */
bufptr += (i + 1) * sizeof(char*);
Daniel Stenberg
committed
/* Clone all known aliases */
for(i = 0; (str = orig->h_aliases[i]); i++) {
Daniel Stenberg
committed
len = strlen(str) + 1;
strncpy(bufptr, str, len);
copy->h_aliases[i] = bufptr;
bufptr += len;
}
Daniel Stenberg
committed
/* Terminate the alias list with a NULL */
Daniel Stenberg
committed
copy->h_aliases[i] = NULL;
copy->h_addrtype = orig->h_addrtype;
copy->h_length = orig->h_length;
Daniel Stenberg
committed
/* align it for (at least) 32bit accesses */
bufptr = (char *)MEMALIGN(bufptr);
Daniel Stenberg
committed
copy->h_addr_list = (char**)bufptr;
/* Figure out how many addresses there are */
for (i = 0; orig->h_addr_list[i] != NULL; ++i);
/* Reserve room for the array */
bufptr += (i + 1) * sizeof(char*);
i = 0;
len = orig->h_length;
str = orig->h_addr_list[i];
while (str != NULL) {
memcpy(bufptr, str, len);
copy->h_addr_list[i] = bufptr;
bufptr += len;
str = orig->h_addr_list[++i];
}
copy->h_addr_list[i] = NULL;
return copy;
}
#endif
Daniel Stenberg
committed
static char *MakeIP(unsigned long num,char *addr, int addr_len)
{
#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
struct in_addr in;
in.s_addr = htonl(num);
#if defined(HAVE_INET_NTOA_R)
inet_ntoa_r(in,addr,addr_len);
#else
strncpy(addr,inet_ntoa(in),addr_len);
#endif
#else
unsigned char *paddr;
num = htonl(num); /* htonl() added to avoid endian probs */
paddr = (unsigned char *)#
sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
#endif
return (addr);
}
/* 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. */
#ifndef INADDR_NONE
#define INADDR_NONE (unsigned long) ~0
#endif
Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data,
char *hostname,
int port,
char **bufp)
Daniel Stenberg
committed
in_addr_t in;
int ret; /* this variable is unused on several platforms but used on some */
#define CURL_NAMELOOKUP_SIZE 9000
/* Allocate enough memory to hold the full name information structs and
* everything. OSF1 is known to require at least 8872 bytes. The buffer
* required for storing all possible aliases and IP numbers is according to
* Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */
char *buf = (char *)malloc(CURL_NAMELOOKUP_SIZE);
if(!buf)
return NULL; /* major failure */
*bufp = buf;
Daniel Stenberg
committed
port=0; /* unused in IPv4 code */
ret = 0; /* to prevent the compiler warning */
struct in_addr *addrentry;
h = (struct hostent*)buf;
h->h_addr_list = (char**)(buf + sizeof(*h));
addrentry = (struct in_addr*)(h->h_addr_list + 2);
addrentry->s_addr = in;
h->h_addr_list[0] = (char*)addrentry;
h->h_addr_list[1] = NULL;
h->h_addrtype = AF_INET;
h->h_length = sizeof(*addrentry);
h->h_name = *(h->h_addr_list) + h->h_length;
/* bad one h->h_name = (char*)(h->h_addr_list + h->h_length); */
MakeIP(ntohl(in),h->h_name, CURL_NAMELOOKUP_SIZE - (long)(h->h_name) + (long)buf);
Daniel Stenberg
committed
#if defined(HAVE_GETHOSTBYNAME_R)
/* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
for some of these functions. */
memset(buf, 0, CURL_NAMELOOKUP_SIZE);
Daniel Stenberg
committed
#ifdef HAVE_GETHOSTBYNAME_R_5
/* Solaris, IRIX and more */
Daniel Stenberg
committed
(struct hostent *)buf,
buf + sizeof(struct hostent),
CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
Daniel Stenberg
committed
&h_errnop)) == NULL )
#endif
#ifdef HAVE_GETHOSTBYNAME_R_6
/* Linux */
if( gethostbyname_r(hostname,
(struct hostent *)buf,
buf + sizeof(struct hostent),
CURL_NAMELOOKUP_SIZE - sizeof(struct hostent),
Daniel Stenberg
committed
&h, /* DIFFERENCE */
&h_errnop))
#endif
#ifdef HAVE_GETHOSTBYNAME_R_3
/* AIX, Digital Unix, HPUX 10, more? */
Daniel Stenberg
committed
if(CURL_NAMELOOKUP_SIZE >=
(sizeof(struct hostent)+sizeof(struct hostent_data)))
Daniel Stenberg
committed
/* August 22nd, 2000: Albert Chin-A-Young brought an updated version
* that should work! September 20: Richard Prescott worked on the buffer
* size dilemma. */
ret = gethostbyname_r(hostname,
(struct hostent *)buf,
(struct hostent_data *)(buf + sizeof(struct hostent)));
else
ret = -1; /* failure, too smallish buffer size */
/* result expected in h */
h = (struct hostent*)buf;
h_errnop= errno; /* we don't deal with this, but set it anyway */
infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
h = NULL; /* set return code to NULL */
Daniel Stenberg
committed
*bufp=NULL;
}
#else
else {
if ((h = gethostbyname(hostname)) == NULL ) {
infof(data, "gethostbyname(2) failed for %s\n", hostname);
Daniel Stenberg
committed
free(buf);
*bufp=NULL;
Daniel Stenberg
committed
else
/* we make a copy of the hostent right now, right here, as the
static one we got a pointer to might get removed when we don't
want/expect that */
h = pack_hostent(buf, h);
#endif /* end of IPv4-specific code */
/*
* local variables:
* eval: (load-file "../curl-mode.el")
* end:
* vim600: fdm=marker
* vim: et sw=2 ts=2 sts=2 tw=78