diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
commit | 6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch) | |
tree | 657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/port/getaddrinfo.c | |
parent | Initial commit. (diff) | |
download | postgresql-13-upstream/13.4.tar.xz postgresql-13-upstream/13.4.zip |
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/port/getaddrinfo.c')
-rw-r--r-- | src/port/getaddrinfo.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/port/getaddrinfo.c b/src/port/getaddrinfo.c new file mode 100644 index 0000000..3b51eea --- /dev/null +++ b/src/port/getaddrinfo.c @@ -0,0 +1,396 @@ +/*------------------------------------------------------------------------- + * + * 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(). + * + * 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-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/getaddrinfo.c + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" /* needed for struct sockaddr_storage */ +#include "port/pg_bswap.h" + + +#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 *node, int nodelen, + char *service, int servicelen, + 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 later versions, the IPv6 routines are present in the + * WinSock 2 library (ws2_32.dll). + */ + hLibrary = LoadLibraryA("ws2_32"); + + /* 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. + * + * Bugs: - only one addrinfo is set even though hintp is NULL or + * ai_socktype is 0 + * - AI_CANONNAME is not supported. + * - 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 *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) + { + 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; + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + + if (node) + { + if (node[0] == '\0') + sin.sin_addr.s_addr = pg_hton32(INADDR_ANY); + else if (hints.ai_flags & AI_NUMERICHOST) + { + if (!inet_aton(node, &sin.sin_addr)) + return EAI_NONAME; + } + else + { + struct hostent *hp; + +#ifdef FRONTEND + struct hostent hpstr; + char buf[BUFSIZ]; + int herrno = 0; + + pqGethostbyname(node, &hpstr, buf, sizeof(buf), + &hp, &herrno); +#else + hp = gethostbyname(node); +#endif + if (hp == NULL) + { + 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_addrtype != AF_INET) + return EAI_FAIL; + + memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length); + } + } + else + { + if (hints.ai_flags & AI_PASSIVE) + sin.sin_addr.s_addr = pg_hton32(INADDR_ANY); + else + sin.sin_addr.s_addr = pg_hton32(INADDR_LOOPBACK); + } + + if (service) + sin.sin_port = pg_hton16((unsigned short) atoi(service)); + +#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) + { + free(ai); + return EAI_MEMORY; + } + + memcpy(psin, &sin, sizeof(*psin)); + + 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; + + *res = ai; + + return 0; +} + + +void +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); + } +} + + +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"; + /* 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 */ +} + +/* + * Convert an ipv4 address to a hostname. + * + * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV behavior. + * It will never resolve a hostname. + * - No IPv6 support. + */ +int +getnameinfo(const struct sockaddr *sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags) +{ +#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; + +#ifdef HAVE_IPV6 + if (sa->sa_family == AF_INET6) + return EAI_FAMILY; +#endif + + /* Unsupported flags. */ + if (flags & NI_NAMEREQD) + return EAI_AGAIN; + + if (node) + { + if (sa->sa_family == AF_INET) + { + if (pg_inet_net_ntop(AF_INET, + &((struct sockaddr_in *) sa)->sin_addr, + sa->sa_family == AF_INET ? 32 : 128, + node, nodelen) == NULL) + return EAI_MEMORY; + } + else + return EAI_MEMORY; + } + + if (service) + { + int ret = -1; + + if (sa->sa_family == AF_INET) + { + ret = snprintf(service, servicelen, "%d", + pg_ntoh16(((struct sockaddr_in *) sa)->sin_port)); + } + if (ret < 0 || ret >= servicelen) + return EAI_MEMORY; + } + + return 0; +} |