Skip to content

Commit 8287cf8

Browse files
committed
fix(dns): Fix IPv6 only networks by preferencing IPv6 if you have address
Work around because AF_UNSPEC does not check available addresses when determining result. If you have a global scope IPv6 address, then first check for IPv6 DNS result; if you don't have an IPv6, or there is no IPv6 result, then check IPv4. This allows IPv6-only networks to connect to dual-stack destinations, as they will get the IPv6 address (rather than the unusable IPv4). It also means a dual-stack host to a dual-stack destination will preference IPv6. There is no effect if you are on an IPv4-only network, or it is an IPv4-only destination.
1 parent f8336de commit 8287cf8

File tree

1 file changed

+71
-36
lines changed

1 file changed

+71
-36
lines changed

Diff for: libraries/Network/src/NetworkManager.cpp

+71-36
Original file line numberDiff line numberDiff line change
@@ -47,55 +47,90 @@ int NetworkManager::hostByName(const char* aHostname, IPAddress& aResult, bool p
4747
{
4848
// IDEA: Rename to getAddressInfo() ?
4949

50-
err_t err = ERR_OK;
51-
52-
// This should generally check if we have a global address assigned to one of the interfaces.
53-
// If such address is not assigned, there is no point in trying to get V6 from DNS as we will not be able to reach it.
54-
// That is of course, if 'preferV6' is not set to true
50+
err_t err = -1;
51+
const char *servname = "0";
52+
struct addrinfo *res;
5553
static bool hasGlobalV6 = false;
56-
bool hasGlobalV6Now = false;//ToDo: implement this!
54+
55+
aResult = static_cast<uint32_t>(0);
56+
57+
// First check if the host parses as a literal address
58+
if (aResult.fromString(aHostname)) {
59+
return 1;
60+
}
61+
62+
// **Workaround**
63+
// LWIP AF_UNSPEC always prefers IPv4 and doesn't check what network is
64+
// available. See https://github.com/espressif/esp-idf/issues/13255
65+
// Until that is fixed, as a work around if we have a global scope IPv6,
66+
// then we check IPv6 only first.
67+
68+
// Iterate over active interfaces, and find if we have any global scope IPv6
69+
bool hasGlobalV6Now = false;
70+
esp_netif_t *netif = NULL;
71+
while ((netif = esp_netif_next_unsafe(netif)) != NULL) {
72+
esp_ip6_addr_t ip6[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
73+
int ip6_addrs = esp_netif_get_all_ip6(netif, ip6);
74+
for (int j = 0; j < ip6_addrs; ++j) {
75+
// Both global and unique local addresses have global scope.
76+
// ULA assumes either private DNS or NAT66 (same assumption as IPv4 private address ranges).
77+
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
78+
if (ipv6_type == ESP_IP6_ADDR_IS_GLOBAL || ipv6_type == ESP_IP6_ADDR_IS_UNIQUE_LOCAL) {
79+
hasGlobalV6Now = true;
80+
break;
81+
}
82+
}
83+
if (hasGlobalV6Now) break;
84+
}
85+
86+
// Clear DNS cache if the flag has changed
5787
if(hasGlobalV6 != hasGlobalV6Now){
5888
hasGlobalV6 = hasGlobalV6Now;
5989
dns_clear_cache();
6090
log_d("Clearing DNS cache");
6191
}
6292

63-
aResult = static_cast<uint32_t>(0);
64-
65-
// First check if the host parses as a literal address
66-
if (!aResult.fromString(aHostname)) {
67-
const char *servname = "0";
68-
struct addrinfo *res;
69-
const struct addrinfo hints = {
70-
.ai_family = AF_UNSPEC,
93+
if (hasGlobalV6) {
94+
const struct addrinfo hints6 = {
95+
.ai_family = AF_INET6,
7196
.ai_socktype = SOCK_STREAM,
7297
};
73-
err = lwip_getaddrinfo(aHostname, servname, &hints, &res);
74-
if (err == 0)
75-
{
76-
if (res->ai_family == AF_INET6)
77-
{
78-
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
79-
// As an array of u8_t
80-
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
81-
log_d("DNS found IPv6 %s", aResult.toString().c_str());
82-
}
83-
else if (res->ai_family == AF_INET)
84-
{
85-
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
86-
// As a single u32_t
87-
aResult = IPAddress(ipv4->sin_addr.s_addr);
88-
log_d("DNS found IPv4 %s", aResult.toString().c_str());
89-
}
90-
else
91-
{
92-
err = -1;
93-
}
98+
err = lwip_getaddrinfo(aHostname, servname, &hints6, &res);
9499

100+
if (err == ERR_OK)
101+
{
102+
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
103+
// As an array of u8_t
104+
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
105+
log_d("DNS found first IPv6 %s", aResult.toString().c_str());
95106
lwip_freeaddrinfo(res);
107+
return 1;
96108
}
97109
}
98-
if (err == ERR_OK) {
110+
// **End Workaround**
111+
112+
const struct addrinfo hints = {
113+
.ai_family = AF_UNSPEC,
114+
.ai_socktype = SOCK_STREAM,
115+
};
116+
err = lwip_getaddrinfo(aHostname, servname, &hints, &res);
117+
if (err == ERR_OK)
118+
{
119+
if (res->ai_family == AF_INET6)
120+
{
121+
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
122+
// As an array of u8_t
123+
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
124+
log_d("DNS found any IPv6 %s", aResult.toString().c_str());
125+
}
126+
else
127+
{
128+
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
129+
// As a single u32_t
130+
aResult = IPAddress(ipv4->sin_addr.s_addr);
131+
log_d("DNS found any IPv4 %s", aResult.toString().c_str());
132+
}
133+
lwip_freeaddrinfo(res);
99134
return 1;
100135
}
101136
log_e("DNS Failed for '%s' with error '%d'", aHostname, err);

0 commit comments

Comments
 (0)