mirror of https://github.com/c-ares/c-ares.git
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: #399pull/431/head
parent
c642b9fbb1
commit
29c4ecdc16
11 changed files with 270 additions and 12 deletions
@ -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; |
||||||
|
} |
Loading…
Reference in new issue