Skip to content

[pull] master from eycorsican:master - #36

Merged
pull[bot] merged 4 commits into
zxfandyy:masterfrom
eycorsican:master
Sep 10, 2026
Merged

pull[bot] merged 4 commits into
zxfandyy:masterfrom
eycorsican:master

Conversation

@pull

@pull pull Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

Both failed on Windows, and neither was Windows' fault.

The SOCKS5 outbound's mock server read the greeting as two bytes. The
shortest greeting there is is three -- VER, NMETHODS, and one method -- so
every read after it was off by one, the request went unread, and a socket
dropped with unread bytes in it is reset rather than closed. The client was
then racing the reset for a reply it was already being sent, and won often
enough on Unix to look like a passing test. It now reads the greeting and the
request in full, which is both correct and what makes the close a close.

`an_absolute_path_is_left_alone` asserted that `/etc/leaf/cert.pem` comes back
unchanged. On Windows that path is not absolute -- it names the root of
whichever drive is current -- so `resolve_certificate` resolves it against the
asset directory, correctly, and the test was asking a question with a Unix
answer. It now uses an absolute path for the platform it runs on.

`cargo test -p leaf --features plugin --lib` is 107 of 107 on Windows.
… tun

On Windows this was unconditional: any build with `inbound-tun` compiled in
set OUTBOUND_INTERFACE at startup, whether or not a TUN inbound was
configured. macOS and Linux ask `has_tun_listener() && tun_auto()` first, a
few lines above; Windows now asks the same.

The cost of setting it was every outbound socket that asks for the wildcard
bind getting pinned to the default interface instead, and such a socket
cannot reach a destination that interface does not serve. The QUIC outbound
is the only caller that passes UNSPECIFIED_BIND_ADDR -- the rest pass the
destination, and so take the loopback branch in `bind_socket` -- which is why
QUIC alone failed, and why it failed against a loopback server: quinn's first
Initial packet never left, with WSAEADDRNOTAVAIL, and the connection died at
the dial timeout eight seconds later.

All 30 quic-trojan e2e cases fail without this on Windows and pass with it.
Thirty-five commits since the pinned 4cc3162, and the reason to take them is
that most are corrections to the lwIP binding rather than features. Among
them: a use-after-free in `UdpSocket::split`, a data race and aliasing UB on
the output path, a UDP callback that shared context unsafely, `sys_now`
arithmetic widened to 64 bits so it no longer overflows on 32-bit targets,
pcbs no longer touched after lwIP has freed them or parked them in TIME_WAIT,
and a timer task that is now aborted when the stack is dropped instead of
outliving it.

The rest is capacity and backpressure work that shows up under connection
churn: pools sized for the connection counts each platform is expected to
reach, a shorter TCP_MSL to cut TIME_WAIT pressure, a smaller window on iOS to
cap per-connection buffering, and `poll_flush`/`poll_write` parking on
transient ERR_MEM rather than failing the connection.

The bindings are now generated at build time for the exact target rather than
checked in, so their layouts always match what is being compiled against.
That needs libclang, which leaf already requires: bindgen is an unconditional
build dependency of the crate.

`inbound-tun` is in the default feature set, so this is on the ordinary build
path rather than an opt-in one.
The netstack-lwip update generates its lwIP bindings at build time for the
exact target rather than shipping them checked in, so bindgen now parses
mingw's headers under `--target=x86_64-pc-windows-gnu`. That reaches
`stdlib.h`, and on mingw `stdlib.h` reaches `malloc.h`, which includes
`mm_malloc.h` -- a header the compiler is expected to supply rather than the
sysroot.

clang has one, in its resource directory, and the cross image carries four
copies of it. The libclang bindgen loads simply does not put that directory on
the include path for this target, so the build fails on a header that is
sitting right there:

    /usr/x86_64-w64-mingw32/include/malloc.h:84:10:
    fatal error: 'mm_malloc.h' file not found

`Cross.toml` links clang's copy into the sysroot clang is already searching --
it found `malloc.h` there -- which is enough, and the glob keeps an LLVM bump
in the image from breaking it again.

Only this target is affected: it is the only one whose headers reach
`mm_malloc.h`. The x86-64 and aarch64 musl targets were built clean to check.
Both `ci.yml` and `release.yml` run `cross` from the repository root, so
neither workflow needs a change.
@pull pull Bot locked and limited conversation to collaborators Sep 10, 2026
@pull pull Bot added the ⤵️ pull label Sep 10, 2026
@pull
pull Bot merged commit 5e8d947 into zxfandyy:master Sep 10, 2026
12 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant