/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* * Shamelessly duplicated from the fetchmail public sources * for use by the Squid Project under GNU Public License. * * Update/Maintenance History: * * 15-Aug-2007 : Copied from fetchmail 6.3.8 * - added protection around library headers * * 16-Aug-2007 : Altered configure checks * Un-hacked slightly to use system gethostbyname() * * 06-Oct-2007 : Various fixes to allow the build on MinGW * * 13-Jan-2015 : Various fixed for C++ and MinGW native build * * Original License and code follows. */ #include "squid.h" /* * This file is part of libESMTP, a library for submission of RFC 2822 * formatted electronic mail messages using the SMTP protocol described * in RFC 2821. * * Copyright (C) 2001,2002 Brian Stafford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* An emulation of the RFC 2553 / Posix getaddrinfo resolver interface. */ #if !HAVE_DECL_GETADDRINFO /* Need to turn off Posix features in glibc to build this */ #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE #if HAVE_STRING_H #include #endif #if HAVE_CTYPE_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_NETDB_H #include #endif static struct addrinfo * dup_addrinfo (struct addrinfo *info, void *addr, size_t addrlen) { struct addrinfo *ret; ret = (struct addrinfo *)malloc(sizeof (struct addrinfo)); if (!ret) return nullptr; memcpy (ret, info, sizeof (struct addrinfo)); ret->ai_addr = (struct sockaddr*)malloc(addrlen); if (!ret->ai_addr) { free (ret); return nullptr; } memcpy (ret->ai_addr, addr, addrlen); ret->ai_addrlen = addrlen; return ret; } int xgetaddrinfo (const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct hostent *hp; struct servent *servent; const char *socktype; int port; struct addrinfo hint, result; struct addrinfo *ai, *sai, *eai; char **addrs; if (!servname && !nodename) return EAI_NONAME; memset (&result, 0, sizeof result); /* default for hints */ if (!hints) { memset (&hint, 0, sizeof hint); hint.ai_family = PF_UNSPEC; hints = &hint; } if (!servname) port = 0; else { /* check for tcp or udp sockets only */ if (hints->ai_socktype == SOCK_STREAM) socktype = "tcp"; else if (hints->ai_socktype == SOCK_DGRAM) socktype = "udp"; else return EAI_SERVICE; result.ai_socktype = hints->ai_socktype; /* Note: maintain port in host byte order to make debugging easier */ if (isdigit (*servname)) port = strtol (servname, nullptr, 10); else if ((servent = getservbyname (servname, socktype))) port = ntohs (servent->s_port); else return EAI_NONAME; } /* if !nodename, refer to the local host for a client or any for a server */ if (!nodename) { struct sockaddr_in sin; /* check protocol family is PF_UNSPEC or PF_INET - could try harder for IPv6 but that's more code than I'm prepared to write */ if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET) result.ai_family = AF_INET; else return EAI_FAMILY; sin.sin_family = result.ai_family; sin.sin_port = htons (port); if (hints->ai_flags & AI_PASSIVE) sin.sin_addr.s_addr = htonl (INADDR_ANY); else sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); /* Duplicate result and addr and return */ *res = dup_addrinfo (&result, &sin, sizeof sin); return (!*res) ? EAI_MEMORY : 0; } /* If AI_NUMERIC is specified, use inet_pton to translate numbers and dots notation. */ if (hints->ai_flags & AI_NUMERICHOST) { struct sockaddr_in sin; /* check protocol family is PF_UNSPEC or PF_INET */ if (hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET) result.ai_family = AF_INET; else return EAI_FAMILY; sin.sin_family = result.ai_family; sin.sin_port = htons (port); if (inet_pton(result.ai_family, nodename, &sin.sin_addr) != 1) return EAI_NONAME; sin.sin_addr.s_addr = inet_addr (nodename); /* Duplicate result and addr and return */ *res = dup_addrinfo (&result, &sin, sizeof sin); return (!*res) ? EAI_MEMORY : 0; } #if HAVE_H_ERRNO h_errno = 0; #endif errno = 0; hp = gethostbyname(nodename); if (!hp) { #ifdef EAI_SYSTEM if (errno != 0) { return EAI_SYSTEM; } #endif switch (h_errno) { case HOST_NOT_FOUND: return EAI_NODATA; case NO_DATA: return EAI_NODATA; #if defined(NO_ADDRESS) && NO_ADDRESS != NO_DATA case NO_ADDRESS: return EAI_NODATA; #endif case NO_RECOVERY: return EAI_FAIL; case TRY_AGAIN: return EAI_AGAIN; default: return EAI_FAIL; } return EAI_FAIL; } /* Check that the address family is acceptable. */ switch (hp->h_addrtype) { case AF_INET: if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET)) return EAI_FAMILY; break; case AF_INET6: if (!(hints->ai_family == PF_UNSPEC || hints->ai_family == PF_INET6)) return EAI_FAMILY; break; default: return EAI_FAMILY; } /* For each element pointed to by hp, create an element in the result linked list. */ sai = eai = nullptr; for (addrs = hp->h_addr_list; *addrs; addrs++) { struct sockaddr sa; size_t addrlen; if (hp->h_length < 1) continue; sa.sa_family = hp->h_addrtype; switch (hp->h_addrtype) { case AF_INET: ((struct sockaddr_in *) &sa)->sin_port = htons (port); memcpy (&((struct sockaddr_in *) &sa)->sin_addr, *addrs, hp->h_length); addrlen = sizeof (struct sockaddr_in); break; case AF_INET6: #if SIN6_LEN ((struct sockaddr_in6 *) &sa)->sin6_len = hp->h_length; #endif ((struct sockaddr_in6 *) &sa)->sin6_port = htons (port); memcpy (&((struct sockaddr_in6 *) &sa)->sin6_addr, *addrs, hp->h_length); addrlen = sizeof (struct sockaddr_in6); break; default: continue; } result.ai_family = hp->h_addrtype; ai = dup_addrinfo (&result, &sa, addrlen); if (!ai) { xfreeaddrinfo (sai); return EAI_MEMORY; } if (!sai) sai = ai; else eai->ai_next = ai; eai = ai; } if (!sai) { return EAI_NODATA; } if (hints->ai_flags & AI_CANONNAME) { sai->ai_canonname = (char *)malloc(strlen(hp->h_name) + 1); if (!sai->ai_canonname) { xfreeaddrinfo (sai); return EAI_MEMORY; } strcpy (sai->ai_canonname, hp->h_name); } *res = sai; return 0; } void xfreeaddrinfo (struct addrinfo *ai) { struct addrinfo *next; while (ai) { next = ai->ai_next; if (ai->ai_canonname) free (ai->ai_canonname); if (ai->ai_addr) free (ai->ai_addr); free (ai); ai = next; } } const char * xgai_strerror (int ecode) { static const char *eai_descr[] = { "no error", "address family for nodename not supported", /* EAI_ADDRFAMILY */ "temporary failure in name resolution", /* EAI_AGAIN */ "invalid value for ai_flags", /* EAI_BADFLAGS */ "non-recoverable failure in name resolution", /* EAI_FAIL */ "ai_family not supported", /* EAI_FAMILY */ "memory allocation failure", /* EAI_MEMORY */ "no address associated with nodename", /* EAI_NODATA */ "nodename nor servname provided, or not known", /* EAI_NONAME */ "servname not supported for ai_socktype", /* EAI_SERVICE */ "ai_socktype not supported", /* EAI_SOCKTYPE */ "system error returned in errno", /* EAI_SYSTEM */ "argument buffer overflow", /* EAI_OVERFLOW */ }; if (ecode < 0 || ecode > (int) (sizeof eai_descr/ sizeof eai_descr[0])) return "unknown error"; return eai_descr[ecode]; } #endif /* HAVE_DECL_GETADDRINFO */