Skip to content

Report closed ports as closed on Windows - #498

Merged
fstubner merged 1 commit into
mainfrom
fix/windows-closed-ports
Oct 2, 2026
Merged

fstubner merged 1 commit into
mainfrom
fix/windows-closed-ports

Conversation

@fstubner

@fstubner fstubner commented Oct 2, 2026

Copy link
Copy Markdown
Owner

On Windows every closed port came back as filtered. Windows retries a SYN that was answered with a RST for about 2 s before connect() reports the refusal, and the scanner stops waiting at 500 ms.

The scanner now connects through a socket with SYN retransmissions turned off (SIO_TCP_INITIAL_RTO), so the first RST is final. With no retransmissions Windows gives up after the initial retransmission timeout, 1 s by default, so that is set to the scan timeout as well; otherwise a longer timeout would be cut to 1 s.

Measured (Windows 11, release build, scan -p 1-1024)

Host main this branch
LAN machine that sends RSTs 1022 filtered, 2 open 1022 closed, 2 open
localhost 1019 filtered 1019 closed
Host that drops packets (no RST) 1024 filtered 1024 filtered

Single connects to a closed port: 2025 to 2047 ms before, 0 to 5 ms after. A port that drops packets still times out at the requested timeout (checked at 300 ms and 3000 ms).

Tests

The closed-port test allowed Filtered, which is how this went unnoticed. It now requires Closed within 500 ms. With the ioctl commented out it fails (left: Filtered, right: Closed).

Linux and macOS are unchanged apart from the connect going through TcpSocket instead of TcpStream::connect.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Site preview: https://pr-498.netscli-site-preview.pages.dev

Built from 540f7fd with NETSCLI_PREVIEW=1 — noindex, and analytics disabled so it does not report into netscli.com's numbers.

Production is unaffected: netscli.com is served from GitHub Pages via pages.yml, which is manual-only.

Windows retries a SYN answered with a RST for about 2 s before connect()
fails with WSAECONNREFUSED. The scanner's default timeout is 500 ms, so
every closed port timed out first and read as filtered.

Connect through a socket with SYN retransmissions off (SIO_TCP_INITIAL_RTO,
MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS), which makes
the first RST final. Without retransmissions Windows gives up after the
initial RTO (1 s by default), so set that to the caller's timeout too.

The closed-port test accepted filtered, which is how this went unnoticed;
it now requires closed within 500 ms and fails without the ioctl.
@fstubner
fstubner force-pushed the fix/windows-closed-ports branch from 07f2f28 to cb94089 Compare October 2, 2026 22:41
@fstubner
fstubner merged commit a3465c7 into main Oct 2, 2026
19 checks passed
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