diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/getaddrinfo.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/src/lib/getaddrinfo.c b/src/lib/getaddrinfo.c new file mode 100644 index 0000000..2022362 --- /dev/null +++ b/src/lib/getaddrinfo.c @@ -0,0 +1,438 @@ +/* + * These functions are defined and used only if the configure + * cannot detect the standard getaddrinfo(), freeaddrinfo(), + * gai_strerror() and getnameinfo(). This avoids sprinkling of ifdefs. + * + * FIXME: getaddrinfo() & getnameinfo() should + * return all IPv4 addresses provided by DNS lookup. + */ + +RCSID("$Id$") + +#include <freeradius-devel/libradius.h> + +#include <ctype.h> +#include <sys/param.h> + +#ifndef HAVE_GETNAMEINFO +# undef LOCAL_GETHOSTBYNAMERSTYLE +# ifndef GETHOSTBYNAMERSTYLE +# define LOCAL_GETHOSTBYNAMERSTYLE 1 +#elif (GETHOSTBYNAMERSTYLE != SYSVSTYLE) && (GETHOSTBYNAMERSTYLE != GNUSTYLE) +# define LOCAL_GETHOSTBYNAMERSTYLE 1 +# endif /* GETHOSTBYNAMERSTYLE */ +#endif + +#ifndef HAVE_GETADDRINFO +# undef LOCAL_GETHOSTBYADDRR +# ifndef GETHOSTBYADDRRSTYLE +# define LOCAL_GETHOSTBYADDRR 1 +# elif (GETHOSTBYADDRRSTYLE != SYSVSTYLE) && (GETHOSTBYADDRRSTYLE != GNUSTYLE) +# define LOCAL_GETHOSTBYADDRR 1 +# endif /* GETHOSTBYADDRRSTYLE */ +#endif + +#ifdef HAVE_PTHREAD_H +# include <pthread.h> + +/* Thread safe DNS lookups */ +/* + * FIXME: There are some systems that use the same hostent + * structure to return for gethostbyname() & gethostbyaddr(), if + * that is the case then use only one mutex instead of separate + * mutexes + */ +# ifdef LOCAL_GETHOSTBYNAMERSTYLE +static int fr_hostbyname = 0; +static pthread_mutex_t fr_hostbyname_mutex; +# endif + +# ifdef LOCAL_GETHOSTBYNAMERSTYLE +static int fr_hostbyaddr = 0; +static pthread_mutex_t fr_hostbyaddr_mutex; +# endif + +#endif + +/* + * gethostbyaddr() & gethostbyname() return hostent structure + * To make these functions thread safe, we need to + * copy the data and not pointers + * + * struct hostent { + * char *h_name; * official name of host * + * char **h_aliases; * alias list * + * int h_addrtype; * host address type * + * int h_length; * length of address * + * char **h_addr_list; * list of addresses * + * } + * This struct contains 3 pointers as members. + * The data from these pointers is copied into a buffer. + * The buffer is formatted as below to store the data + * --------------------------------------------------------------- + * | h_name\0alias_array\0h_aliases\0..\0addr_array\0h_addr_list\0 | + * --------------------------------------------------------------- + */ +#if defined(LOCAL_GETHOSTBYNAMER) || defined(LOCAL_GETHOSTBYADDRR) +# define BUFFER_OVERFLOW 255 +static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, int buflen, int *error) +{ + int i, len; + char *ptr = buffer; + + *error = 0; + to->h_addrtype = from->h_addrtype; + to->h_length = from->h_length; + to->h_name = (char *)ptr; + + /* copy hostname to buffer */ + len = strlen(from->h_name) + 1; + strcpy(ptr, from->h_name); + ptr += len; + + /* copy aliases to buffer */ + to->h_aliases = (char**)ptr; + for (i = 0; from->h_aliases[i]; i++); + ptr += (i+1) * sizeof(char *); + + for (i = 0; from->h_aliases[i]; i++) { + len = strlen(from->h_aliases[i])+1; + if ((ptr-buffer) + len < buflen) { + to->h_aliases[i] = ptr; + strcpy(ptr, from->h_aliases[i]); + ptr += len; + } else { + *error = BUFFER_OVERFLOW; + return *error; + } + } + to->h_aliases[i] = NULL; + + /* copy addr_list to buffer */ + to->h_addr_list = (char**)ptr; + for (i = 0; (int *)from->h_addr_list[i] != 0; i++); + ptr += (i + 1) * sizeof(int *); + + for (i = 0; (int *)from->h_addr_list[i] != 0; i++) { + len = sizeof(int); + + if ((ptr-buffer)+len < buflen) { + to->h_addr_list[i] = ptr; + memcpy(ptr, from->h_addr_list[i], len); + ptr += len; + } else { + *error = BUFFER_OVERFLOW; + return *error; + } + } + to->h_addr_list[i] = 0; + return *error; +} +#endif /* (LOCAL_GETHOSTBYNAMER == 1) || (LOCAL_GETHOSTBYADDRR == 1) */ + +#ifdef LOCAL_GETHOSTBYNAMERSTYLE +static struct hostent * +gethostbyname_r(char const *hostname, struct hostent *result, + char *buffer, int buflen, int *error) +{ + struct hostent *hp; + +# ifdef HAVE_PTHREAD_H + if (fr_hostbyname == 0) { + pthread_mutex_init(&fr_hostbyname_mutex, NULL); + fr_hostbyname = 1; + } + pthread_mutex_lock(&fr_hostbyname_mutex); +# endif + + hp = gethostbyname(hostname); + if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) { + *error = h_errno; + hp = NULL; + } else { + copy_hostent(hp, result, buffer, buflen, error); + hp = result; + } + +# ifdef HAVE_PTHREAD_H + pthread_mutex_unlock(&fr_hostbyname_mutex); +# endif + + return hp; +} +#endif /* GETHOSTBYNAMERSTYLE */ + + +#ifdef LOCAL_GETHOSTBYADDRR +static struct hostent *gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result, + char *buffer, int buflen, int *error) +{ + struct hostent *hp; + +#ifdef HAVE_PTHREAD_H + if (fr_hostbyaddr == 0) { + pthread_mutex_init(&fr_hostbyaddr_mutex, NULL); + fr_hostbyaddr = 1; + } + pthread_mutex_lock(&fr_hostbyaddr_mutex); +#endif + + hp = gethostbyaddr(addr, len, type); + if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) { + *error = h_errno; + hp = NULL; + } else { + copy_hostent(hp, result, buffer, buflen, error); + hp = result; + } + +#ifdef HAVE_PTHREAD_H + pthread_mutex_unlock(&fr_hostbyaddr_mutex); +#endif + + return hp; +} +#endif /* GETHOSTBYADDRRSTYLE */ + +/* + * Mar 8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org> + * + * Below code is based on ssh-1.2.27-IPv6-1.5 written by + * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp> + */ + +#ifndef HAVE_GETADDRINFO +static struct addrinfo *malloc_ai(uint16_t port, u_long addr, int socktype, int proto) +{ + struct addrinfo *ai; + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + if (!ai) return NULL; + + memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + ai->ai_addr = (struct sockaddr *)(ai + 1); + ai->ai_addrlen = sizeof(struct sockaddr_in); +# ifdef HAVE_SOCKADDR_SA_LEN + ai->ai_addr->sa_len = sizeof(struct sockaddr_in); +# endif + ai->ai_addr->sa_family = ai->ai_family = AF_INET; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr; + ai->ai_socktype = socktype; + ai->ai_protocol = proto; + + return ai; +} + +char const *gai_strerror(int ecode) +{ + switch (ecode) { + case EAI_MEMORY: + return "memory allocation failure"; + + case EAI_FAMILY: + return "ai_family not supported"; + + case EAI_NONAME: + return "hostname nor servname provided, or not known"; + + case EAI_SERVICE: + return "servname not supported for ai_socktype"; + + default: + return "unknown error"; + } +} + +void freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + if (ai->ai_canonname) free(ai->ai_canonname); + + do { + next = ai->ai_next; + free(ai); + } while ((ai = next) != NULL); +} + +int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res) +{ + struct addrinfo *cur, *prev = NULL; + struct hostent *hp; + struct hostent result; + struct in_addr in; + int i, socktype, proto; + uint16_t port = 0; + int error; + char buffer[2048]; + + if (hints && (hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC)) return EAI_FAMILY; + + socktype = (hints && hints->ai_socktype) ? hints->ai_socktype : SOCK_STREAM; + if (hints && hints->ai_protocol) { + proto = hints->ai_protocol; + } else { + switch (socktype) { + case SOCK_DGRAM: + proto = IPPROTO_UDP; + break; + case SOCK_STREAM: + proto = IPPROTO_TCP; + break; + default: + proto = 0; + break; + } + } + + if (servname) { + if (isdigit((int)*servname)) { + port = htons(atoi(servname)); + } else { + struct servent *se; + char const *pe_proto; + + switch (socktype) { + case SOCK_DGRAM: + pe_proto = "udp"; + break; + + case SOCK_STREAM: + pe_proto = "tcp"; + break; + + default: + pe_proto = NULL; + break; + } + if ((se = getservbyname(servname, pe_proto)) == NULL) return EAI_SERVICE; + + port = se->s_port; + } + } + + if (!hostname) { + if (hints && hints->ai_flags & AI_PASSIVE) { + *res = malloc_ai(port, htonl(0x00000000), socktype, proto); + } else { + *res = malloc_ai(port, htonl(0x7f000001), socktype, proto); + } + if (!*res) return EAI_MEMORY; + + return 0; + } + + /* Numeric IP Address */ + if (inet_aton(hostname, &in)) { + *res = malloc_ai(port, in.s_addr, socktype, proto); + if (!*res) return EAI_MEMORY; + + return 0; + } + + if (hints && hints->ai_flags & AI_NUMERICHOST) return EAI_NONAME; + + /* DNS Lookup */ +#ifdef GETHOSTBYNAMERSTYLE +# if GETHOSTBYNAMERSTYLE == SYSVSTYLE + hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error); +# elif GETHOSTBYNAMERSTYLE == GNUSTYLE + if (gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &hp, &error) != 0) hp = NULL; +# else + hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error); +# endif +#else + hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error); +#endif + + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + for (i = 0; hp->h_addr_list[i]; i++) { + if ((cur = malloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr, + socktype, proto)) == NULL) { + if (*res) freeaddrinfo(*res); + return EAI_MEMORY; + } + + if (prev) { + prev->ai_next = cur; + } else { + *res = cur; + } + prev = cur; + } + + if (hints && hints->ai_flags & AI_CANONNAME && *res) { + if (((*res)->ai_canonname = strdup(hp->h_name)) == NULL) { + freeaddrinfo(*res); + return EAI_MEMORY; + } + } + return 0; + } + return EAI_NONAME; +} +#endif /* HAVE_GETADDRINFO */ + + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(struct sockaddr const *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, + unsigned int flags) +{ + const struct sockaddr_in *sin = (struct sockaddr_in const *)sa; + struct hostent *hp; + struct hostent result; + char tmpserv[16]; + char buffer[2048]; + int error; + + if (serv) { + snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); + if (strlen(tmpserv) > servlen) return EAI_MEMORY; + + strcpy(serv, tmpserv); + + if (host) { + if (flags & NI_NUMERICHOST) { + /* No Reverse DNS lookup */ + if (flags & NI_NAMEREQD) return EAI_NONAME; + if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY; + + strcpy(host, inet_ntoa(sin->sin_addr)); + return 0; + } else { + /* Reverse DNS lookup required */ +#ifdef GETHOSTBYADDRRSTYLE +# if GETHOSTBYADDRRSTYLE == SYSVSTYLE + hp = gethostbyaddr_r((char const *)&sin->sin_addr, + salen, AF_INET, &result, buffer, sizeof(buffer), &error); +# elif GETHOSTBYADDRRSTYLE == GNUSTYLE + if (gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET, + &result, buffer, sizeof(buffer), &hp, &error) != 0) { + hp = NULL; + } +# else + hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET, + &result, buffer, sizeof(buffer), &error); +# endif +#else + hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET, + &result, buffer, sizeof(buffer), &error); +#endif + if (hp) { + if (strlen(hp->h_name) >= hostlen) return EAI_MEMORY; + + strcpy(host, hp->h_name); + return 0; + } + + if (flags & NI_NAMEREQD) return EAI_NONAME; + if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY; + + strcpy(host, inet_ntoa(sin->sin_addr)); + return 0; + } + } + return 0; +} +#endif /* HAVE_GETNAMEINFO */ |