Winix.NetCat 0.4.0

Prefix Reserved
dotnet tool install --global Winix.NetCat --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Winix.NetCat --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Winix.NetCat&version=0.4.0
                    
nuke :add-package Winix.NetCat --version 0.4.0
                    

nc

Cross-platform netcat replacement — TCP/UDP send/receive, port checks, TLS clients.

Built for the developer/sysadmin on Windows who's tired of typing Test-NetConnection. Familiar nc muscle memory works (-z, -l, -u, -w, -4, -6), but long-form flags are first-class.

Install

Scoop (Windows)

scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/nc

Winget (Windows, stable releases)

winget install Winix.NetCat

.NET Tool (cross-platform)

dotnet tool install -g Winix.NetCat

Direct Download

Download native binaries from GitHub Releases.

Usage

nc [options] HOST PORT          # connect (default)
nc --listen [options] PORT      # listen for one connection
nc --check [options] HOST PORT  # check port(s)

Examples

# Quick port check
nc -z target.com 443

# Multiple ports
nc -z target.com 80,443,5432

# Range check with JSON
nc -z target.com 1-1024 --json

# TCP send/receive
echo "GET / HTTP/1.0" | nc target.com 80

# TLS client
nc --tls api.example.com 443

# UDP send
nc -u dnsserver 53 < query.bin

# Listen for one connection
nc -l 8080

# File transfer
nc target.com 80 < request.bin > response.bin

Options

Long Short Description
--listen -l Listen for one inbound connection
--check -z Check whether port(s) are open
--udp -u Use UDP (default is TCP)
--tls (also --ssl) Wrap TCP connection in TLS (client only)
--insecure Skip TLS certificate validation
--ipv4 -4 Force IPv4
--ipv6 -6 Force IPv6
--timeout SEC -w SEC Connect/idle timeout
--bind ADDR Listener bind interface
--no-shutdown Don't half-close on stdin EOF
--verbose -v Show closed/timeout ports too in check mode
--json Emit JSON summary to stderr
--describe Agent metadata as JSON
--color / --no-color Colour control
--help / --version Standard

Port Range Syntax

The --check mode accepts single ports, ranges, lists, or mixed specifiers:

nc -z host 80                       # single
nc -z host 80-1000                  # range (inclusive)
nc -z host 80,443,8080              # list
nc -z host 80-100,443,8080-8090     # mixed

Stdout prints one line per open port (suitable for piping). Closed/timeout/error ports go to stderr only under --verbose.

Half-close on stdin EOF

When stdin reaches EOF (e.g. echo request | nc host 80 or nc host 80 < file.bin), nc calls Socket.Shutdown(Send) so the peer sees end-of-stream on its read side. Without this, request/response protocols like HTTP hang because the server waits for more request bytes before sending a response.

Use --no-shutdown only for the rare line-oriented protocols that treat half-close as a full connection close.

Exit Codes

Code Meaning
0 Success
1 Connection refused / unreachable / closed port / TLS failure
2 Timeout
125 Usage error
126 Permission denied (privileged port)
130 Interrupted (Ctrl-C)

Colour

  • Status messages on stderr use colour: green = open, red = closed/error, yellow = timeout/warning.
  • Bytes relayed on stdout are never coloured.
  • Respects NO_COLOR and --color / --no-color.

Part of Winix

nc is part of the Winix CLI toolkit.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.4.0 141 6/13/2026
0.3.0 124 5/26/2026
0.3.0-rc2 123 5/10/2026
0.2.0 123 4/16/2026

## [0.3.0] - 2026-04-24

### Added

- `--ipv4` / `--ipv6` flags now actually honoured in TCP Connect and
 `--check` modes. Previously the flags were advertised in `--help` /
 `--describe` but the resolver ran dual-stack regardless.
- `--check --json` now suppresses per-port text on stdout so downstream
 parsers get a single-stream JSON envelope.
- Check-mode non-verbose stderr summary now covers both all-error AND
 all-timeout cases — `nc -z blackhole 80,443` no longer exits 2 with
 empty stdout and empty stderr.
- `nc --check` emits a JSON summary where `exit_reason` distinguishes
 `all_failed` from `some_failed` when probes return a mix of errors and
 successes.
- `nc --listen -w N` now writes a stderr note when accept times out, so
 the user sees WHY the tool exited with 2 rather than a silent exit.
- `nc -u host port -w N` now writes a stderr warning when no UDP response
 arrives within the timeout (BSD nc precedent is exit 0; the warning
 makes the empty-output case intentional rather than ambiguous).
- JSON error envelope emitted from the safety-net catches when `--json`
 is set. Automation no longer gets a bare stderr crash line on
 unexpected exceptions.
- New JSON field `error` advertised in `--describe` (present only on the
 catch-all envelope).
- New `exit_reason` enum values: `pump_failed` (emitted when the bidirectional
 relay hits an unexpected exception class — ObjectDisposedException or
 InvalidOperationException from a racy half-close — instead of escaping to
 the safety-net), `accept_failed` (listener-side parity for non-SocketException
 accept failures), `unexpected_error` (emitted on the Main safety-net's JSON
 envelope when `--json` is set and an exception escaped all per-site catches),
 and `connect_failed` (TCP/UDP client-side parity for non-SocketException
 connect failures).

### Changed

- TLS handshake timeout is now scoped to the handshake itself rather than
 sharing the TCP connect deadline. A hanging TLS server with `-w N` set
 now times out at N seconds instead of blocking indefinitely.
- TLS close uses `SslStream.ShutdownAsync` (`close_notify`) before the
 TCP write-half shutdown, so strict peers don't log truncation alerts.
- `--bind` now rejects unparseable IPs at parse time (was silently
 falling through to `IPAddress.Any`, defeating the security intent).
- `--bind` now rejects AF mismatch with `--ipv4` / `--ipv6` — e.g.
 `nc -l --ipv6 --bind 127.0.0.1 8080` is a usage error rather than
 silently ignoring `--ipv6`.
- `--bind` rejects hostnames — IP literals only (matches BSD `nc -s`).
- Check-mode port-spec parse failures (`nc -z host invalid`) now return
 a clean usage error instead of a stack trace.

### Fixed

- Non-SocketException / non-OperationCanceledException exception types
 from ConnectAsync / UdpClient.Connect / stdin ReadAsync / stdout
 WriteAsync no longer escape to the safety-net as "unexpected error"
 exit 126. Each path now classifies its failures to a real exit code
 (1 socket_error / 1 io_error / 0 stdout_closed / 1 connect_failed)
 and returns a proper JSON envelope.
- Relay pump now observes both send and receive task outcomes via a
 linked CTS + try/finally. A send-side exception no longer leaves
 the receive task unobserved, and both sides' exceptions are surfaced
 via `ExceptionDispatchInfo` with preserved stack traces.
- Downstream pipe closure during receive (e.g. `nc host 80 | head -c 10`)
 now exits 0 with `exit_reason=stdout_closed`, matching BSD nc's
 SIGPIPE behaviour. Previously mis-classified as `socket_error`.
- `nc -z bad-hostname` no longer aborts the whole scan via Task.WhenAll.
 Per-port errors are now bucketed as Error so the remaining probes run.
- Ctrl-C during UDP Connect send now returns RunResult(130) with partial
 byte counts + duration, so `--json` consumers see a complete envelope.
- NetCatListener UDP path now has user-cancel (Ctrl-C) parity with the
 TCP path — Ctrl-C during `nc -l -u 53` no longer bypasses the JSON
 envelope.
- `RelayPump.ShouldShutdownSend` now reflects actual state — clears
 when the half-close callback fails instead of staying true.
- `onSendComplete` callbacks in NetCatClient and NetCatListener catch
 broadly so a cosmetic half-close failure (racy
 InvalidOperationException, SslStream IOException) cannot mask a
 successful pump outcome.
- TCP accept `SocketException` (Linux interface flap, fd limit) now
 mapped to exit 1 rather than escaping as "unexpected error".
- User-cancel OCE during TLS handshake rethrown so Main's 130 arm fires
 instead of mis-labelling as `tls_failed` / exit 1.
- `--version` output no longer carries the `+gitsha` SourceLink suffix.
 Users see plain `nc 0.3.0`, matching the suite-wide convention.

See full changelog at https://github.com/Yortw/winix/blob/main/src/nc/CHANGELOG.md