Skip to content

Commit 01d7efb

Browse files
committed
fix(dns): Fix IPv6-only network, by IPv6 preference if you have public 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 0a47f34 commit 01d7efb

File tree

1 file changed

+51
-18
lines changed

1 file changed

+51
-18
lines changed

libraries/Network/src/NetworkManager.cpp

+51-18
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ NetworkManager::NetworkManager(){
1414

1515
}
1616

17+
NetworkInterface * getNetifByID(Network_Interface_ID id);
18+
1719
bool NetworkManager::begin(){
1820
static bool initialized = false;
1921
if(!initialized){
@@ -44,33 +46,66 @@ bool NetworkManager::begin(){
4446
*/
4547
int NetworkManager::hostByName(const char* aHostname, IPAddress& aResult)
4648
{
49+
static bool hasGlobalV6 = false;
4750
err_t err = ERR_OK;
51+
const char *servname = "0";
52+
struct addrinfo *res;
53+
aResult = static_cast<uint32_t>(0);
4854

49-
// This should generally check if we have a global address assigned to one of the interfaces.
50-
// 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.
51-
// That is of course, if 'preferV6' is not set to true
52-
static bool hasGlobalV6 = false;
53-
bool hasGlobalV6Now = false;//ToDo: implement this!
55+
// First check if the host parses as a literal address
56+
if (aResult.fromString(aHostname)) {
57+
return 1;
58+
}
59+
60+
// **Workaround**
61+
// LWIP AF_UNSPEC always prefers IPv4 and doesn't check what network is
62+
// available. See https://github.com/espressif/esp-idf/issues/13255
63+
// Until that is fixed, as a work around if we have a global scope IPv6,
64+
// then we check IPv6 only first.
65+
66+
// This checks if we have a global address assigned to one of the interfaces.
67+
// If such address is assigned, then we trying to get V6 from DNS first.
68+
bool hasGlobalV6Now = false;
69+
for (int i = 0; i < ESP_NETIF_ID_MAX; ++i){
70+
NetworkInterface * iface = getNetifByID((Network_Interface_ID)i);
71+
if(iface != NULL && iface->hasGlobalIPv6()){
72+
hasGlobalV6Now = true;
73+
}
74+
if (hasGlobalV6Now){
75+
break;
76+
}
77+
}
78+
79+
// Clear DNS cache if the flag has changed
5480
if(hasGlobalV6 != hasGlobalV6Now){
5581
hasGlobalV6 = hasGlobalV6Now;
5682
dns_clear_cache();
5783
log_d("Clearing DNS cache");
5884
}
5985

60-
aResult = static_cast<uint32_t>(0);
86+
if (hasGlobalV6) {
87+
const struct addrinfo hints6 = {
88+
.ai_family = AF_INET6,
89+
.ai_socktype = SOCK_STREAM,
90+
};
91+
err = lwip_getaddrinfo(aHostname, servname, &hints6, &res);
6192

62-
// First check if the host parses as a literal address
63-
if (aResult.fromString(aHostname)) {
64-
return 1;
93+
if (err == ERR_OK)
94+
{
95+
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
96+
// As an array of u8_t
97+
aResult = IPAddress(IPv6, ipv6->sin6_addr.s6_addr);
98+
log_d("DNS found IPv6 first %s", aResult.toString().c_str());
99+
lwip_freeaddrinfo(res);
100+
return 1;
101+
}
65102
}
103+
// **End Workaround**
66104

67-
const char *servname = "0";
68-
struct addrinfo *res;
69-
struct addrinfo hints;
70-
memset(&hints, 0, sizeof(hints));
71-
hints.ai_family = AF_UNSPEC;
72-
hints.ai_socktype = SOCK_STREAM;
73-
105+
const struct addrinfo hints = {
106+
.ai_family = AF_UNSPEC,
107+
.ai_socktype = SOCK_STREAM,
108+
};
74109
err = lwip_getaddrinfo(aHostname, servname, &hints, &res);
75110
if (err == ERR_OK)
76111
{
@@ -130,8 +165,6 @@ bool NetworkManager::setHostname(const char * name)
130165
return true;
131166
}
132167

133-
NetworkInterface * getNetifByID(Network_Interface_ID id);
134-
135168
bool NetworkManager::setDefaultInterface(NetworkInterface & ifc)
136169
{
137170
return ifc.setDefault();

0 commit comments

Comments
 (0)