Skip to content

fix(wireguard): include ListenPort in buildConfigString#91

Open
Sentinel-Bluebuilder wants to merge 2 commits into
sentinel-official:developmentfrom
Sentinel-Bluebuilder:fix/wg-build-config-string-listenport
Open

fix(wireguard): include ListenPort in buildConfigString#91
Sentinel-Bluebuilder wants to merge 2 commits into
sentinel-official:developmentfrom
Sentinel-Bluebuilder:fix/wg-build-config-string-listenport

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown

Summary

writeConfig (line 148) emits ListenPort = .... buildConfigString (line 177-195) omits it. Since printQRCode uses buildConfigString, QR-imported configs lack the deterministic listen port that file-saved configs carry.

This is an asymmetry, not a behavior debate: the two methods should produce identical [Interface] sections from the same Interface object.

Diff

 let config = "[Interface]\n";
 config += "Address = " + this.interface.addresses.join(",") + "\n";
 config += "PrivateKey = " + this.interface.privateKey + "\n";
+config += "ListenPort = " + this.interface.listenPort.toString() + "\n";
 config += "DNS = " + this.interface.dns.join(",") + "\n";

Test plan

  • npx tsc --noEmit passes
  • No behavior change for writeConfig
  • buildConfigString output now matches writeConfig for the [Interface] core fields

Sentinel-Bluebuilder and others added 2 commits April 26, 2026 15:54
writeConfig emits `ListenPort = ...`; buildConfigString omits it.
printQRCode uses buildConfigString, so QR-imported configs lack the
deterministic listen port that file-saved configs carry. The two paths
now produce identical [Interface] sections for the same Interface.
Per maintainer feedback:
- Remove findFreePorts(1) from parseConfig. A port found free at config-build
  time can be taken before WireGuard binds it (TOCTOU). When no port is given,
  leave listenPort unset so WireGuard auto-selects a free UDP port at bind time.
- listenPort is now an optional parseConfig param and an optional interface field.
- writeConfig and buildConfigString emit 'ListenPort = ...' only when a port was
  explicitly set, keeping the two serializers consistent.
- Drop the now-unused find-free-ports import.
@Sentinel-Bluebuilder

Copy link
Copy Markdown
Author

Reworked per your feedback — two changes beyond the original buildConfigString fix:

1. Dropped the findFreePorts(1) TOCTOU. The old parseConfig probed for a free port and wrote it as ListenPort. As you noted, that port can be claimed by something else between the probe and the moment WireGuard actually binds — a classic time-of-check/time-of-use gap. It's now removed entirely.

2. ListenPort is optional end to end. When no port is supplied, the interface leaves listenPort unset and WireGuard auto-selects a free UDP port at bind time (kernel-level, no race). To force a fixed port, pass it explicitly:

  • listenPort?: number is now an optional last param on parseConfig(...).
  • listenPort? on the Interface type.
  • Both writeConfig and buildConfigString now emit ListenPort = ... only when it's defined, so the two serializers stay consistent (the original bug was buildConfigString silently omitting a line writeConfig wrote).

The now-unused find-free-ports import is removed.

Note on asymmetry with the v2ray SOCKS-port PR: there I put the optional port on the constructor, here it's on parseConfig. The WG Interface only exists after the handshake (it depends on handshakeData.addrs), so there's no meaningful "construction time" to set it — parseConfig is the natural place. Open to aligning if you'd rather both be constructor params.

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.

1 participant