/*------------------------------------------------------------------------- * * 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 #include #include #include #include #ifdef HAVE_UNIX_SOCKETS #include #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; }