[core,tcp] Fix PreferIPv6OverIPv4 fallback to IPv4 addresses

Currently, when the `FreeRDP_PreferIPv6OverIPv4` option is `TRUE` and
the `getaddrinfo` function returns IPv4 addresses before IPv6 addresses,
the code tries all IPv6 addresses, but it doesn't try any IPv4 address.
This happens because the `get_next_addrinfo` function skips to the first
IPv6 address, but never goes back to try IPv4 addresses that appeared
earlier in the list. Let's fix this by reordering the address list
first when the `PreferIPv6OverIPv4` option is `TRUE`.

Co-Authored-By: Claude <noreply@anthropic.com>

Related: https://github.com/FreeRDP/FreeRDP/issues/5335
This commit is contained in:
Ondrej Holy
2026-01-08 13:14:45 +01:00
committed by Armin Novak
parent 0bdd8da099
commit 72585691fd

View File

@@ -1081,6 +1081,53 @@ int freerdp_tcp_connect(rdpContext* context, const char* hostname, int port, DWO
return transport_tcp_connect(context->rdp->transport, hostname, port, timeout);
}
static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr)
{
WINPR_ASSERT(context);
WINPR_ASSERT(addr);
const BOOL preferIPv6 =
freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4);
if (!preferIPv6)
return addr;
struct addrinfo* ipv6Head = NULL;
struct addrinfo* ipv6Tail = NULL;
struct addrinfo* otherHead = NULL;
struct addrinfo* otherTail = NULL;
/* Partition the list into IPv6 and other addresses */
while (addr)
{
struct addrinfo* next = addr->ai_next;
addr->ai_next = NULL;
if (addr->ai_family == AF_INET6)
{
if (!ipv6Head)
ipv6Head = addr;
else
ipv6Tail->ai_next = addr;
ipv6Tail = addr;
}
else
{
if (!otherHead)
otherHead = addr;
else
otherTail->ai_next = addr;
otherTail = addr;
}
addr = next;
}
/* Concatenate the lists */
if (ipv6Tail)
ipv6Tail->ai_next = otherHead;
return ipv6Head ? ipv6Head : otherHead;
}
static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
UINT32 errorCode)
{
@@ -1091,14 +1138,6 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct
if (!addr)
goto fail;
if (freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4))
{
while (addr && (addr->ai_family != AF_INET6))
addr = addr->ai_next;
if (!addr)
addr = input;
}
/* We want to force IPvX, abort if not detected */
{
const UINT32 IPvX = freerdp_settings_get_uint32(context->settings, FreeRDP_ForceIPvX);
@@ -1200,9 +1239,11 @@ static int freerdp_host_connect(rdpContext* context, const char* hostname, int p
freerdp_set_last_error_log(context, 0);
/* By default we take the first returned entry.
* * If PreferIPv6OverIPv4 = TRUE we force to IPv6 if there
* is such an address available, but fall back to first if not found
* If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference:
* IPv6 addresses come first, then other addresses.
*/
result = reorder_addrinfo_by_preference(context, result);
const int rc = get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
goto fail;