summaryrefslogtreecommitdiffstats
path: root/src/lib/getaddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/getaddrinfo.c438
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 */