Skip to content

cgo_bridge: export CGoSetEnv so hosts can reach xray-core env knobs - #149

Open
plutoisnotaplanet wants to merge 1 commit into
XTLS:mainfrom
plutoisnotaplanet:cgo-setenv
Open

cgo_bridge: export CGoSetEnv so hosts can reach xray-core env knobs#149
plutoisnotaplanet wants to merge 1 commit into
XTLS:mainfrom
plutoisnotaplanet:cgo-setenv

Conversation

@plutoisnotaplanet

@plutoisnotaplanet plutoisnotaplanet commented Sep 1, 2026

Copy link
Copy Markdown

Problem

The Go runtime snapshots environ when a c-archive image initializes. A
setenv() performed later by the host application is therefore never visible
to os.LookupEnv, which is what common/platform.EnvFlag uses.

That makes every env-based knob in xray-core unreachable from a non-Go host.
The one that matters in practice is platform.TunFdKey (xray.tun.fd), read
by proxy/tun/tun_darwin.go in the branch commented "iOS: use provided fd from
NetworkExtension".

On iOS the tun descriptor only exists after
NEPacketTunnelProvider.startTunnel runs — long after the snapshot — so a
Swift host cannot publish it. NewTun then falls through to the macOS path and
tries to create its own utun via an AF_SYSTEM socket, which the app sandbox
denies:

app/proxyman/inbound: failed to start proxy > operation not permitted

Reproduced on iPhone 16 Pro / iOS 26.6 with libXray v26.7.28, and isolated with
a minimal spike: a C program calls setenv("xray.tun.fd", "5", 1) and then an
exported Go function — getenv returns 5, os.LookupEnv returns
found=false.

Change

One exported function in cgo_bridge, calling os.Setenv inside the Go
runtime. No behavior change for existing callers; nothing else is touched.

With it, the iOS branch of NewTun finally becomes reachable: our client now
brings the tunnel up on device with the same core version that previously
failed at the inbound. The packet path needed no further work — the 4-byte utun
header is handled in DarwinTun.ReadPacket/WritePacket, which are shared with
the macOS path.

Alternative considered

Passing the descriptor through TunConfig (a fd field) would be more
explicit and would not rely on process env at all. That is a larger change to
xray-core's config surface; happy to prepare it instead if maintainers prefer.

The Go runtime snapshots environ when the c-archive image initializes, so a
setenv() performed later by a non-Go host is never visible to os.LookupEnv,
which common/platform.EnvFlag relies on.

That makes platform.TunFdKey ("xray.tun.fd") unreachable on iOS: the tun
descriptor only exists after NEPacketTunnelProvider.startTunnel, long after
the snapshot, so the "iOS: use provided fd from NetworkExtension" branch in
proxy/tun/tun_darwin.go can never be taken. NewTun falls through to creating
its own utun via an AF_SYSTEM socket, which the app sandbox denies, and the
inbound fails with "failed to start proxy > operation not permitted".
@yiguodev

yiguodev commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Thanks for contribution.
But why not use Environment Variables inside xray config?
Here is an example

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.

2 participants