123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- /*
- 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
- #ifndef HOST_NAME_MAX
- #define HOST_NAME_MAX 255
- #endif
- static int check_hostent_err(struct hostent *hp)
- {
- #ifndef INET6
- extern int h_errno;
- #endif
- 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((void *)&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 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(
- (void *)&((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 < (socklen_t)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;
- }
|