Home / Alt manpages / nc_openbsd(1)

  • nc_openbsd(1)
  • User command
  • linux

Test TCP Services Safely with OpenBSD nc

At least three tools called "netcat" exist, and they do not share a flag set, which is how a working command from a forum post fails flat on your box. This walks through the OpenBSD build of nc: proving a TCP path on loopback, sending a payload, reading a plain HTTP response, and checking a handful of ports. Packaged here as netcat-openbsd 1.226-1ubuntu2, with the binary usually just called nc, and nc.openbsd or netcat pointing at the same tool.

Allow fifteen minutes and the netcat-openbsd package. Nothing here needs sudo unless the service you are testing is itself restricted, or you deliberately want a privileged source port. Do not scan systems or ports you do not have permission to touch.

1. Confirm which nc you actually have

Check the binary, package version and local option set before you copy anyone's example, this one included:

$ command -v nc
/usr/bin/nc
$ dpkg-query -W -f='${Package} ${Version}\n' netcat-openbsd
netcat-openbsd 1.226-1ubuntu2
$ nc -h 2>&1 | sed -n '1,4p'
OpenBSD netcat (Debian patchlevel 1.226-1ubuntu2)
usage: nc [-46CDdFhklNnrStUuvZz] ...

This guide is written for that exact installed command. Notably, this build has no -e or -c for spawning a shell or wrapping a TLS connection. Do not borrow flags from a guide written for a different netcat; they will not exist here.

Checkpoint

If command -v nc points somewhere unexpected, run readlink -f "$(command -v nc)" and work out which implementation you actually have before going further.

2. Prove a TCP path on loopback

Open two terminals. In the first, start a listener on loopback: binding to 127.0.0.1 keeps the whole test off the network, and port 43123 here is only an example, pick another free high port if it is taken.

$ nc -l 127.0.0.1 43123

That process now waits for one incoming connection. In the second terminal, send a line and let the client close its side once standard input hits EOF:

$ printf 'loopback-check\n' | nc -N 127.0.0.1 43123

The first terminal should print loopback-check. -l listens, -N tells the client to shut down its network socket after EOF on input; skip that half-close and a service can sit there waiting for more data, which looks exactly like a hang.

Once the listener exits, nothing persistent has changed. If you started one with -k, stop it with Ctrl-C: -w's timeout does not apply to a listener, so it will not do that for you.

Checkpoint

A clean test shows the exact payload in the listener's terminal and both nc processes have exited.

3. Move a file, but only when you control both ends

Netcat moves raw bytes and nothing else: no authentication, no encryption, no integrity check, no filename. Keep this between hosts and processes you control on a network you trust, and reach for something like SSH when the data is sensitive.

On the receiving host, pick a fresh output path and listen:

$ nc -l 127.0.0.1 43124 > received.bin

On the sending host, connect and feed in the file. -N lets the receiver see EOF and finish cleanly:

$ nc -N RECEIVER_IP 43124 < ./original.bin

Check the result before you trust it with anything important:

$ sha256sum ./original.bin received.bin
HASH  ./original.bin
HASH  received.bin

Those two hashes need to match. If the transfer drops partway through, delete the incomplete output before trying again, but check the path first: the listener truncates its destination via shell redirection the moment the command starts, so an existing file there is gone as soon as you press Enter.

4. Ask a plain HTTP service one precise question

For a service taking unencrypted HTTP on a known port, pipe a full request straight in. The blank line ends the headers:

$ printf 'GET /health HTTP/1.0\r\nHost: SERVICE_HOST\r\n\r\n' | nc -w 5 SERVICE_HOST 80

Expect a status line, headers, and a body if the service sends one. Swap in a host and path you are actually authorised to test, and remember port 80 is not HTTPS: do not send credentials or anything private down it.

-C translates outgoing line feeds to CRLF for a line-oriented protocol that expects it. Do not add that on top of input that already has correct line endings without checking the result first: doubled carriage returns can make the request invalid.

5. Scan a small, approved range

Zero-I/O mode tells you only whether a TCP connection can be made, nothing more. Add -v for readable output, -n to skip DNS lookups, and -w 1 to keep each attempt short:

$ nc -zvn -w 1 HOST_YOU_OWN 22 80 443
Connection to HOST_YOU_OWN 22 port [tcp/ssh] succeeded!
nc: connect to HOST_YOU_OWN port 80 (tcp) failed: Connection refused

Output varies by host and firewall. A successful connection means something accepted TCP there at that moment; it does not identify the application or prove the service is healthy. Refused, timed out and filtered are three different stories, not one.

You can scan a numeric range such as 8000-8010, but keep it narrow and cleared with whoever owns the target. The local manpage says ports in a range go in increasing order unless you pass -r. UDP behaves differently again: -uz reports success regardless of what is actually on the other end, so never treat it as proof a UDP service is up.

6. The traps that waste the most time

  • Nothing connects. Check the address family with -4 or -6 first, then the destination and port. -v gives you the diagnostics.
  • The command just sits there. A client has no default timeout. Add -w 5 for a five-second connect or idle limit; it does nothing for -l.
  • A hostname fails to resolve. Try a numeric address plus -n to separate a network failure from a DNS one.
  • Local binding fails. Either the port is already in use, or -p asked for a source port you are not allowed to use. Let the kernel pick unless the test genuinely needs a fixed source port.
  • Output looks garbled. nc just copies bytes between stdin, stdout and the socket, so redirect stderr separately when you are parsing output in a script.

Do not leave a diagnostic listener open on every interface in a shared or untrusted environment: a listener is a network service like any other. Stop it once you are done, and check with ss -ltn if you are not sure it actually went away.

Done means

  • Confirmed the binary. You checked the OpenBSD netcat binary and its package version before trusting any example.
  • Proved loopback works. A local TCP exchange completed using -l and -N.
  • Understand the file-transfer risk. You know that transfer is raw and unauthenticated, not a substitute for a real transfer protocol.
  • Made one clean HTTP request. You did not confuse a plain HTTP test with HTTPS.
  • Scanned only what you were allowed to. Any scan used a narrow, authorised range with a timeout.
  • Left nothing running. Every temporary listener was stopped and no persistent service configuration changed.