Skip to content

Commit b894c48

Browse files
committed
fix(dns): Dynamic DNS sorting so AF_UNSPEC works in IPv6-only network
This fix adds an option to dynamically set the order based on whether an the host has a global (or ULA) IPv6 address, allowing a device with both IPv4 and IPv6 enabled to work in any network configuration, including IPv4-only, IPv6-only, and dual-stack. Without the option there is a static ordering preferring IPv4, which means on an IPv6-only network, a dual-stack destination returns the IPv4 address, which fails. Setting the static preference to IPv6 would have the opposite problem. A dynamic sort, based on available source addresses, is required to work on all networks.
1 parent f792214 commit b894c48

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

src/api/netdb.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,34 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
341341
}
342342
#endif /* ESP_LWIP */
343343
}
344+
#if LWIP_DNS_DYNAMIC_SORT
345+
else {
346+
/* AF_UNSPEC prefer IPv6 if we have global IPv6 */
347+
// This is not a full implementation of RFC 6724, but it's a simple heuristic that works for most cases.
348+
bool has_global_scope_ipv6 = false;
349+
struct netif *netif;
350+
for(netif = netif_list; netif != NULL; netif = netif->next) {
351+
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
352+
// Both global and unique local addresses have global scope.
353+
// ULA assumes either private DNS or NAT66 (same assumpation as IPv4 private address ranges).
354+
if (ip6_addr_isglobal(netif_ip6_addr(netif, i)) || ip6_addr_isuniquelocal(netif_ip6_addr(netif, i))) {
355+
has_global_scope_ipv6 = true;
356+
break;
357+
}
358+
}
359+
if (has_global_scope_ipv6) {
360+
break;
361+
}
362+
}
363+
// NOTE: This will still preference IPv4 on IPv4-only networks (i.e. with only a link-local IPv6),
364+
// and IPv6 on IPv6-only (and dual-stack) networks, so works for all cases.
365+
if (has_global_scope_ipv6) {
366+
type = NETCONN_DNS_IPV6_IPV4;
367+
}
368+
}
369+
#endif /* LWIP_DNS_DYNAMIC_SORT */
344370
#endif /* LWIP_IPV4 && LWIP_IPV6 */
371+
// NOTE: If LWIP_IPV4 && LWIP_IPV6 are not defined this is a macro that eliminates `type`, so it still compiles
345372
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
346373
if (err != ERR_OK) {
347374
return EAI_FAIL;

0 commit comments

Comments
 (0)