diff --git a/CMakeLists.txt b/CMakeLists.txt index cdd62094..1e0728d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 () diff --git a/Makefile.m32 b/Makefile.m32 index e7a423be..66d28b1c 100644 --- a/Makefile.m32 +++ b/Makefile.m32 @@ -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 diff --git a/Makefile.msvc b/Makefile.msvc index 21d3de66..62cc48fb 100644 --- a/Makefile.msvc +++ b/Makefile.msvc @@ -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 # ------------------------------------------------- diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index a1474329..a3b060c2 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -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 \ diff --git a/src/lib/ares__addrinfo_localhost.c b/src/lib/ares__addrinfo_localhost.c new file mode 100644 index 00000000..1551ef1a --- /dev/null +++ b/src/lib/ares__addrinfo_localhost.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 +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +#include +#include +#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; iNumEntries; 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; +} diff --git a/src/lib/ares__readaddrinfo.c b/src/lib/ares__readaddrinfo.c index 10201388..7d809101 100644 --- a/src/lib/ares__readaddrinfo.c +++ b/src/lib/ares__readaddrinfo.c @@ -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; } diff --git a/src/lib/ares_getaddrinfo.c b/src/lib/ares_getaddrinfo.c index b1bfe862..a7a6c1eb 100644 --- a/src/lib/ares_getaddrinfo.c +++ b/src/lib/ares_getaddrinfo.c @@ -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': - /* DNS lookup */ - if (next_dns_lookup(hquery)) - break; + /* 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); diff --git a/src/lib/ares_private.h b/src/lib/ares_private.h index 43910b74..1a1e26ca 100644 --- a/src/lib/ares_private.h +++ b/src/lib/ares_private.h @@ -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); diff --git a/test/Makefile.m32 b/test/Makefile.m32 index 1b308c44..d5fdb1c0 100644 --- a/test/Makefile.m32 +++ b/test/Makefile.m32 @@ -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 diff --git a/test/Makefile.msvc b/test/Makefile.msvc index e0337db3..818232c6 100644 --- a/test/Makefile.msvc +++ b/test/Makefile.msvc @@ -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 diff --git a/test/ares-test-live.cc b/test/ares-test-live.cc index bce97a6a..8f9b2804 100644 --- a/test/ares-test-live.cc +++ b/test/ares-test-live.cc @@ -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;