open
https://gitlab.synchro.net/main/sbbs/-/work_items/1293
## Summary
Whether a listening socket gets `SO_REUSEADDR` is currently left to `ctrl/sockopts.ini`, and the right answer differs by platform:
- **Unix-like:** `SO_REUSEADDR` is the standard, safe choice for a listener. It only lets a server rebind its port while connections from the previous listener are still in `FIN_WAIT_2`/`TIME_WAIT`; it does not let two live listeners share a port. The stock `sockopts.ini` does not enable it (it appears only in a comment).
- **Windows:** `SO_REUSEADDR` is not needed to rebind over `TIME_WAIT`, and what it does instead is allow a second socket to bind a port that already has an active listener. The safe Windows option for a listener is `SO_EXCLUSIVEADDRUSE`.
## Consequences
**Unix-like, stock config:** after a recycle or restart with connections still closing, `bind()` fails with `EADDRINUSE` until those connections expire (up to `tcp_fin_timeout` plus 60 seconds of `TIME_WAIT`). `retry_bind()` covers part of this, but the defaults (`BindRetryCount=2`, `BindRetryDelay=15`) allow only 30 seconds. Sysops who set `REUSEADDR = 1` or raise `BindRetryCount` don't see it. Related: #402.
**Windows, wherever `REUSEADDR` is enabled:**
- A second Synchronet instance starting on the same ports binds successfully instead of failing with "address in use", and the two then split incoming connections unpredictably.
- Another process under the same account (or an administrator) can bind the port and take connections.
- The leaked-listener scenario in #1151 relied on exactly this: the new parent could still bind via `SO_REUSEADDR` while an orphaned child held the old listener.
Because `sockopts.ini` options in the root section apply to every socket, a single `ctrl/` shared between Unix and Windows hosts can't hold a setting that is right for both.
## Code locations
- `xpdev/multisock.c` `xpms_add()`: creates every server listener (Terminal, Mail, FTP, Web, Services, and JS `ListeningSocket`). It sets options only via the `sock_init` callback (`set_socket_options()`, i.e. `sockopts.ini`).
- `sbbs3/mqtt_broker.cpp` (IPv4 and IPv6 listeners): sets `SO_REUSEADDR` unconditionally, including on Windows.
- Not affected: `sbbs3/ftpsrvr.cpp` sets `SO_REUSEADDR` on the outgoing active-mode data socket so several transfers can share local port 20. That is correct on both platforms. PASV explicitly clears it.
## Proposed fix
In `xpms_add()` and the MQTT broker listener, before `bind()`:
- Unix-like: set `SO_REUSEADDR`.
- Windows: set `SO_EXCLUSIVEADDRUSE`, and never `SO_REUSEADDR`.
Then either ignore `REUSEADDR` from `sockopts.ini` for listeners on Windows, or document that it is unsafe there.
-- *Authored by Claude (Claude Code), on behalf of @rswindell*
--- SBBSecho 3.38-Linux
* Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)