Skip to main content
RunBook Academy

LinuxXXII · Network TroubleshootingProbing

nc and openssl s_client - probing TCP and TLS

Intermediate⏱ ~12 minncatopenssl

What you'll learn

  • Use nc to test TCP and UDP connectivity
  • Send and receive raw data with nc
  • Use openssl s_client to probe TLS endpoints
  • Verify chain and hostname explicitly, and read names from subjectAltName
  • Diagnose application-protocol problems with these tools

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

nc (netcat) and openssl s_client are the tools that answer “can I reach this service, and if so, what does it say?”. They probe specific ports without needing a full client.

nc basics

Modern Linux ships ncat (from the nmap project) rather than the original netcat. The flags are largely compatible:

nc -vz host 80                    # scan TCP port 80
nc -vz host 80 443                # scan multiple ports
nc -vzu host 53                   # scan UDP port 53
nc -w 3 -vz host 80               # 3s timeout
nc -vz -w 3 host 80-90            # port range
nc host 80                        # connect and read/write stdin/stdout

-v is verbose (output regardless of result). -z is zero-I/O (just check if the port accepts connections). -u is UDP.

Output:

Connection to host 80 port [tcp/http] succeeded!
Connection to host 81 port [tcp/81] failed: Connection refused
Connection to host 82 port [tcp/82] failed: Connection timed out

The three outcomes map to:

  • succeeded: port is open, something is listening.
  • Connection refused: target is up but nothing is listening on the port (target sent RST).
  • Connection timed out: nothing replied at all (firewall drop, host down, network problem).

Send and receive

nc reads from stdin and writes to stdout once connected:

echo "GET / HTTP/1.0" | nc -w 5 example.com 80

Manually:

nc example.com 80
GET / HTTP/1.0
Host: example.com

HTTP/1.0 200 OK
...

q to exit (or Ctrl-D).

TCP and UDP servers

nc can also run as a listener:

nc -l -p 8080                     # listen on TCP 8080
nc -u -l -p 5353                  # listen on UDP 5353
nc -l -p 8080 > received.txt      # capture incoming data

Useful for testing firewall rules (“can the remote host reach me on port 8080?”) and for ad-hoc data transfer.

TLS probing with openssl s_client

openssl s_client connects to a TLS endpoint and shows the handshake details:

openssl s_client -connect example.com:443                # basic
openssl s_client -connect example.com:443 -servername example.com   # SNI
openssl s_client -connect example.com:443 -showcerts      # full cert chain
openssl s_client -connect example.com:443 -tls1_2        # force TLS 1.2
openssl s_client -connect example.com:443 -no_ign_eof     # interactive
openssl s_client -connect example.com:443 </dev/null     # one-shot, no input

Output:

CONNECTED(00000003)
---
Certificate chain
 0 s:CN = example.com
   i:CN = Example CA R3
   ...
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIfj...
-----END CERTIFICATE-----
---
No client certificate CA names sent
---
SSL handshake has read 12345 bytes and written 5678 bytes
Verification: OK
---
New, TLSv1/SSLv3, Cipher: TLS_AES_128_GCM_SHA256
...

What to look for:

  • Certificate chain: every cert from server to root. If the chain is incomplete, the client cannot verify.
  • Verification: OK: the chain was verified against the local trust store. Any other result (unable to get local issuer, self signed certificate) is a problem. It does not mean the hostname matched - see below.
  • Cipher: the negotiated cipher. Check this matches your policy.
  • Protocol: TLSv1.2 or TLSv1.3. Anything older is a security finding.
  • Verify return code: 0 means OK. 21 means unable to verify (untrusted chain). 62 means hostname mismatch, and you will only ever see it if you asked for the check.

Test specific TLS features

# Verify the chain AND the hostname, and fail on a verify error
openssl s_client -connect example.com:443 -servername example.com \
    -verify_hostname example.com -verify_return_error \
    -CAfile /etc/ssl/certs/ca-certificates.crt </dev/null
# expect both "Verification: OK" and "Verify return code: 0 (ok)"

# Inspect the names the certificate is actually valid for
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# Check the certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

# Check the negotiated cipher
openssl s_client -connect example.com:443 </dev/null 2>&1 | grep 'Cipher'

# Check the negotiated protocol
openssl s_client -connect example.com:443 </dev/null 2>&1 | grep 'Protocol'

# Verify with a specific CA bundle
openssl s_client -connect example.com:443 -CAfile /etc/ssl/certs/ca-certificates.crt </dev/null

Read the names from subjectAltName, not from the subject. Browsers have required SAN since 2017 and ignore the CN entirely, so a certificate whose CN looks right but whose SAN list omits the name is rejected by every browser while -subject shows exactly what you hoped to see. That is why the inspection command above asks for -ext subjectAltName. OpenSSL is more forgiving - -verify_hostname falls back to the CN when a certificate carries no SAN at all - so a name that passes s_client can still fail in a browser.

The verification result is worth spelling out:

# without the flags - trusted chain, wrong name, no complaint
Verification: OK
Verify return code: 0 (ok)

# with -verify_hostname and -verify_return_error
verify error:num=62:hostname mismatch
Verification error: hostname mismatch
Verify return code: 62 (hostname mismatch)

Same server, same certificate. Only the second command is a test.

STARTTLS for SMTP, IMAP, etc.

Some protocols encrypt on top of an existing plaintext connection:

openssl s_client -connect mail.example.com:587 -starttls smtp
openssl s_client -connect imap.example.com:143 -starttls imap

-starttls <protocol> does the protocol-specific STARTTLS handshake after the TCP connection is open.

Diagnostic patterns

“The website is down”:

nc -vz example.com 80              # check TCP
nc -vz example.com 443             # check TLS
openssl s_client -connect example.com:443 </dev/null   # TLS handshake
curl -I https://example.com        # full request

“TLS handshake fails”:

openssl s_client -connect example.com:443 -servername example.com \
    -verify_hostname example.com -verify_return_error </dev/null
# Look for: certificate verify failed, hostname mismatch, alert,
# handshake failure

Use the verifying form here. The bare form succeeds against a certificate issued for the wrong name, which is the failure you are most likely hunting.

“Service is listening but application fails”:

nc -vz host port                   # confirm port open
echo "PROTOCOL_COMMAND" | nc host port   # send a probe

Knowledge check

Knowledge check · 5 questions

  1. Q1. What does nc -vz return when nothing is listening on the port?

  2. Q2. openssl s_client can speak HTTP.

  3. Q3. Which of the following are valid openssl s_client uses? Select all that apply.

  4. Q4. openssl s_client prints "Verification: OK" for a trusted certificate even when it was issued for a completely different hostname.

  5. Q5. A rollout renames a service from api-old.example.com to api.example.com. Browsers now report a certificate error, but your check - `openssl s_client -connect api.example.com:443 -servername api.example.com` - prints "Verification: OK". What is the most likely explanation, and what should you run?

Passing score: 75%. Answers are checked in this browser.