[PR #3165] fix(macOS): pre-resolve Xray endpoint before tunnel setup #3112

Open
opened 2026-10-14 21:07:34 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amnezia-client/pull/3165
Author: @HeisLuka
Created: 9/14/2026
Status: 🔄 Open

Base: dev ← Head: fix/macos-xray-domain-endpoint


📝 Commits (1)

  • 8d39561 fix: pre-resolve Xray endpoint on macOS

📊 Changes

1 file changed (+59 additions, -1 deletions)

View changed files

📝 client/core/protocols/xrayProtocol.cpp (+59 -1)

📄 Description

Summary

Fixes #3132.

On macOS, Xray connections that use a domain name as the server endpoint can get stuck after the tunnel and DNS routing are configured.

The client already resolves the server hostname before starting Xray and stores the result in m_remoteAddress, but the native Xray config still contains the original hostname in:

outbounds[*].settings.vnext[*].address

As a result, Xray performs another DNS lookup after tunnel setup has started. On macOS this can cause the lookup to be routed through the tunnel itself, creating a DNS deadlock.

Fix

Before starting Xray on macOS, create a runtime copy of the Xray config and replace only:

outbounds[*].settings.vnext[*].address

when it matches the original server hostname.

The resolved IP is used only for the runtime Xray configuration.

The stored profile is not modified.

Why not use a global string replacement

PR #814 previously implemented Xray pre-resolution using a global hostname-to-IP string replacement.

That approach can also replace the same hostname when it is used as TLS/REALITY SNI, for example:

streamSettings.realitySettings.serverName

This patch updates only the actual connection endpoint and leaves serverName unchanged.

Example:

Before:

{
  "address": "vpn.example.org",
  "realitySettings": {
    "serverName": "vpn.example.org"
  }
}

Runtime config after this patch:

{
  "address": "203.0.113.10",
  "realitySettings": {
    "serverName": "vpn.example.org"
  }
}
Expected behavior
hostname is resolved before tunnel setup;
Xray connects directly to the resolved IP;
REALITY/TLS SNI remains the original hostname;
the saved configuration still contains the hostname;
a new DNS resolution is performed on the next connection, so DDNS continues to work.
Scope

The change is limited to macOS runtime Xray configuration.

No routing logic, stored configuration format, or REALITY/TLS settings are changed.

Validation

The behavior matches the A/B test reported in #3132:

domain in vnext.address -> connection stalls;
pre-resolved IPv4 in vnext.address, with the same REALITY serverName -> connection works immediately.

The change is intentionally small and limited to the runtime Xray config.

---

<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amnezia-client/pull/3165 **Author:** [@HeisLuka](https://github.com/HeisLuka) **Created:** 9/14/2026 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `fix/macos-xray-domain-endpoint` --- ### 📝 Commits (1) - [`8d39561`](https://github.com/amnezia-vpn/amnezia-client/commit/8d395614103ca25f3c917ffe8635874ae40eb00a) fix: pre-resolve Xray endpoint on macOS ### 📊 Changes **1 file changed** (+59 additions, -1 deletions) <details> <summary>View changed files</summary> 📝 `client/core/protocols/xrayProtocol.cpp` (+59 -1) </details> ### 📄 Description ## Summary Fixes #3132. On macOS, Xray connections that use a domain name as the server endpoint can get stuck after the tunnel and DNS routing are configured. The client already resolves the server hostname before starting Xray and stores the result in `m_remoteAddress`, but the native Xray config still contains the original hostname in: `outbounds[*].settings.vnext[*].address` As a result, Xray performs another DNS lookup after tunnel setup has started. On macOS this can cause the lookup to be routed through the tunnel itself, creating a DNS deadlock. ## Fix Before starting Xray on macOS, create a runtime copy of the Xray config and replace only: `outbounds[*].settings.vnext[*].address` when it matches the original server hostname. The resolved IP is used only for the runtime Xray configuration. The stored profile is not modified. ## Why not use a global string replacement PR #814 previously implemented Xray pre-resolution using a global hostname-to-IP string replacement. That approach can also replace the same hostname when it is used as TLS/REALITY SNI, for example: `streamSettings.realitySettings.serverName` This patch updates only the actual connection endpoint and leaves `serverName` unchanged. Example: Before: ```json { "address": "vpn.example.org", "realitySettings": { "serverName": "vpn.example.org" } } Runtime config after this patch: { "address": "203.0.113.10", "realitySettings": { "serverName": "vpn.example.org" } } Expected behavior hostname is resolved before tunnel setup; Xray connects directly to the resolved IP; REALITY/TLS SNI remains the original hostname; the saved configuration still contains the hostname; a new DNS resolution is performed on the next connection, so DDNS continues to work. Scope The change is limited to macOS runtime Xray configuration. No routing logic, stored configuration format, or REALITY/TLS settings are changed. Validation The behavior matches the A/B test reported in #3132: domain in vnext.address -> connection stalls; pre-resolved IPv4 in vnext.address, with the same REALITY serverName -> connection works immediately. The change is intentionally small and limited to the runtime Xray config. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#3112
No description provided.