RFC6761: special case "localhost" (#430)

As per RFC6761 Section 6.3, "localhost" lookups need to be special cased to return loopback addresses, and not forward queries to recursive dns servers.

We first look up via files (/etc/hosts or equivalent), and if that fails, we then attempt a system-specific address enumeration for loopback addresses (currently Windows-only), and finally fallback to ::1 and 127.0.0.1.

Fix By: Brad House (@bradh352)
Fixes Bug: #399
pull/431/head
Brad House 3 years ago committed by GitHub
parent c642b9fbb1
commit 29c4ecdc16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CMakeLists.txt
  2. 2
      Makefile.m32
  3. 4
      Makefile.msvc
  4. 1
      src/lib/Makefile.inc
  5. 236
      src/lib/ares__addrinfo_localhost.c
  6. 2
      src/lib/ares__readaddrinfo.c
  7. 16
      src/lib/ares_getaddrinfo.c
  8. 3
      src/lib/ares_private.h
  9. 2
      test/Makefile.m32
  10. 4
      test/Makefile.msvc
  11. 4
      test/ares-test-live.cc

@ -236,7 +236,7 @@ IF (HAVE_LIBRT)
LIST (APPEND CARES_DEPENDENT_LIBS rt)
ENDIF ()
IF (WIN32)
LIST (APPEND CARES_DEPENDENT_LIBS ws2_32 advapi32)
LIST (APPEND CARES_DEPENDENT_LIBS ws2_32 advapi32 iphlpapi)
ENDIF ()

@ -20,7 +20,7 @@ CP = cp -afv
CFLAGS = $(CARES_CFLAG_EXTRAS) -O2 -Wall -I./include -I./src/lib -D_WIN32_WINNT=0x0600
CFLAGS += -DCARES_STATICLIB
LDFLAGS = $(CARES_LDFLAG_EXTRAS) -s
LIBS = -lwsock32
LIBS = -lws2_32 -liphlpapi
# Makefile.inc provides the CSOURCES and HHEADERS defines
include src/lib/Makefile.inc

@ -184,8 +184,8 @@ EX_LIBS_REL = $(WATT_ROOT)\lib\wattcpvc_imp.lib
EX_LIBS_DBG = $(WATT_ROOT)\lib\wattcpvc_imp_d.lib
!ELSE
CFLAGS = /DWIN32 /D_WIN32_WINNT=0x0600
EX_LIBS_REL = ws2_32.lib advapi32.lib kernel32.lib
EX_LIBS_DBG = ws2_32.lib advapi32.lib kernel32.lib
EX_LIBS_REL = ws2_32.lib advapi32.lib kernel32.lib iphlpapi.lib
EX_LIBS_DBG = ws2_32.lib advapi32.lib kernel32.lib iphlpapi.lib
!ENDIF
# -------------------------------------------------

@ -1,5 +1,6 @@
CSOURCES = ares__addrinfo2hostent.c \
ares__addrinfo_localhost.c \
ares__close_sockets.c \
ares__get_hostent.c \
ares__parse_into_addrinfo.c \

@ -0,0 +1,236 @@
/* Copyright (C) 2019 by Andrew Selivanov
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#include "ares_setup.h"
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
#include <ws2ipdef.h>
#include <iphlpapi.h>
#endif
#include "ares.h"
#include "ares_inet_net_pton.h"
#include "ares_nowarn.h"
#include "ares_private.h"
static int ares_append_ai_node(int aftype,
unsigned short port,
const void *adata,
struct ares_addrinfo_node **nodes)
{
struct ares_addrinfo_node *node;
node = ares__append_addrinfo_node(nodes);
if (!node)
{
return ARES_ENOMEM;
}
memset(node, 0, sizeof(*node));
if (aftype == AF_INET)
{
struct sockaddr_in *sin = ares_malloc(sizeof(*sin));
if (!sin)
{
return ARES_ENOMEM;
}
memset(sin, 0, sizeof(*sin));
memcpy(&sin->sin_addr.s_addr, adata, sizeof(struct in_addr));
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
node->ai_addr = (struct sockaddr *)sin;
node->ai_family = AF_INET;
node->ai_addrlen = sizeof(struct sockaddr_in);
node->ai_addr = (struct sockaddr *)sin;
}
if (aftype == AF_INET6)
{
struct sockaddr_in6 *sin6 = ares_malloc(sizeof(*sin6));
if (!sin6)
{
return ARES_ENOMEM;
}
memset(sin6, 0, sizeof(*sin6));
memcpy(&sin6->sin6_addr.s6_addr, adata, sizeof(struct ares_in6_addr));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
node->ai_addr = (struct sockaddr *)sin6;
node->ai_family = AF_INET6;
node->ai_addrlen = sizeof(struct ares_in6_addr);
node->ai_addr = (struct sockaddr *)sin6;
}
return ARES_SUCCESS;
}
static int ares__default_loopback_addrs(int aftype,
unsigned short port,
struct ares_addrinfo_node **nodes)
{
int status = ARES_SUCCESS;
if (aftype == AF_UNSPEC || aftype == AF_INET6)
{
struct ares_in6_addr addr6;
ares_inet_pton(AF_INET6, "::1", &addr6);
status = ares_append_ai_node(AF_INET6, port, &addr6, nodes);
if (status != ARES_SUCCESS)
{
return status;
}
}
if (aftype == AF_UNSPEC || aftype == AF_INET)
{
struct in_addr addr4;
ares_inet_pton(AF_INET, "127.0.0.1", &addr4);
status = ares_append_ai_node(AF_INET, port, &addr4, nodes);
if (status != ARES_SUCCESS)
{
return status;
}
}
return status;
}
static int ares__system_loopback_addrs(int aftype,
unsigned short port,
struct ares_addrinfo_node **nodes)
{
#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
PMIB_UNICASTIPADDRESS_TABLE table;
unsigned int i;
int status;
*nodes = NULL;
if (GetUnicastIpAddressTable(aftype, &table) != NO_ERROR)
return ARES_ENOTFOUND;
for (i=0; i<table->NumEntries; i++)
{
if (table->Table[i].InterfaceLuid.Info.IfType !=
IF_TYPE_SOFTWARE_LOOPBACK)
{
continue;
}
if (table->Table[i].Address.si_family == AF_INET)
{
status = ares_append_ai_node(table->Table[i].Address.si_family, port,
&table->Table[i].Address.Ipv4.sin_addr,
nodes);
}
else if (table->Table[i].Address.si_family == AF_INET6)
{
status = ares_append_ai_node(table->Table[i].Address.si_family, port,
&table->Table[i].Address.Ipv6.sin6_addr,
nodes);
}
else
{
/* Ignore any others */
continue;
}
if (status != ARES_SUCCESS)
{
goto fail;
}
}
if (*nodes == NULL)
status = ARES_ENOTFOUND;
fail:
FreeMibTable(table);
if (status != ARES_SUCCESS)
{
ares__freeaddrinfo_nodes(*nodes);
*nodes = NULL;
}
return status;
#else
/* Not supported on any other OS at this time */
return ARES_ENOTFOUND;
#endif
}
int ares__addrinfo_localhost(const char *name,
unsigned short port,
const struct ares_addrinfo_hints *hints,
struct ares_addrinfo *ai)
{
struct ares_addrinfo_node *nodes = NULL;
int result;
/* Validate family */
switch (hints->ai_family) {
case AF_INET:
case AF_INET6:
case AF_UNSPEC:
break;
default:
return ARES_EBADFAMILY;
}
ai->name = ares_strdup(name);
if(!ai->name)
{
goto enomem;
}
result = ares__system_loopback_addrs(hints->ai_family, port, &nodes);
if (result == ARES_ENOTFOUND)
{
result = ares__default_loopback_addrs(hints->ai_family, port, &nodes);
}
ares__addrinfo_cat_nodes(&ai->nodes, nodes);
return result;
enomem:
ares__freeaddrinfo_nodes(nodes);
ares_free(ai->name);
ai->name = NULL;
return ARES_ENOMEM;
}

@ -265,5 +265,7 @@ enomem:
ares_free(line);
ares__freeaddrinfo_cnames(cnames);
ares__freeaddrinfo_nodes(nodes);
ares_free(ai->name);
ai->name = NULL;
return ARES_ENOMEM;
}

@ -503,6 +503,16 @@ static int file_lookup(struct host_query *hquery)
}
status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai);
fclose(fp);
/* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
* SHOULD recognize localhost names as special and SHOULD always return the
* IP loopback address for address queries" */
if (status == ARES_ENOTFOUND && strcmp(hquery->name, "localhost") == 0)
{
return ares__addrinfo_localhost(hquery->name, hquery->port,
&hquery->hints, hquery->ai);
}
return status;
}
@ -511,9 +521,15 @@ static void next_lookup(struct host_query *hquery, int status)
switch (*hquery->remaining_lookups)
{
case 'b':
/* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
* queries for localhost names to their configured caching DNS
* server(s)." */
if (strcmp(hquery->name, "localhost") != 0)
{
/* DNS lookup */
if (next_dns_lookup(hquery))
break;
}
hquery->remaining_lookups++;
next_lookup(hquery, status);

@ -399,6 +399,9 @@ int ares__addrinfo2hostent(const struct ares_addrinfo *ai, int family,
int ares__addrinfo2addrttl(const struct ares_addrinfo *ai, int family,
int req_naddrttls, struct ares_addrttl *addrttls,
struct ares_addr6ttl *addr6ttls, int *naddrttls);
int ares__addrinfo_localhost(const char *name, unsigned short port,
const struct ares_addrinfo_hints *hints,
struct ares_addrinfo *ai);
#if 0 /* Not used */
long ares__tvdiff(struct timeval t1, struct timeval t2);

@ -17,7 +17,7 @@ GMOCK_DIR = gmock-1.8.0
CPPFLAGS = -I$(ARES_SRC_DIR)/include -I$(ARES_SRC_DIR)/src/lib -I$(GMOCK_DIR) -DCARES_STATICLIB
CXXFLAGS = -Wall $(PTHREAD_CFLAGS) -std=gnu++11
LDFLAGS =
LDLIBS = -lwsock32
LDLIBS = -lws2_32 -liphlpapi
# Makefile.inc provides the TESTSOURCES and TESTHEADERS defines
include Makefile.inc

@ -140,8 +140,8 @@ LIB_OUTDIR = $(LIB_DIR)\$(CFG)
# TCP/IP stack settings
# -------------------------------------
CFLAGS = /DWIN32
EX_LIBS_REL = ws2_32.lib advapi32.lib kernel32.lib
EX_LIBS_DBG = ws2_32.lib advapi32.lib kernel32.lib
EX_LIBS_REL = ws2_32.lib advapi32.lib kernel32.lib iphlpapi.lib
EX_LIBS_DBG = ws2_32.lib advapi32.lib kernel32.lib iphlpapi.lib
# -------------------------------------------------
# Switches that depend on ancient compiler versions

@ -145,7 +145,7 @@ TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameV4) {
ares_gethostbyname(channel_, "localhost", AF_INET, HostCallback, &result);
Process();
EXPECT_TRUE(result.done_);
if ((result.status_ != ARES_ENOTFOUND) && (result.status_ != ARES_ECONNREFUSED)) {
if (result.status_ != ARES_ECONNREFUSED) {
EXPECT_EQ(ARES_SUCCESS, result.status_);
EXPECT_EQ(1, (int)result.host_.addrs_.size());
EXPECT_EQ(AF_INET, result.host_.addrtype_);
@ -158,7 +158,7 @@ TEST_P(DefaultChannelModeTest, LiveGetLocalhostByNameV6) {
ares_gethostbyname(channel_, "localhost", AF_INET6, HostCallback, &result);
Process();
EXPECT_TRUE(result.done_);
if (result.status_ == ARES_SUCCESS) {
if (result.status_ != ARES_ECONNREFUSED) {
EXPECT_EQ(1, (int)result.host_.addrs_.size());
EXPECT_EQ(AF_INET6, result.host_.addrtype_);
std::stringstream ss;

Loading…
Cancel
Save