postgresql/src/port/getaddrinfo.c

279 lines
4.9 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* getaddrinfo.c
* Support getaddrinfo() on platforms that don't have it.
*
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/port/getaddrinfo.c,v 1.6 2003/06/12 08:11:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/* This is intended to be used in both frontend and backend, so use c.h */
#include "c.h"
#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
#include "getaddrinfo.h"
/*
* 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;
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 = htonl(INADDR_ANY);
else if (hints.ai_flags & AI_NUMERICHOST)
{
if (!inet_aton(node, &sin.sin_addr))
{
return EAI_FAIL;
}
}
else
{
struct hostent *hp;
hp = gethostbyname(node);
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 = htonl(INADDR_ANY);
else
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
if (service)
sin.sin_port = htons((unsigned short) atoi(service));
#if SALEN
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)
{
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";
case EAI_FAIL:
default:
return "Unknown server error";
}
#endif /* HAVE_HSTRERROR */
}
/*
* Convert an 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)
{
sa_family_t family;
int ret = -1;
/* 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;
}
family = sa->sa_family;
#ifdef HAVE_IPV6
if (family == AF_INET6)
{
return EAI_FAMILY;
}
#endif
if (service)
{
if (family == AF_INET)
{
ret = snprintf(service, servicelen, "%d",
ntohs(((struct sockaddr_in *)sa)->sin_port));
}
#ifdef HAVE_UNIX_SOCKETS
else if (family == AF_UNIX)
{
ret = snprintf(service, servicelen, "%s",
((struct sockaddr_un *)sa)->sun_path);
}
#endif
if (ret == -1 || ret > servicelen)
{
return EAI_MEMORY;
}
}
if (node)
{
if (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 (family == AF_UNIX)
{
ret = snprintf(node, nodelen, "%s", "localhost");
}
#endif
if (ret == -1 || ret > nodelen)
{
return EAI_MEMORY;
}
}
return 0;
}