Skip to content

net: report the kernel-assigned port after listening on port 0 - #80

Open
0pcom wants to merge 2 commits into
tinygo-org:mainfrom
0magnet:listener-ephemeral-port
Open

net: report the kernel-assigned port after listening on port 0#80
0pcom wants to merge 2 commits into
tinygo-org:mainfrom
0magnet:listener-ephemeral-port

Conversation

@0pcom

@0pcom 0pcom commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Listening on :0 asks the kernel for an ephemeral port, but the listener kept the requested address, so ln.Addr() reported port 0 and the common pattern of binding an ephemeral port and dialing ln.Addr() (used by httptest.NewServer among others) could not work.

Add GetSockname to the host netdev and look it up from listenTCP through an optional interface, so netdevs that cannot report the bound address are unaffected and keep the previous behavior. UDP does not have this problem: ListenUDP already fills in a port of its own before binding.

Verified on linux/amd64 (tinygo dev): Listen("127.0.0.1:0") now reports the real port and dialing ln.Addr().String() connects.

🤖 Generated with Claude Code

https://claude.ai/code/session_01C3X3gvq8ZggMRvzVzWm66i

Listening on ":0" asks the kernel for an ephemeral port, but the listener
kept the requested address, so ln.Addr() reported port 0 and the common
pattern of binding an ephemeral port and dialing ln.Addr() (used by
httptest.NewServer among others) could not work.

Add GetSockname to the host netdev and look it up from listenTCP through
an optional interface, so netdevs that cannot report the bound address
are unaffected and keep the previous behavior. UDP does not have this
problem: ListenUDP already fills in a port of its own before binding.
@yohimik

yohimik commented Sep 5, 2026

Copy link
Copy Markdown

I integrated your 7bf5e52 into the fork and kept your authorship and source commit reference. One small finding: the new TCPAddr assignment drops laddr.Zone. Adding Zone: laddr.Zone preserves it without changing the input address.

Tests and correction are available to use here. TestEphemeralListenDial checks a nonzero port from Listen("tcp4", "127.0.0.1:0"), calls normal Dial with l.Addr().String(), and transfers a byte. It fails on the fork before your patch and passes with your patch. TestEphemeralPreservesZone uses a fake netdev and fails on the original assignment, then passes with the Zone field preserved.

Both tests pass on Linux arm64 and native Darwin arm64 with released TinyGo 0.43.0-net.1, Go 1.26.7, isolated TINYGOROOT, and 15-second process limits. For integration with #74, Accept must retain setSockDefaults(nfd, syscall.SOCK_STREAM) before the new return. TestEphemeralAcceptedNoSigpipe also passes on Darwin and checks the accepted socket option.

No duplicate PR was opened. This remains your port fix. The fork's known close and poller limits are unchanged.

@0pcom

0pcom commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thank you — the Zone drop was real. Your 0f46080 is now cherry-picked here as 7b7aacb, authorship preserved.

One adaptation: ephemeralTestNetdev no longer embeds listenConfigNetdev, which lives in the #82 branch — the fake is self-contained (a nopNetdev with Socket/Bind/Listen/Close succeeding and the fixed GetSockname), so this PR stays independent of ListenConfig. Both tests pass with the dev compiler on linux/amd64: TestEphemeralPreservesZone fails on the old assignment and passes with Zone: laddr.Zone, as you found.

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