A new version of the getaddrinfo code from the samba source.
authorWayne Davison <wayned@samba.org>
Fri, 2 Nov 2007 20:52:57 +0000 (20:52 +0000)
committerWayne Davison <wayned@samba.org>
Fri, 2 Nov 2007 20:52:57 +0000 (20:52 +0000)
lib/addrinfo.h [new file with mode: 0644]
lib/getaddrinfo.c [new file with mode: 0644]

diff --git a/lib/addrinfo.h b/lib/addrinfo.h
new file mode 100644 (file)
index 0000000..f3899e5
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.h
+ *       Support getaddrinfo() on platforms that don't have it.
+ *
+ * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO,
+ * whether or not the library routine getaddrinfo() can be found.  This
+ * policy is needed because on some platforms a manually installed libbind.a
+ * may provide getaddrinfo(), yet the system headers may not provide the
+ * struct definitions needed to call it.  To avoid conflict with the libbind
+ * definition in such cases, we rename our routines to pg_xxx() via macros.
+ *
+ * This code will also work on platforms where struct addrinfo is defined
+ * in the system headers but no getaddrinfo() can be located.
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ADDRINFO_H
+#define ADDRINFO_H
+
+
+/* Various macros that ought to be in <netdb.h>, but might not be */
+
+#ifndef EAI_FAIL
+#define EAI_BADFLAGS   (-1)
+#define EAI_NONAME             (-2)
+#define EAI_AGAIN              (-3)
+#define EAI_FAIL               (-4)
+#define EAI_FAMILY             (-6)
+#define EAI_SOCKTYPE   (-7)
+#define EAI_SERVICE            (-8)
+#define EAI_MEMORY             (-10)
+#define EAI_SYSTEM             (-11)
+#endif   /* !EAI_FAIL */
+
+#ifndef AI_PASSIVE
+#define AI_PASSIVE             0x0001
+#endif
+
+#ifndef AI_NUMERICHOST
+/*
+ * some platforms don't support AI_NUMERICHOST; define as zero if using
+ * the system version of getaddrinfo...
+ */
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICHOST 0
+#else
+#define AI_NUMERICHOST 0x0004
+#endif
+#endif
+
+#ifndef AI_CANONNAME
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_CANONNAME 0
+#else
+#define AI_CANONNAME 0x0008
+#endif
+#endif
+
+#ifndef AI_NUMERICSERV
+#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
+#define AI_NUMERICSERV 0
+#else
+#define AI_NUMERICSERV 0x0010
+#endif
+#endif
+
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+
+#ifndef NI_NUMERICSERV
+#define NI_NUMERICSERV 2
+#endif
+
+#ifndef NI_NOFQDN
+#define NI_NOFQDN      4
+#endif
+
+#ifndef NI_NAMEREQD
+#define NI_NAMEREQD    8
+#endif
+
+#ifndef NI_DGRAM
+#define NI_DGRAM       16
+#endif
+
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST     1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV     32
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+
+struct addrinfo
+{
+       int                     ai_flags;
+       int                     ai_family;
+       int                     ai_socktype;
+       int                     ai_protocol;
+       size_t          ai_addrlen;
+       struct sockaddr *ai_addr;
+       char       *ai_canonname;
+       struct addrinfo *ai_next;
+};
+#endif   /* HAVE_STRUCT_ADDRINFO */
+
+
+#ifndef HAVE_GETADDRINFO
+
+/* Rename private copies per comments above */
+#ifdef getaddrinfo
+#undef getaddrinfo
+#endif
+#define getaddrinfo pg_getaddrinfo
+
+#ifdef freeaddrinfo
+#undef freeaddrinfo
+#endif
+#define freeaddrinfo pg_freeaddrinfo
+
+#ifdef gai_strerror
+#undef gai_strerror
+#endif
+#define gai_strerror pg_gai_strerror
+
+#ifdef getnameinfo
+#undef getnameinfo
+#endif
+#define getnameinfo pg_getnameinfo
+
+extern int getaddrinfo(const char *node, const char *service,
+                       const struct addrinfo * hints, struct addrinfo ** res);
+extern void freeaddrinfo(struct addrinfo * res);
+extern const char *gai_strerror(int errcode);
+extern int getnameinfo(const struct sockaddr * sa, socklen_t salen,
+                       char *node, size_t nodelen,
+                       char *service, size_t servicelen, int flags);
+#endif   /* HAVE_GETADDRINFO */
+
+#endif   /* ADDRINFO_H */
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
new file mode 100644 (file)
index 0000000..3e81557
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this paragraph
+and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+*/
+
+/*-------------------------------------------------------------------------
+ *
+ * getaddrinfo.c
+ *       Support getaddrinfo() on platforms that don't have it.
+ *
+ * We also supply getnameinfo() here, assuming that the platform will have
+ * it if and only if it has getaddrinfo().     If this proves false on some
+ * platform, we'll need to split this file and provide a separate configure
+ * test for getnameinfo().
+ *
+ * Copyright (c) 2003-2007, PostgreSQL Global Development Group
+ *
+ * Copyright (C) 2007 Jeremy Allison.
+ * Modified to return multiple IPv4 addresses for Samba.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "rsync.h"
+
+#ifndef SMB_MALLOC
+#define SMB_MALLOC(s) malloc(s)
+#endif
+
+#ifndef SMB_STRDUP
+#define SMB_STRDUP(s) strdup(s)
+#endif
+
+static int check_hostent_err(struct hostent *hp)
+{
+       if (!hp) {
+               switch (h_errno) {
+                       case HOST_NOT_FOUND:
+                       case NO_DATA:
+                               return EAI_NONAME;
+                       case TRY_AGAIN:
+                               return EAI_AGAIN;
+                       case NO_RECOVERY:
+                       default:
+                               return EAI_FAIL;
+               }
+       }
+       if (!hp->h_name || hp->h_addrtype != AF_INET) {
+               return EAI_FAIL;
+       }
+       return 0;
+}
+
+static char *canon_name_from_hostent(struct hostent *hp,
+                               int *perr)
+{
+       char *ret = NULL;
+
+       *perr = check_hostent_err(hp);
+       if (*perr) {
+               return NULL;
+       }
+       ret = SMB_STRDUP(hp->h_name);
+       if (!ret) {
+               *perr = EAI_MEMORY;
+       }
+       return ret;
+}
+
+static char *get_my_canon_name(int *perr)
+{
+       char name[HOST_NAME_MAX+1];
+
+       if (gethostname(name, HOST_NAME_MAX) == -1) {
+               *perr = EAI_FAIL;
+               return NULL;
+       }
+       /* Ensure null termination. */
+       name[HOST_NAME_MAX] = '\0';
+       return canon_name_from_hostent(gethostbyname(name), perr);
+}
+
+static char *get_canon_name_from_addr(struct in_addr ip,
+                               int *perr)
+{
+       return canon_name_from_hostent(
+                       gethostbyaddr(&ip, sizeof(ip), AF_INET),
+                       perr);
+}
+
+static struct addrinfo *alloc_entry(const struct addrinfo *hints,
+                               struct in_addr ip,
+                               unsigned short port)
+{
+       struct sockaddr_in *psin = NULL;
+       struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
+
+       if (!ai) {
+               return NULL;
+       }
+       memset(ai, '\0', sizeof(*ai));
+
+       psin = SMB_MALLOC(sizeof(*psin));
+       if (!psin) {
+               free(ai);
+               return NULL;
+       }
+
+       memset(psin, '\0', sizeof(*psin));
+
+       psin->sin_family = AF_INET;
+       psin->sin_port = htons(port);
+       psin->sin_addr = ip;
+
+       ai->ai_flags = 0;
+       ai->ai_family = AF_INET;
+       ai->ai_socktype = hints->ai_socktype;
+       ai->ai_protocol = hints->ai_protocol;
+       ai->ai_addrlen = sizeof(*psin);
+       ai->ai_addr = (struct sockaddr *) psin;
+       ai->ai_canonname = NULL;
+       ai->ai_next = NULL;
+
+       return ai;
+}
+
+/*
+ * get address info for a single ipv4 address.
+ *
+ *     Bugs:   - servname can only be a number, not text.
+ */
+
+static int getaddr_info_single_addr(const char *service,
+                               uint32_t addr,
+                               const struct addrinfo *hints,
+                               struct addrinfo **res)
+{
+
+       struct addrinfo *ai = NULL;
+       struct in_addr ip;
+       unsigned short port = 0;
+
+       if (service) {
+               port = (unsigned short)atoi(service);
+       }
+       ip.s_addr = htonl(addr);
+
+       ai = alloc_entry(hints, ip, port);
+       if (!ai) {
+               return EAI_MEMORY;
+       }
+
+       /* If we're asked for the canonical name,
+        * make sure it returns correctly. */
+       if (!(hints->ai_flags & AI_NUMERICSERV) &&
+                       hints->ai_flags & AI_CANONNAME) {
+               int err;
+               if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
+                       ai->ai_canonname = get_my_canon_name(&err);
+               } else {
+                       ai->ai_canonname =
+                       get_canon_name_from_addr(ip,&err);
+               }
+               if (ai->ai_canonname == NULL) {
+                       freeaddrinfo(ai);
+                       return err;
+               }
+       }
+
+       *res = ai;
+       return 0;
+}
+
+/*
+ * get address info for multiple ipv4 addresses.
+ *
+ *     Bugs:   - servname can only be a number, not text.
+ */
+
+static int getaddr_info_name(const char *node,
+                               const char *service,
+                               const struct addrinfo *hints,
+                               struct addrinfo **res)
+{
+       struct addrinfo *listp = NULL, *prevp = NULL;
+       char **pptr = NULL;
+       int err;
+       struct hostent *hp = NULL;
+       unsigned short port = 0;
+
+       if (service) {
+               port = (unsigned short)atoi(service);
+       }
+
+       hp = gethostbyname(node);
+       err = check_hostent_err(hp);
+       if (err) {
+               return err;
+       }
+
+       for(pptr = hp->h_addr_list; *pptr; pptr++) {
+               struct in_addr ip = *(struct in_addr *)*pptr;
+               struct addrinfo *ai = alloc_entry(hints, ip, port);
+
+               if (!ai) {
+                       freeaddrinfo(listp);
+                       return EAI_MEMORY;
+               }
+
+               if (!listp) {
+                       listp = ai;
+                       prevp = ai;
+                       ai->ai_canonname = SMB_STRDUP(hp->h_name);
+                       if (!ai->ai_canonname) {
+                               freeaddrinfo(listp);
+                               return EAI_MEMORY;
+                       }
+               } else {
+                       prevp->ai_next = ai;
+                       prevp = ai;
+               }
+       }
+       *res = listp;
+       return 0;
+}
+
+/*
+ * get address info for ipv4 sockets.
+ *
+ *     Bugs:   - servname can only be a number, not text.
+ */
+
+int getaddrinfo(const char *node,
+               const char *service,
+               const struct addrinfo * hintp,
+               struct addrinfo ** res)
+{
+       struct addrinfo hints;
+
+       /* Setup the hints struct. */
+       if (hintp == NULL) {
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_family = AF_INET;
+               hints.ai_socktype = SOCK_STREAM;
+       } else {
+               memcpy(&hints, hintp, sizeof(hints));
+       }
+
+       if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
+               return EAI_FAMILY;
+       }
+
+       if (hints.ai_socktype == 0) {
+               hints.ai_socktype = SOCK_STREAM;
+       }
+
+       if (!node && !service) {
+               return EAI_NONAME;
+       }
+
+       if (node) {
+               if (node[0] == '\0') {
+                       return getaddr_info_single_addr(service,
+                                       INADDR_ANY,
+                                       &hints,
+                                       res);
+               } else if (hints.ai_flags & AI_NUMERICHOST) {
+                       struct in_addr ip;
+                       if (!inet_aton(node, &ip)) {
+                               return EAI_FAIL;
+                       }
+                       return getaddr_info_single_addr(service,
+                                       ntohl(ip.s_addr),
+                                       &hints,
+                                       res);
+               } else {
+                       return getaddr_info_name(node,
+                                               service,
+                                               &hints,
+                                               res);
+               }
+       } else if (hints.ai_flags & AI_PASSIVE) {
+               return getaddr_info_single_addr(service,
+                                       INADDR_ANY,
+                                       &hints,
+                                       res);
+       }
+       return getaddr_info_single_addr(service,
+                                       INADDR_LOOPBACK,
+                                       &hints,
+                                       res);
+}
+
+
+void freeaddrinfo(struct addrinfo *res)
+{
+       struct addrinfo *next = NULL;
+
+       for (;res; res = next) {
+               next = res->ai_next;
+               if (res->ai_canonname) {
+                       free(res->ai_canonname);
+               }
+               if (res->ai_addr) {
+                       free(res->ai_addr);
+               }
+               free(res);
+       }
+}
+
+
+const char *gai_strerror(int errcode)
+{
+#ifdef HAVE_HSTRERROR
+       int                     hcode;
+
+       switch (errcode)
+       {
+               case EAI_NONAME:
+                       hcode = HOST_NOT_FOUND;
+                       break;
+               case EAI_AGAIN:
+                       hcode = TRY_AGAIN;
+                       break;
+               case EAI_FAIL:
+               default:
+                       hcode = NO_RECOVERY;
+                       break;
+       }
+
+       return hstrerror(hcode);
+#else                                                  /* !HAVE_HSTRERROR */
+
+       switch (errcode)
+       {
+               case EAI_NONAME:
+                       return "Unknown host";
+               case EAI_AGAIN:
+                       return "Host name lookup failure";
+#ifdef EAI_BADFLAGS
+               case EAI_BADFLAGS:
+                       return "Invalid argument";
+#endif
+#ifdef EAI_FAMILY
+               case EAI_FAMILY:
+                       return "Address family not supported";
+#endif
+#ifdef EAI_MEMORY
+               case EAI_MEMORY:
+                       return "Not enough memory";
+#endif
+#ifdef EAI_NODATA
+               case EAI_NODATA:
+                       return "No host data of that type was found";
+#endif
+#ifdef EAI_SERVICE
+               case EAI_SERVICE:
+                       return "Class type not found";
+#endif
+#ifdef EAI_SOCKTYPE
+               case EAI_SOCKTYPE:
+                       return "Socket type not supported";
+#endif
+               default:
+                       return "Unknown server error";
+       }
+#endif   /* HAVE_HSTRERROR */
+}
+
+static int gethostnameinfo(const struct sockaddr *sa,
+                       char *node,
+                       size_t nodelen,
+                       int flags)
+{
+       int ret = -1;
+       char *p = NULL;
+
+       if (!(flags & NI_NUMERICHOST)) {
+               struct hostent *hp = gethostbyaddr(
+                               &((struct sockaddr_in *)sa)->sin_addr,
+                               sizeof(struct in_addr),
+                               sa->sa_family);
+               ret = check_hostent_err(hp);
+               if (ret == 0) {
+                       /* Name looked up successfully. */
+                       ret = snprintf(node, nodelen, "%s", hp->h_name);
+                       if (ret < 0 || (size_t)ret >= nodelen) {
+                               return EAI_MEMORY;
+                       }
+                       if (flags & NI_NOFQDN) {
+                               p = strchr(node,'.');
+                               if (p) {
+                                       *p = '\0';
+                               }
+                       }
+                       return 0;
+               }
+
+               if (flags & NI_NAMEREQD) {
+                       /* If we require a name and didn't get one,
+                        * automatically fail. */
+                       return ret;
+               }
+               /* Otherwise just fall into the numeric host code... */
+       }
+       p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
+       ret = snprintf(node, nodelen, "%s", p);
+       if (ret < 0 || (size_t)ret >= nodelen) {
+               return EAI_MEMORY;
+       }
+       return 0;
+}
+
+static int getservicenameinfo(const struct sockaddr *sa,
+                       char *service,
+                       size_t servicelen,
+                       int flags)
+{
+       int ret = -1;
+       int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+
+       if (!(flags & NI_NUMERICSERV)) {
+               struct servent *se = getservbyport(
+                               port,
+                               (flags & NI_DGRAM) ? "udp" : "tcp");
+               if (se && se->s_name) {
+                       /* Service name looked up successfully. */
+                       ret = snprintf(service, servicelen, "%s", se->s_name);
+                       if (ret < 0 || (size_t)ret >= servicelen) {
+                               return EAI_MEMORY;
+                       }
+                       return 0;
+               }
+               /* Otherwise just fall into the numeric service code... */
+       }
+       ret = snprintf(service, servicelen, "%d", port);
+       if (ret < 0 || (size_t)ret >= servicelen) {
+               return EAI_MEMORY;
+       }
+       return 0;
+}
+
+/*
+ * Convert an ipv4 address to a hostname.
+ *
+ * Bugs:       - No IPv6 support.
+ */
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+                       char *node, size_t nodelen,
+                       char *service, size_t servicelen, int flags)
+{
+
+       /* Invalid arguments. */
+       if (sa == NULL || (node == NULL && service == NULL)) {
+               return EAI_FAIL;
+       }
+
+       if (sa->sa_family != AF_INET) {
+               return EAI_FAIL;
+       }
+
+       if (salen < sizeof(struct sockaddr_in)) {
+               return EAI_FAIL;
+       }
+
+       /* We don't support those. */
+       if ((node && !(flags & NI_NUMERICHOST))
+               || (service && !(flags & NI_NUMERICSERV)))
+               return EAI_FAIL;
+
+       if (node) {
+               return gethostnameinfo(sa, node, nodelen, flags);
+       }
+
+       if (service) {
+               return getservicenameinfo(sa, service, servicelen, flags);
+       }
+       return 0;
+}