You should have received a copy of the GNU General Public License
along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+In addition, as a special exception, the Free Software Foundation
+gives permission to link the code of its release of Wget with the
+OpenSSL project's "OpenSSL" library (or with modified versions of it
+that use the same license as the "OpenSSL" library), and distribute
+the linked executables. You must obey the GNU General Public License
+in all respects for all of the code used other than "OpenSSL". If you
+modify this file, you may extend this exception to your version of the
+file, but you are not obligated to do so. If you do not wish to do
+so, delete this exception statement from your version. */
#include <config.h>
+
+#ifndef WINDOWS
#include <netdb.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef WINDOWS
# include <winsock.h>
+# define SET_H_ERRNO(err) WSASetLastError(err)
#else
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# endif
# include <netdb.h>
+# define SET_H_ERRNO(err) ((void)(h_errno = (err)))
#endif /* WINDOWS */
#ifndef NO_ADDRESS
# endif
#endif
-#ifdef INET6
+#ifdef ENABLE_IPV6
int ip_default_family = AF_INET6;
#else
int ip_default_family = AF_INET;
void
address_list_copy_one (struct address_list *al, int index, ip_address *ip_store)
{
- assert (index >= al->faulty && index < al->count && ip_store!=NULL );
+ assert (index >= al->faulty && index < al->count);
memcpy (ip_store, al->addresses + index, sizeof (ip_address));
}
al->faulty = 0;
}
-#ifdef INET6
+#ifdef HAVE_GETADDRINFO
/**
* address_list_from_addrinfo
*
for (i = 0, ai = ai_head; ai; ai = ai->ai_next)
if (ai->ai_family == AF_INET6)
{
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
memcpy (al->addresses + i, &sin6->sin6_addr, 16);
++i;
}
return al;
}
#else
-/* Create an address_list out of a NULL-terminated list of addresses,
- as returned by gethostbyname. */
+/* Create an address_list out of a NULL-terminated vector of
+ addresses, as returned by gethostbyname. */
static struct address_list *
-address_list_new (char **h_addr_list)
+address_list_from_vector (char **h_addr_list)
{
int count = 0, i;
al->refcount = 1;
for (i = 0; i < count; i++)
- memcpy (al->addresses + i, h_addr_list[i], sizeof (ip_address));
+ map_ipv4_to_ip ((ip4_address *)h_addr_list[i], al->addresses + i);
return al;
}
#endif
-/* Like address_list_new, but initialized with only one address. */
+/* Like address_list_from_vector, but initialized with a single
+ address. */
static struct address_list *
-address_list_new_one (ip_address *addr)
+address_list_from_single (ip_address *addr)
{
struct address_list *al = xmalloc (sizeof (struct address_list));
al->count = 1;
sa->sin.sin_family = ip_family;
sa->sin.sin_port = htons (port);
if (addr == NULL)
- memset ((unsigned char*)&sa->sin.sin_addr, 0, sizeof(ip_address));
- else
- memcpy ((unsigned char*)&sa->sin.sin_addr, addr, sizeof(ip_address));
+ memset (&sa->sin.sin_addr, 0, sizeof(ip4_address));
+ else
+ {
+ ip4_address addr4;
+ if (!map_ip_to_ipv4 (addr, &addr4))
+ /* should the callers have prevented this? */
+ abort ();
+ memcpy (&sa->sin.sin_addr, &addr4, sizeof(ip4_address));
+ }
return;
}
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (ip_family == AF_INET6)
{
sa->sin6.sin6_family = ip_family;
sa->sin6.sin6_port = htons (port);
if (addr == NULL)
- memset (&sa->sin6.sin6_addr, 0 , sizeof(ip_address));
+ memset (&sa->sin6.sin6_addr, 0 , 16);
else
- memcpy (&sa->sin6.sin6_addr, addr, sizeof(ip_address));
+ memcpy (&sa->sin6.sin6_addr, addr, 16);
return;
}
#endif
sa->sin.sin_port = htons (port);
return;
}
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
{
sa->sin6.sin6_port = htons (port);
{
if (sa->sa.sa_family == AF_INET)
return &sa->sin.sin_addr;
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
return &sa->sin6.sin6_addr;
#endif
{
if (sa->sa.sa_family == AF_INET)
return htons (sa->sin.sin_port);
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (sa->sa.sa_family == AF_INET6)
return htons (sa->sin6.sin6_port);
#endif
{
if (ip_default_family == AF_INET)
return sizeof (struct sockaddr_in);
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (ip_default_family == AF_INET6)
return sizeof (struct sockaddr_in6);
#endif
void
map_ipv4_to_ip (ip4_address *ipv4, ip_address *ip)
{
-#ifdef INET6
+#ifdef ENABLE_IPV6
static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
memcpy ((char *)ip + 12, ipv4 , 4);
memcpy ((char *)ip + 0, ipv64, 12);
int
map_ip_to_ipv4 (ip_address *ip, ip4_address *ipv4)
{
-#ifdef INET6
+#ifdef ENABLE_IPV6
static unsigned char ipv64[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
if (0 != memcmp (ip, ipv64, 12))
return 0;
return 1;
}
\f
+/* Versions of gethostbyname and getaddrinfo that support timeout. */
+
+#ifndef ENABLE_IPV6
+
+struct ghbnwt_context {
+ const char *host_name;
+ struct hostent *hptr;
+};
+
+static void
+gethostbyname_with_timeout_callback (void *arg)
+{
+ struct ghbnwt_context *ctx = (struct ghbnwt_context *)arg;
+ ctx->hptr = gethostbyname (ctx->host_name);
+}
+
+/* Just like gethostbyname, except it times out after TIMEOUT seconds.
+ In case of timeout, NULL is returned and errno is set to ETIMEDOUT.
+ The function makes sure that when NULL is returned for reasons
+ other than timeout, errno is reset. */
+
+static struct hostent *
+gethostbyname_with_timeout (const char *host_name, double timeout)
+{
+ struct ghbnwt_context ctx;
+ ctx.host_name = host_name;
+ if (run_with_timeout (timeout, gethostbyname_with_timeout_callback, &ctx))
+ {
+ SET_H_ERRNO (HOST_NOT_FOUND);
+ errno = ETIMEDOUT;
+ return NULL;
+ }
+ if (!ctx.hptr)
+ errno = 0;
+ return ctx.hptr;
+}
+
+#else /* ENABLE_IPV6 */
+
+struct gaiwt_context {
+ const char *node;
+ const char *service;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+ int exit_code;
+};
+
+static void
+getaddrinfo_with_timeout_callback (void *arg)
+{
+ struct gaiwt_context *ctx = (struct gaiwt_context *)arg;
+ ctx->exit_code = getaddrinfo (ctx->node, ctx->service, ctx->hints, ctx->res);
+}
+
+/* Just like getaddrinfo, except it times out after TIMEOUT seconds.
+ In case of timeout, the EAI_SYSTEM error code is returned and errno
+ is set to ETIMEDOUT. */
+
+static int
+getaddrinfo_with_timeout (const char *node, const char *service,
+ const struct addrinfo *hints, struct addrinfo **res,
+ double timeout)
+{
+ struct gaiwt_context ctx;
+ ctx.node = node;
+ ctx.service = service;
+ ctx.hints = hints;
+ ctx.res = res;
+
+ if (run_with_timeout (timeout, getaddrinfo_with_timeout_callback, &ctx))
+ {
+ errno = ETIMEDOUT;
+ return EAI_SYSTEM;
+ }
+ return ctx.exit_code;
+}
+
+#endif /* ENABLE_IPV6 */
+\f
/* Pretty-print ADDR. When compiled without IPv6, this is the same as
inet_ntoa. With IPv6, it either prints an IPv6 address or an IPv4
address. */
char *
pretty_print_address (ip_address *addr)
{
-#ifdef INET6
+#ifdef ENABLE_IPV6
ip4_address addr4;
static char buf[128];
++al->refcount;
hash_table_put (host_name_addresses_map, xstrdup_lower (host), al);
-#ifdef DEBUG
+#ifdef ENABLE_DEBUG
if (opt.debug)
{
int i;
/* First, try to check whether the address is already a numeric
address. */
-#ifdef INET6
+#ifdef ENABLE_IPV6
if (inet_pton (AF_INET6, host, &addr) > 0)
- return address_list_new_one (&addr);
+ return address_list_from_single (&addr);
#endif
addr_ipv4 = (unsigned long)inet_addr (host);
we copy the correct four bytes. */
int offset = 0;
#ifdef WORDS_BIGENDIAN
- offset = sizeof (unsigned long) - sizeof (ip_address);
+ offset = sizeof (unsigned long) - sizeof (ip4_address);
#endif
map_ipv4_to_ip ((ip4_address *)((char *)&addr_ipv4 + offset), &addr);
- return address_list_new_one (&addr);
+ return address_list_from_single (&addr);
}
if (host_name_addresses_map)
if (!silent)
logprintf (LOG_VERBOSE, _("Resolving %s... "), host);
-#ifdef INET6
+ /* Host name lookup goes on below. */
+
+#ifdef HAVE_GETADDRINFO
{
struct addrinfo hints, *ai;
int err;
memset (&hints, 0, sizeof (hints));
- hints.ai_family = PF_UNSPEC;
+ if (ip_default_family == AF_INET)
+ hints.ai_family = AF_INET;
+ else
+ hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo (host, NULL, &hints, &ai);
+ err = getaddrinfo_with_timeout (host, NULL, &hints, &ai, opt.dns_timeout);
if (err != 0 || ai == NULL)
{
if (!silent)
- logprintf (LOG_VERBOSE, _("failed: %s.\n"), gai_strerror (err));
+ logprintf (LOG_VERBOSE, _("failed: %s.\n"),
+ err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
return NULL;
}
al = address_list_from_addrinfo (ai);
}
#else
{
- struct hostent *hptr = gethostbyname (host);
+ struct hostent *hptr;
+ hptr = gethostbyname_with_timeout (host, opt.dns_timeout);
if (!hptr)
{
if (!silent)
- logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
+ {
+ if (errno != ETIMEDOUT)
+ logprintf (LOG_VERBOSE, _("failed: %s.\n"), herrmsg (h_errno));
+ else
+ logputs (LOG_VERBOSE, _("failed: timed out.\n"));
+ }
return NULL;
}
/* Do all systems have h_addr_list, or is it a newer thing? If
- the latter, use address_list_new_one. */
- al = address_list_new (hptr->h_addr_list);
+ the latter, use address_list_from_single. */
+ al = address_list_from_vector (hptr->h_addr_list);
}
#endif
+ /* Print the addresses determined by DNS lookup, but no more than
+ three. */
if (!silent)
- logprintf (LOG_VERBOSE, _("done.\n"));
+ {
+ int i;
+ int printmax = al->count <= 3 ? al->count : 3;
+ for (i = 0; i < printmax; i++)
+ {
+ logprintf (LOG_VERBOSE, "%s",
+ pretty_print_address (al->addresses + i));
+ if (i < printmax - 1)
+ logputs (LOG_VERBOSE, ", ");
+ }
+ if (printmax != al->count)
+ logputs (LOG_VERBOSE, ", ...");
+ logputs (LOG_VERBOSE, "\n");
+ }
/* Cache the lookup information. */
- cache_host_lookup (host, al);
+ if (opt.dns_cache)
+ cache_host_lookup (host, al);
return al;
}