]> granicus.if.org Git - postgresql/blobdiff - src/port/getaddrinfo.c
Centralize single quote escaping in src/port/quotes.c
[postgresql] / src / port / getaddrinfo.c
index cc584a2209a85077a217941126929cfdaecb5242..e2b59efac86edbd144583f410ffbf711f5582596 100644 (file)
@@ -3,12 +3,20 @@
  * 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, PostgreSQL Global Development Group
+ * Windows may or may not have these routines, so we handle Windows specially
+ * by dynamically checking for their existence.  If they already exist, we
+ * use the Windows native routines, but if not, we use our own.
  *
  *
+ * Copyright (c) 2003-2013, PostgreSQL Global Development Group
+ *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/port/getaddrinfo.c,v 1.9 2003/06/23 23:52:00 momjian Exp $
+ *       src/port/getaddrinfo.c
  *
  *-------------------------------------------------------------------------
  */
 /* This is intended to be used in both frontend and backend, so use c.h */
 #include "c.h"
 
-#if !defined(_MSC_VER) && !defined(__BORLANDC__)
-#include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#ifdef HAVE_UNIX_SOCKETS
-#include <sys/un.h>
-#endif
-#endif
 
 #include "getaddrinfo.h"
+#include "libpq/pqcomm.h"              /* needed for struct sockaddr_storage */
+
+
+#ifdef WIN32
+/*
+ * The native routines may or may not exist on the Windows platform we are on,
+ * so we dynamically look up the routines, and call them via function pointers.
+ * Here we need to declare what the function pointers look like
+ */
+typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename,
+                                                                                                                const char *servname,
+                                                                                          const struct addrinfo * hints,
+                                                                                                        struct addrinfo ** res);
+
+typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai);
+
+typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa,
+                                                                                                                int salen,
+                                                                                                        char *host, int hostlen,
+                                                                                                        char *serv, int servlen,
+                                                                                                                int flags);
+
+/* static pointers to the native routines, so we only do the lookup once. */
+static getaddrinfo_ptr_t getaddrinfo_ptr = NULL;
+static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL;
+static getnameinfo_ptr_t getnameinfo_ptr = NULL;
+
+
+static bool
+haveNativeWindowsIPv6routines(void)
+{
+       void       *hLibrary = NULL;
+       static bool alreadyLookedForIpv6routines = false;
+
+       if (alreadyLookedForIpv6routines)
+               return (getaddrinfo_ptr != NULL);
+
+       /*
+        * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines
+        * are present in the WinSock 2 library (ws2_32.dll). Try that first
+        */
+
+       hLibrary = LoadLibraryA("ws2_32");
+
+       if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL)
+       {
+               /*
+                * Well, ws2_32 doesn't exist, or more likely doesn't have
+                * getaddrinfo.
+                */
+               if (hLibrary != NULL)
+                       FreeLibrary(hLibrary);
+
+               /*
+                * In Windows 2000, there was only the IPv6 Technology Preview look in
+                * the IPv6 WinSock library (wship6.dll).
+                */
+
+               hLibrary = LoadLibraryA("wship6");
+       }
+
+       /* If hLibrary is null, we couldn't find a dll with functions */
+       if (hLibrary != NULL)
+       {
+               /* We found a dll, so now get the addresses of the routines */
+
+               getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary,
+                                                                                                                        "getaddrinfo");
+               freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary,
+                                                                                                                        "freeaddrinfo");
+               getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary,
+                                                                                                                        "getnameinfo");
+
+               /*
+                * If any one of the routines is missing, let's play it safe and
+                * ignore them all
+                */
+               if (getaddrinfo_ptr == NULL ||
+                       freeaddrinfo_ptr == NULL ||
+                       getnameinfo_ptr == NULL)
+               {
+                       FreeLibrary(hLibrary);
+                       hLibrary = NULL;
+                       getaddrinfo_ptr = NULL;
+                       freeaddrinfo_ptr = NULL;
+                       getnameinfo_ptr = NULL;
+               }
+       }
+
+       alreadyLookedForIpv6routines = true;
+       return (getaddrinfo_ptr != NULL);
+}
+#endif
+
 
 /*
  * get address info for ipv4 sockets.
  */
 int
 getaddrinfo(const char *node, const char *service,
-                       const struct addrinfo *hintp,
-                       struct addrinfo **res)
+                       const struct addrinfo * hintp,
+                       struct addrinfo ** res)
 {
-       struct addrinfo         *ai;
-       struct sockaddr_in      sin, *psin;
-       struct addrinfo         hints;
+       struct addrinfo *ai;
+       struct sockaddr_in sin,
+                          *psin;
+       struct addrinfo hints;
+
+#ifdef WIN32
+
+       /*
+        * If Windows has native IPv6 support, use the native Windows routine.
+        * Otherwise, fall through and use our own code.
+        */
+       if (haveNativeWindowsIPv6routines())
+               return (*getaddrinfo_ptr) (node, service, hintp, res);
+#endif
 
-       if (hintp == NULL)      
+       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;
@@ -77,20 +182,19 @@ getaddrinfo(const char *node, const char *service,
                else if (hints.ai_flags & AI_NUMERICHOST)
                {
                        if (!inet_aton(node, &sin.sin_addr))
-                       {
                                return EAI_FAIL;
-                       }
                }
                else
                {
                        struct hostent *hp;
+
 #ifdef FRONTEND
                        struct hostent hpstr;
-                       char buf[BUFSIZ];
-                       int herrno = 0;
+                       char            buf[BUFSIZ];
+                       int                     herrno = 0;
 
                        pqGethostbyname(node, &hpstr, buf, sizeof(buf),
-                                       &hp, &herrno);
+                                                       &hp, &herrno);
 #else
                        hp = gethostbyname(node);
 #endif
@@ -124,15 +228,14 @@ getaddrinfo(const char *node, const char *service,
 
        if (service)
                sin.sin_port = htons((unsigned short) atoi(service));
-#if SALEN
-        sin.sin_len = sizeof(sin);
+
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+       sin.sin_len = sizeof(sin);
 #endif
 
        ai = malloc(sizeof(*ai));
        if (!ai)
-       {
                return EAI_MEMORY;
-       }
 
        psin = malloc(sizeof(*psin));
        if (!psin)
@@ -159,10 +262,23 @@ getaddrinfo(const char *node, const char *service,
 
 
 void
-freeaddrinfo(struct addrinfo *res)
+freeaddrinfo(struct addrinfo * res)
 {
        if (res)
        {
+#ifdef WIN32
+
+               /*
+                * If Windows has native IPv6 support, use the native Windows routine.
+                * Otherwise, fall through and use our own code.
+                */
+               if (haveNativeWindowsIPv6routines())
+               {
+                       (*freeaddrinfo_ptr) (res);
+                       return;
+               }
+#endif
+
                if (res->ai_addr)
                        free(res->ai_addr);
                free(res);
@@ -174,7 +290,7 @@ const char *
 gai_strerror(int errcode)
 {
 #ifdef HAVE_HSTRERROR
-       int hcode;
+       int                     hcode;
 
        switch (errcode)
        {
@@ -191,8 +307,7 @@ gai_strerror(int errcode)
        }
 
        return hstrerror(hcode);
-
-#else /* !HAVE_HSTRERROR */
+#else                                                  /* !HAVE_HSTRERROR */
 
        switch (errcode)
        {
@@ -200,86 +315,93 @@ gai_strerror(int errcode)
                        return "Unknown host";
                case EAI_AGAIN:
                        return "Host name lookup failure";
-               case EAI_FAIL:
+                       /* Errors below are probably WIN32 only */
+#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
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME            /* MSVC/WIN64 duplicate */
+               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 */
+#endif   /* HAVE_HSTRERROR */
 }
 
 /*
- * Convert an address to a hostname.
- * 
+ * Convert an ipv4 address to a hostname.
+ *
  * Bugs:       - Only supports NI_NUMERICHOST and NI_NUMERICSERV
  *               It will never resolv a hostname.
  *             - No IPv6 support.
  */
 int
-getnameinfo(const struct sockaddr *sa, int salen,
-               char *node, int nodelen,
-               char *service, int servicelen, int flags)
+getnameinfo(const struct sockaddr * sa, int salen,
+                       char *node, int nodelen,
+                       char *service, int servicelen, int flags)
 {
-       int             ret = -1;
+#ifdef WIN32
+
+       /*
+        * If Windows has native IPv6 support, use the native Windows routine.
+        * Otherwise, fall through and use our own code.
+        */
+       if (haveNativeWindowsIPv6routines())
+               return (*getnameinfo_ptr) (sa, salen, node, nodelen,
+                                                                  service, servicelen, flags);
+#endif
 
        /* Invalid arguments. */
        if (sa == NULL || (node == NULL && service == NULL))
-       {
-               return EAI_FAIL;
-       }
-
-       /* We don't support those. */
-       if ((node && !(flags & NI_NUMERICHOST))
-               || (service && !(flags & NI_NUMERICSERV)))
-       {
                return EAI_FAIL;
-       }
 
 #ifdef HAVE_IPV6
        if (sa->sa_family == AF_INET6)
-       {
-               return  EAI_FAMILY;
-       }
+               return EAI_FAMILY;
 #endif
 
-       if (service)
+       if (node)
        {
                if (sa->sa_family == AF_INET)
                {
-                       ret = snprintf(service, servicelen, "%d",
-                               ntohs(((struct sockaddr_in *)sa)->sin_port));
-               }
-#ifdef HAVE_UNIX_SOCKETS
-               else if (sa->sa_family == AF_UNIX)
-               {
-                       ret = snprintf(service, servicelen, "%s",
-                               ((struct sockaddr_un *)sa)->sun_path);
+                       if (inet_net_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr,
+                                                         sa->sa_family == AF_INET ? 32 : 128,
+                                                         node, nodelen) == NULL)
+                               return EAI_MEMORY;
                }
-#endif
-               if (ret == -1 || ret > servicelen)
-               {
+               else
                        return EAI_MEMORY;
-               }
        }
 
-       if (node)
+       if (service)
        {
+               int                     ret = -1;
+
                if (sa->sa_family == AF_INET)
                {
-                       char    *p;
-                       p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
-                       ret = snprintf(node, nodelen, "%s", p);
-               }
-#ifdef HAVE_UNIX_SOCKETS
-               else if (sa->sa_family == AF_UNIX)
-               {
-                       ret = snprintf(node, nodelen, "%s", "localhost");
+                       ret = snprintf(service, servicelen, "%d",
+                                                  ntohs(((struct sockaddr_in *) sa)->sin_port));
                }
-#endif
-               if (ret == -1 || ret > nodelen)
-               {
+               if (ret == -1 || ret > servicelen)
                        return EAI_MEMORY;
-               }
        }
 
        return 0;