Skip to content

IPv6 Unreachability Fallback for TLS Configs #4846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 16, 2025

Conversation

hossinasaadi
Copy link
Contributor

If the configuration uses TLS and prefers IPv6, but IPv6 is not reachable or ISP not supported, the connection should gracefully fall back to IPv4. To handle this reliably, the core’s built-in Happy Eyeballs function is used to automatically select the best available address family and avoid connection failures.

@hossinasaadi
Copy link
Contributor Author

Maybe we should only do Happy Eyeballs when IPv6 is preferred?

@patterniha
Copy link
Contributor

in v2rayNG, and for proxy-outbound, happy eyeballs only operates when Outbound domain pre-solve method is Resolve and add to DNS Hosts.

domainStrategy should always be UseIP regardless of Prefer IPv6.

if Prefer IPv6 is true, set prioritizeIPv6 to true, otherwise false.

set tryDelayMs to 250 and let interleave and maxConcurrentTry be the default values( 1 and 4)

@hossinasaadi
Copy link
Contributor Author

in v2rayNG, and for proxy-outbound, happy eyeballs only operates when Outbound domain pre-solve method is Resolve and add to DNS Hosts.

domainStrategy should always be UseIP regardless of Prefer IPv6.

if Prefer IPv6 is true, set prioritizeIPv6 to true, otherwise false.

set tryDelayMs to 250 and let interleave and maxConcurrentTry be the default values( 1 and 4)

Thank you. I implemented prioritizeIPv6 so that when prefer IPv6 is enabled, it’s set to true and the interleave is set to 2, which makes it check two IPs of the selected version first (either IPv4 or IPv6).
As far as I’ve checked, this config seems to work properly.

@patterniha
Copy link
Contributor

patterniha commented Aug 14, 2025

domainStrategy should always be UseIP regardless of Prefer IPv6.

where is it?

without setting domainStrategy to UseIP, Resolve and add to DNS Hosts mode does not apply at all.

in AsIs mode, golang-happy-eyeballs is applied, This is because your test was successful, but this is for Do not resolve mode.

@hossinasaadi
Copy link
Contributor Author

where is it?

You were right.
V2rayng also resolve and add to DNS hosts :

  "dns": {
    "hosts": {
      "currentConfig.com": [
        "3a2a:**",
        "1.2.3.4"
      ]
    },
}

@patterniha
Copy link
Contributor

@hossinasaadi

@DHR60 is right, you should replace item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" with item.ensureSockopt().domainStrategy = "UseIP" in the same line.

@DHR60
Copy link
Contributor

DHR60 commented Aug 14, 2025

emm,我的意思是 resolvedIps.isNullOrEmpty() == true 时的 bug

要么

            if (newHosts.containsKey(domain)) {
                item.ensureSockopt().domainStrategy = "UseIP"
                item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
                    prioritizeIPv6 = preferIpv6,
                    interleave = 2
                )
                continue
            }

            val resolvedIps = HttpUtil.resolveHostToIP(domain, preferIpv6)
            if (resolvedIps.isNullOrEmpty()) continue

            item.ensureSockopt().domainStrategy = "UseIP"
            item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
                prioritizeIPv6 = preferIpv6,
                interleave = 2
            )

要么

if (resolvedIps.isNullOrEmpty()) {
    item.ensureSockopt().domainStrategy = null
    continue
}

不处理会导致 DNS 回环

@DHR60
Copy link
Contributor

DHR60 commented Aug 14, 2025

当然,如果你认为这个错误不可能触发,删掉 if (resolvedIps.isNullOrEmpty()) continue 这个处理就行

@hossinasaadi
Copy link
Contributor Author

@hossinasaadi

@DHR60 is right, you should replace item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6" with item.ensureSockopt().domainStrategy = "UseIP" in the same line.

I see, you think we can handle it from core? XTLS/Xray-core#5022

@patterniha
Copy link
Contributor

emm, I mean the bug when resolvedIps.isNullOrEmpty() == true

or

            if (newHosts.containsKey(domain)) {
                item.ensureSockopt().domainStrategy = "UseIP"
                item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
                    prioritizeIPv6 = preferIpv6,
                    interleave = 2
                )
                continue
            }

            val resolvedIps = HttpUtil.resolveHostToIP(domain, preferIpv6)
            if (resolvedIps.isNullOrEmpty()) continue

            item.ensureSockopt().domainStrategy = "UseIP"
            item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
                prioritizeIPv6 = preferIpv6,
                interleave = 2
            )

or

if (resolvedIps.isNullOrEmpty()) {
    item.ensureSockopt().domainStrategy = null
    continue
}

Failure to handle this will result in a DNS loop.

?
both of these codes are correct, I said the same thing.

@patterniha
Copy link
Contributor

حسین ایرانی دیگه؟ مشکلت چیه؟

@DHR60
Copy link
Contributor

DHR60 commented Aug 14, 2025

? both of these codes are correct, I said the same thing.

看成 replace A to B 了

@patterniha
Copy link
Contributor

I think some things are mixed up, we also have useSystem dns-queryStrategy.

@hossinasaadi
Copy link
Contributor Author

حسین ایرانی دیگه؟ مشکلت چیه؟

اره.الان باید مشکل حل شده باشه

@patterniha
Copy link
Contributor

patterniha commented Aug 14, 2025

@DHR60

I think it is okay now.

@2dust
Copy link
Owner

2dust commented Aug 16, 2025

没有使用过 happyEyeballs ,请问在真实环境下能有什么优势?

@hossinasaadi
Copy link
Contributor Author

I have never used happyEyeballs. What advantages does it have in a real environment?

XTLS/Xray-core#4667

for example suppose our IP-list is [ip4-1, ip4-2, ip4-3, ip4-4, ip6-1, ip6-2, ip6-3, ip6-4]

when interleave is 1 and prioritizeIPv6 is false, the sorted-ip-list is:
[ip4-1, ip6-1, ip4-2, ip6-2, ip4-3, ip6-3, ip4-4, ip6-4]

and when for example interleave is 2 and prioritizeIPv6 is true:
[ip6-1, ip6-2, ip4-1, ip4-2, ip6-3, ip6-4, ip4-3, ip4-4]

then delay 250ms for each attempt until first connection is established.

the first-stablished-connection is winner connection and selected for sending/receiving data.

@2dust 2dust merged commit 7b11755 into 2dust:master Aug 16, 2025
1 check passed
@hossinasaadi hossinasaadi deleted the patch-ipv6 branch August 16, 2025 06:39
@patterniha
Copy link
Contributor

patterniha commented Aug 16, 2025

when we have multiple-IP, this causes a race between ips and it tries to find to the first IP that can be connected.

so it solve ipv4/ipv6 unreachable problem.

I wrote a full explanation when I implemented it on the core: XTLS/Xray-core#4667

///

also, it only applies when sockopt-domainStrategy is UseIP/ForceIP, and for AsIs mode golang-happy-eyeballs is applied.

you can read Xray-core doc for it : https://xtls.github.io/config/transport.html#sockoptobject

///

Anyway, this PR has used this feature correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants