Same Job, Different Shell Part 8: Port and Service Testing

Part 1 covered ICMP reachability and flagged that a failed ping proves almost nothing about whether a service is up. This is the follow-through: testing the actual TCP port a service listens on, which is the check that matters.

Linux: nc, curl, and the bash built-in nobody remembers

nc (netcat) is the standard tool for a raw port check:

$ nc -zv -w3 1.1.1.1 443
Connection to 1.1.1.1 443 port [tcp/https] succeeded!

-z scan mode, don’t send data, just test the connection; -v verbose; -w3 give up after 3 seconds. That’s the “open” case. Here’s what a filtered port looks like, captured live against the same host on a port nothing is listening on:

$ nc -zv -w3 1.1.1.1 12345
nc: connect to 1.1.1.1 port 12345 (tcp) timed out: Operation now in progress

That distinction matters and it’s worth internalizing precisely: a timeout means something dropped the SYN silently, most likely a firewall. A fast, immediate “connection refused” means the packet arrived and the host actively rejected it (RST sent back), which usually means nothing’s listening but the host itself is reachable. Same failure from the outside (“can’t connect”), completely different cause, and nc’s behavior tells you which one you’re looking at without needing a packet capture.

curl works for this too, when the service in question is actually HTTP(S), and it’s often already open in a terminal:

$ curl -s -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" https://1.1.1.1
HTTP 200 in 0.460s

And if neither tool is installed, which happens more often than you’d expect on a minimal container image, bash itself can open a raw TCP socket with no external binary at all:

$ (echo > /dev/tcp/1.1.1.1/443) && echo "port 443 open"
port 443 open

/dev/tcp/<host>/<port> is a bash built-in pseudo-device, not a real file; redirecting to it opens a TCP connection, and the echo’s success or failure tells you whether it connected. It doesn’t do anything with the connection beyond opening it, which is exactly enough for a yes/no port check.

Windows: Test-NetConnection -Port

Here’s real, verified output rather than a trimmed documentation example:

PS> Test-NetConnection -ComputerName 1.1.1.1 -Port 443 -InformationLevel Detailed

ComputerName            : 1.1.1.1
RemoteAddress           : 1.1.1.1
RemotePort              : 443
NameResolutionResults   : 1.1.1.1
                          one.one.one.one
MatchingIPsecRules      :
NetworkIsolationContext : Internet
IsAdmin                 : False
InterfaceAlias          : WiFi
SourceAddress           : 192.168.1.50
NetRoute (NextHop)      : 192.168.1.1
TcpTestSucceeded        : True

That’s more fields than the trimmed examples in Microsoft’s own docs usually show, worth knowing what each one means rather than skimming past it. NameResolutionResults is the interesting one here: the target was the literal IP 1.1.1.1, no hostname involved, but the result includes one.one.one.one anyway, that’s Cloudflare’s own reverse-DNS (PTR) record for that address. Test-NetConnection does a reverse lookup even when you hand it a raw IP and reports back whatever hostname it finds, not just the address you asked about. MatchingIPsecRules is blank here because no IPsec policy applies to this connection, it’ll list a rule name if one does. NetworkIsolationContext reflects which of Windows’ network profile categories (Internet, Private, or None) this traffic is considered to be crossing, relevant if a firewall rule is scoped to a specific profile and the connection isn’t matching it for that reason. IsAdmin reflects whether the current PowerShell session is elevated, some of Test-NetConnection’s deeper diagnostics (route diagnostics in particular) need admin rights to run fully and will silently return less detail without it.

One command does what nc -zv plus a manual DNS lookup plus a route check would take three commands to assemble on Linux: name resolution, the route it’ll take, and the actual TCP connect result, all in one object. TcpTestSucceeded is the boolean you actually care about, and it’s directly usable in a script without parsing anything:

if ((Test-NetConnection -ComputerName www.contoso.com -Port 443).TcpTestSucceeded) { "open" } else { "closed or filtered" }

That last bit is worth being honest about: unlike nc, Test-NetConnection doesn’t distinguish a timeout from a refusal in its return value the way the Linux tools do above. TcpTestSucceeded is False either way. If you need to tell “filtered by a firewall” apart from “nothing listening, connection actively refused” on Windows, you’re back to reading the raw timing (a refusal comes back near-instantly, a filtered port takes the full timeout) or reaching for a packet capture, which is Part 10.

For well-known services, -CommonTCPPort saves you memorizing port numbers:

PS> Test-NetConnection -ComputerName fileserver01 -CommonTCPPort SMB
PS> Test-NetConnection -ComputerName dc01 -CommonTCPPort WINRM

Valid values include HTTP, RDP, SMB, and WINRM, the ports an actual Windows admin tests against most often, which is a nice bit of platform-specific convenience Linux’s generic tools don’t bother with because they’re not scoped to any one OS’s common services.

Quick reference

What you wantLinuxWindows
Basic open/closed checknc -zv <host> <port>Test-NetConnection -ComputerName <host> -Port <port>
No nc/netcat installed(echo > /dev/tcp/<host>/<port>) && echo openn/a, Test-NetConnection is always available
HTTP-aware checkcurl -o /dev/null -w "%{http_code}" <url>Test-NetConnection -ComputerName <host> -CommonTCPPort HTTP
Well-known service shortcutmanual port number-CommonTCPPort SMB|RDP|HTTP|WINRM
Distinguish filtered vs refusedtimeout vs instant “refused” (nc shows this directly)not exposed directly; infer from response timing
Boolean result for scriptingexit code.TcpTestSucceeded

What’s next

Part 9 covers reading firewall state without changing it: iptables -L, nft list ruleset, and ufw status against Get-NetFirewallRule and netsh advfirewall show, the read-only “what’s currently allowed” check that usually comes right after a port test comes back filtered.

Cross-references: for nc’s full range beyond port testing (relays, file transfer, reverse shells and catching them) see Netcat: The Swiss Army Knife of TCP/IP; for what’s actually happening on the wire when a connection gets refused rather than timing out, see Who Sent That RST? Forensic Classification of TCP Resets.