Networking Interview Prep
Diagnostic Tools

Essential Network Tools

curl, netstat, nslookup, dig & telnet

LinkedIn Hook

Every backend engineer has stared at a broken API and thought: "Is it the server? The DNS? The firewall? My code?"

The difference between a junior who guesses and a senior who knows?

Five commands.

nslookup — Is DNS resolving correctly? dig — What do the actual DNS records say? curl — Is the API actually returning what I think it is? netstat / ss — Is my server even listening on that port? telnet / nc — Is that remote port open at all?

These are the tools that let you point your finger at the exact layer where things are breaking. Network? DNS? Application? Firewall? You'll know in 60 seconds.

Lesson 7.3 is live — practical examples, annotated command output, and the cheat sheet you'll actually use on the job.

Read the full lesson → 03-other-tools.md

#Networking #curl #DNS #dig #InterviewPrep #BackendDevelopment #SoftwareEngineering #SystemDesign #DevTools


Essential Network Tools thumbnail


What You'll Learn

  • How to use nslookup for quick DNS lookups on any OS including Windows
  • How to use dig for deep, authoritative DNS inspection with full trace output
  • How to use curl to test APIs, debug headers, authentication, and redirects
  • How to use netstat and ss to find what is listening on which port and why
  • How to use telnet and nc to verify a remote port is open before blaming the code
  • How to read the raw output from each tool and extract the signal from the noise
  • Which tool to reach for at each layer of the debugging stack

Tool 1 — nslookup: DNS Lookup the Simple Way

What It Is

nslookup (Name Server Lookup) is a command-line tool for querying DNS records. It ships natively on Windows, macOS, and Linux, making it the most universally available DNS tool in your arsenal. It runs in one-shot mode (pass the domain as an argument) or interactive mode (enter nslookup alone and type queries).

Basic Usage

nslookup google.com

Annotated output:

Server:         192.168.1.1       <- which resolver answered your query (your router/ISP/configured DNS)
Address:        192.168.1.1#53    <- resolver IP and port (53 = DNS)

Non-authoritative answer:         <- came from cache, not the authoritative nameserver directly
Name:   google.com                <- the canonical name you queried
Address: 142.250.80.46            <- the A record (IPv4 address) returned
Address: 142.250.80.78            <- Google often returns multiple IPs for load balancing

The "Non-authoritative answer" line is normal — it means your resolver had the answer cached. Only authoritative nameservers (Google's own DNS servers) give authoritative answers.

Query a Specific Record Type

nslookup -type=MX google.com      # Mail exchange records — where does Gmail receive email?
nslookup -type=AAAA google.com    # IPv6 address record
nslookup -type=TXT google.com     # TXT records — SPF, DKIM, domain verification tokens
nslookup -type=NS google.com      # Which nameservers are authoritative for this domain?
nslookup -type=CNAME www.github.com  # Canonical name alias

MX record output:

Server:         192.168.1.1
Address:        192.168.1.1#53

Non-authoritative answer:
google.com      mail exchanger = 10 smtp.google.com.   <- priority 10 (lower = higher priority)

Query a Specific DNS Server

nslookup google.com 8.8.8.8       # ask Google's public resolver directly
nslookup google.com 1.1.1.1       # ask Cloudflare's resolver instead

This is useful for comparing what different resolvers return — helpful when diagnosing DNS propagation issues after a DNS change.

Interactive Mode

nslookup                          # enters interactive prompt
> google.com                      # query any domain
> set type=MX                     # change record type
> github.com                      # query MX records for GitHub
> server 8.8.8.8                  # switch to a different resolver
> exit

nslookup vs dig: When to Use Which

SituationUse
Windows machine with no other toolsnslookup
Quick cross-platform DNS checknslookup
Need full TTL, flags, authority sectiondig
Tracing the full DNS resolution chaindig +trace
Scripting / parsing DNS outputdig +short

Tool 2 — dig: DNS Lookup the Authoritative Way

What It Is

dig (Domain Information Groper) is the professional DNS query tool. It gives you the complete picture: every section of the DNS response, TTLs, query timing, which server answered, and the full resolution chain. It is standard on macOS and Linux. On Windows, install it via BIND tools or use WSL.

Basic Usage

dig google.com

Fully annotated output:

; <<>> DiG 9.18.1 <<>> google.com        <- dig version and what you queried

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;;                                         ^-- status is the key field:
;;                                             NOERROR   = found it
;;                                             NXDOMAIN  = domain does not exist
;;                                             SERVFAIL  = resolver had an error
;;                                             REFUSED   = resolver refused the query

;; QUESTION SECTION:
;google.com.                    IN      A         <- what you asked: A record for google.com

;; ANSWER SECTION:
google.com.             255     IN      A       142.250.80.46
;;  ^domain             ^TTL    ^class  ^type   ^value
;;                      255 seconds until this cache entry expires

;; AUTHORITY SECTION:
google.com.             52779   IN      NS      ns1.google.com.   <- authoritative nameservers
google.com.             52779   IN      NS      ns2.google.com.   <- for this domain

;; ADDITIONAL SECTION:
ns1.google.com.         21599   IN      A       216.239.32.10     <- "glue records" — IPs of NSes
ns2.google.com.         21599   IN      A       216.239.34.10     <- so you can reach the NS servers

;; Query time: 12 msec                           <- how long the lookup took
;; SERVER: 192.168.1.1#53(192.168.1.1)           <- which resolver answered
;; WHEN: Tue Apr 22 10:00:00 UTC 2026
;; MSG SIZE  rcvd: 111                            <- response packet size in bytes

Query Specific Record Types

dig google.com MX           # mail exchange records
dig google.com AAAA         # IPv6 address records
dig google.com TXT          # text records (SPF, DKIM, domain verification)
dig google.com NS           # nameserver records
dig google.com SOA          # Start of Authority — serial number, refresh intervals
dig google.com CNAME        # canonical name alias

Short Output — Just the Answer

dig +short google.com
# 142.250.80.46
# 142.250.80.78

dig +short google.com MX
# 10 smtp.google.com.

dig +short google.com NS
# ns1.google.com.
# ns2.google.com.
# ns3.google.com.
# ns4.google.com.

+short is perfect for scripting — no headers, just the values.

Full Resolution Trace — Following the Chain

dig +trace google.com

This is one of the most powerful debugging tools in networking. It shows the complete path from root servers down to the authoritative answer:

.                       518400  IN      NS      a.root-servers.net.    <- Step 1: ask root servers
.                       518400  IN      NS      b.root-servers.net.       who handles .com?

com.                    172800  IN      NS      a.gtld-servers.net.    <- Step 2: .com TLD servers
com.                    172800  IN      NS      b.gtld-servers.net.       tell us who handles google.com

google.com.             345600  IN      NS      ns1.google.com.        <- Step 3: Google's own
google.com.             345600  IN      NS      ns2.google.com.           nameservers answer

google.com.             300     IN      A       142.250.80.46          <- Step 4: final answer
;; Received 55 bytes from 216.239.32.10#53(ns1.google.com.) in 4 ms

Use +trace when: DNS changes are not propagating, you suspect a broken delegation, or you want to verify which nameserver is actually authoritative.

Query a Specific Resolver

dig @8.8.8.8 google.com          # use Google's public DNS
dig @1.1.1.1 google.com          # use Cloudflare's DNS
dig @9.9.9.9 google.com          # use Quad9 DNS
dig @192.168.1.1 google.com      # query your local router's resolver

Tool 3 — curl: HTTP from the Terminal

What It Is

curl (Client URL) is a command-line HTTP client. It lets you make HTTP requests, inspect headers, send JSON bodies, handle authentication, and follow redirects — all without a browser or Postman. If you work with APIs, curl is non-negotiable.

Basic GET Request

curl https://api.github.com/users/octocat

This sends a GET request and prints the response body. The output is the raw JSON (or HTML, or whatever the server returns).

Verbose Mode — See Everything

curl -v https://example.com

Annotated verbose output:

*   Trying 93.184.216.34:443...        <- TCP connection attempt
* Connected to example.com (93.184.216.34) port 443  <- TCP connected
* ALPN: offers h2,http/1.1             <- TLS: negotiating HTTP version
* TLSv1.3 (OUT), TLS handshake, Client hello     <- TLS handshake starting
* TLSv1.3 (IN),  TLS handshake, Server hello     <- server responds
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384  <- cipher suite agreed
* Server certificate: example.com      <- cert details follow
* issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS RSA SHA256 2020 CA1
* SSL certificate verify ok.           <- cert is valid and trusted

> GET / HTTP/1.1                       <- request line (method + path + version)
> Host: example.com                    <- Host header (required in HTTP/1.1)
> User-Agent: curl/7.88.1              <- curl identifies itself
> Accept: */*                          <- will accept any content type

< HTTP/1.1 200 OK                      <- response status line
< Content-Type: text/html; charset=UTF-8  <- response headers start with <
< Cache-Control: max-age=604800
< Content-Length: 1256

<!doctype html>                        <- response body
<html>...

Lines starting with * are connection info. Lines with > are your request. Lines with < are the server's response headers.

Just the Headers (HEAD Request)

curl -I https://example.com

Sends a HEAD request — gets response headers only, no body. Fast for checking Content-Type, Cache-Control, CORS headers, server type, and redirect targets.

HTTP/2 200
content-type: text/html; charset=UTF-8
cache-control: max-age=604800
etag: "3147526947"
last-modified: Thu, 17 Oct 2019 07:18:26 GMT

POST Request with JSON Body

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"name": "test", "email": "user@example.com"}' \
  https://api.example.com/users

Breaking it down:

  • -X POST — override the HTTP method
  • -H "..." — add a request header (use multiple -H flags for multiple headers)
  • -d '...' — the request body (automatically sets Content-Length)

Authentication Headers

# Bearer token (JWT, OAuth)
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." https://api.example.com/protected

# Basic auth (encodes username:password in Base64)
curl -u username:password https://api.example.com/protected

# API key as header
curl -H "X-API-Key: your-api-key-here" https://api.example.com/data

Follow Redirects

curl -L https://example.com          # follow 301/302 redirects automatically
curl -Lv https://example.com         # follow redirects AND show each step verbosely

Without -L, curl stops at the redirect response and shows you the 301/302. With -L, it follows the chain to the final destination.

Save Response to File

curl -o output.json https://api.example.com/data       # save to named file
curl -O https://example.com/file.zip                   # save with server's filename

Useful One-Liners

# Check HTTP status code only (no body, no progress bar)
curl -s -o /dev/null -w "%{http_code}" https://example.com
# output: 200

# Show response time breakdown
curl -s -o /dev/null -w "dns:%{time_namelookup}s tcp:%{time_connect}s tls:%{time_appconnect}s total:%{time_total}s" https://example.com
# output: dns:0.023s tcp:0.045s tls:0.123s total:0.187s

# Silent mode (suppress progress bar, useful in scripts)
curl -s https://api.example.com/data | jq .

# Send a custom HTTP method
curl -X DELETE https://api.example.com/users/42
curl -X PATCH -H "Content-Type: application/json" -d '{"status":"active"}' https://api.example.com/users/42

Debugging Auth and CORS Issues

When an API call fails, run it with -v to immediately see:

  1. Whether the TLS handshake succeeded
  2. Which request headers you actually sent (not what you thought you sent)
  3. The exact response headers the server returned (look for WWW-Authenticate, Access-Control-Allow-Origin, X-RateLimit-Remaining)
  4. The full response body including error messages
curl -v -H "Authorization: Bearer TOKEN" https://api.example.com/protected 2>&1 | less

Piping to less lets you scroll through the full verbose output at your own pace.


Tool 4 — netstat / ss: What Is Listening on My Machine?

What They Are

netstat (Network Statistics) shows all active network connections, listening ports, and socket states. ss (Socket Statistics) is the modern replacement — faster, more features, and the preferred tool on modern Linux systems. On macOS, netstat is still common. On Windows, netstat is built in.

Core Commands

# Show ALL connections and listening ports, numeric (no hostname resolution)
netstat -an

# Linux: TCP listening sockets with process info
netstat -tlnp

# Modern Linux — prefer ss
ss -tlnp          # TCP listening with process info
ss -an            # all sockets, numeric
ss -tunp          # TCP + UDP, numeric, with process

Flag breakdown for netstat -tlnp:

  • -t — TCP only
  • -l — listening sockets only (not established connections)
  • -n — numeric output (no DNS resolution — much faster)
  • -p — show the process name and PID

Reading the Output

$ ss -tlnp
State   Recv-Q  Send-Q  Local Address:Port    Peer Address:Port  Process
LISTEN  0       128     0.0.0.0:22            0.0.0.0:*          users:(("sshd",pid=1234))
LISTEN  0       128     0.0.0.0:80            0.0.0.0:*          users:(("nginx",pid=5678))
LISTEN  0       511     127.0.0.1:5432        0.0.0.0:*          users:(("postgres",pid=9012))
LISTEN  0       128     0.0.0.0:3000          0.0.0.0:*          users:(("node",pid=3456))

Reading each column:

  • State — LISTEN (waiting for connections), ESTABLISHED (active connection), TIME_WAIT (closing), CLOSE_WAIT (remote end closed)
  • Local Address:Port — what IP and port this machine is bound to. 0.0.0.0 means all interfaces. 127.0.0.1 means localhost only (not externally reachable).
  • Peer Address:Port — the remote end. * means not connected (for LISTEN state).
  • Process — which program owns this socket and its PID.

Common Use Cases

Which process is using port 3000?

ss -tlnp | grep :3000
# or
netstat -tlnp | grep :3000
# or on macOS/Linux:
lsof -i :3000

Is my web server actually listening?

ss -tlnp | grep :80
ss -tlnp | grep :443

How many connections to my database?

ss -tn dst :5432 | wc -l

Show all ESTABLISHED connections (not just listeners)

ss -tnp state established

Connection States You Will See

StateMeaning
LISTENSocket is open and accepting connections
ESTABLISHEDActive connection in progress
TIME_WAITConnection closing — waiting to ensure remote end got the FIN
CLOSE_WAITRemote end closed; local end hasn't closed yet
SYN_SENTTCP SYN sent, waiting for SYN-ACK
FIN_WAIT1/2Local end initiated close

Finding a Process by Port — Platform Quick Reference

# Linux
ss -tlnp | grep :3000
lsof -i :3000

# macOS
lsof -i :3000

# Windows
netstat -ano | findstr :3000
# then look up the PID:
tasklist | findstr <PID>

# Windows PowerShell
Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess

Tool 5 — telnet / nc: Is That Port Even Open?

What They Are

telnet and nc (netcat) let you open a raw TCP connection to a specific host and port. If the connection succeeds, the port is open and accepting connections. If you get "Connection refused," the port is closed. If it hangs, a firewall is blocking.

telnet for Port Testing

telnet example.com 80

Possible outcomes:

# Port is open:
Connected to example.com.
Escape character is '^]'.
(blank line — server is waiting for HTTP request)

# Port is closed:
telnet: connect to address 93.184.216.34: Connection refused

# Firewall blocking (hangs until timeout):
Trying 93.184.216.34...
(no output — waits 30+ seconds then times out)

Once connected, you can manually type raw protocol commands. For HTTP:

telnet example.com 80
GET / HTTP/1.0
Host: example.com
(press Enter twice)

The server will respond with a raw HTTP response. This is how developers tested HTTP before modern tools existed — and it still works perfectly for diagnosing what a server actually returns at the protocol level.

Testing SMTP Manually

telnet mail.example.com 25
# Connected — server greets you:
220 mail.example.com ESMTP Postfix

EHLO mycomputer.local
# Server lists supported commands:
250-mail.example.com
250-SIZE 10240000
250-STARTTLS
250 HELP

This confirms the mail server is reachable and responding to SMTP commands. Used to debug email delivery issues.

nc (netcat) — the Modern Alternative

nc is cleaner and more scriptable than telnet:

nc -zv example.com 80       # z = zero I/O (just test), v = verbose
# output: Connection to example.com 80 port [tcp/http] succeeded!

nc -zv example.com 443      # test HTTPS port
nc -zv example.com 5432     # test PostgreSQL port
nc -zv example.com 6379     # test Redis port

Port range scan:

nc -zv example.com 20-25    # test ports 20 through 25 (FTP, SSH, SMTP range)

With timeout (don't wait forever):

nc -zv -w 3 example.com 80  # give up after 3 seconds

Windows Alternative

Windows does not ship with telnet enabled by default and does not have nc. Use PowerShell:

Test-NetConnection -ComputerName example.com -Port 80
# output:
# ComputerName     : example.com
# RemoteAddress    : 93.184.216.34
# RemotePort       : 80
# InterfaceAlias   : Ethernet
# SourceAddress    : 192.168.1.100
# TcpTestSucceeded : True            <- True = port open, False = port closed/blocked

The Debugging Ladder

When something is not working, use this sequence to pinpoint the exact layer of failure:

1. Can you reach the host at all?
   ping example.com
   └─> No response = network/firewall issue at ICMP layer

2. Is the port open?
   telnet example.com 80   (or nc -zv example.com 80)
   └─> Connection refused = nothing listening on that port
   └─> Timeout           = firewall blocking TCP to that port
   └─> Connected         = port is open, problem is at application layer

3. Is DNS resolving correctly?
   nslookup example.com
   dig example.com
   └─> NXDOMAIN = domain does not exist or DNS is broken

4. What is the HTTP response?
   curl -v https://example.com
   └─> Shows exact headers, status code, body, TLS details

5. What is listening locally?
   ss -tlnp | grep :80
   └─> Nothing there = your server is not running or listening on wrong port

Common Mistakes

  • Not using curl -v when debugging API issues. The -v flag reveals the complete TLS handshake, every request header your client actually sent, and every response header the server returned. Auth failures, CORS errors, and content-type mismatches are all visible in the headers — but only if you look at them. Running curl without -v and only looking at the response body means you are missing the most diagnostic half of the conversation.

  • Using ping to test if a port is open. ping sends ICMP echo requests — it tests network-layer reachability, not TCP port availability. A server can be completely pingable and still have port 443 closed (or vice versa — a server can be ping-blocked by a firewall but have its ports fully open). Use telnet example.com 443 or nc -zv example.com 443 to test port connectivity specifically.

  • Running netstat without -n. Without the -n flag, netstat attempts to resolve every IP address to a hostname using reverse DNS. On a server with many connections, this can take minutes and produce garbled output as slow DNS queries come back out of order. Always use -n for numeric output. The same applies to ss. If you genuinely need hostnames, add them back selectively after identifying the IPs you care about.


Interview Questions

1. How would you check which process is using port 3000 on Linux?

Use ss -tlnp | grep :3000 for a listening socket, or ss -tnp | grep :3000 for an established connection. The output includes the process name and PID in the Process column. Alternatively, lsof -i :3000 gives the same result with a different output format. On older systems, netstat -tlnp | grep :3000 works equivalently.

2. What is the difference between dig and nslookup?

Both query DNS, but dig provides significantly more information: full response sections (QUESTION, ANSWER, AUTHORITY, ADDITIONAL), TTLs for every record, query time, the exact server that answered, status codes (NOERROR/NXDOMAIN/SERVFAIL), and the +trace option to follow the full resolution chain from root servers down. nslookup has a simpler output and is cross-platform (ships natively on Windows). Use nslookup for quick checks anywhere; use dig when you need complete DNS visibility, TTL values, or trace-level debugging.

3. How would you test if port 443 is open on a remote server without a browser?

Run telnet example.com 443 or nc -zv example.com 443. If the connection succeeds, the port is open. "Connection refused" means nothing is listening there. A timeout means a firewall is blocking TCP traffic to that port. On Windows without these tools, use Test-NetConnection -ComputerName example.com -Port 443 in PowerShell and check the TcpTestSucceeded field.

4. How do you use curl to debug an API authentication issue?

Run curl -v -H "Authorization: Bearer TOKEN" https://api.example.com/protected. The -v flag shows the full request headers you sent (confirming the Authorization header is present and correctly formatted) and the full response headers from the server. Look at: the HTTP status code (401 = not authenticated, 403 = authenticated but not authorized), the WWW-Authenticate response header (tells you what auth scheme the server expects), and the response body error message. If the token looks correct in the request but still fails, check whether the server is returning a 401 or a redirect (which might strip the auth header).

5. What is the difference between netstat and ss?

ss is the modern replacement for netstat on Linux systems. ss reads directly from the kernel's socket tables via netlink sockets, making it significantly faster — especially on servers with thousands of connections where netstat (which parses /proc/net/tcp) becomes noticeably slow. ss also provides more detail about socket internals (like TCP retransmit counts and congestion window size) and has a more powerful filtering syntax. The flags are largely compatible: ss -tlnp is the equivalent of netstat -tlnp. On macOS and Windows, netstat is still the standard tool since ss is Linux-specific.


Quick Reference — Cheat Sheet

nslookup

PurposeCommand
Basic A record lookupnslookup google.com
MX (mail) recordsnslookup -type=MX google.com
Query specific resolvernslookup google.com 8.8.8.8
TXT records (SPF/DKIM)nslookup -type=TXT google.com
Interactive modenslookup (then type queries)

dig

PurposeCommand
Basic A record lookupdig google.com
Just the answer (scriptable)dig +short google.com
MX recordsdig google.com MX
AAAA (IPv6) recordsdig google.com AAAA
Full resolution tracedig +trace google.com
Query specific resolverdig @8.8.8.8 google.com
TXT recordsdig google.com TXT

curl

PurposeCommand
Basic GET requestcurl https://example.com
See all headers + TLScurl -v https://example.com
HEAD request (headers only)curl -I https://example.com
POST with JSON bodycurl -X POST -H "Content-Type: application/json" -d '{"key":"val"}' https://api.example.com/endpoint
Bearer token authcurl -H "Authorization: Bearer TOKEN" https://api.example.com/protected
Follow redirectscurl -L https://example.com
Get status code onlycurl -s -o /dev/null -w "%{http_code}" https://example.com
Save to filecurl -o file.json https://api.example.com/data
Silent (no progress bar)curl -s https://example.com

netstat / ss

PurposeCommand
All connections, numericnetstat -an / ss -an
TCP listeners with processnetstat -tlnp / ss -tlnp
Who is on port 3000?ss -tlnp | grep :3000
All established connectionsss -tnp state established
TCP + UDP listenersss -tunlp
Process on port (Mac/Linux)lsof -i :3000

telnet / nc

PurposeCommand
Test if port is opentelnet example.com 80
Port test (clean output)nc -zv example.com 80
Port test with timeoutnc -zv -w 3 example.com 80
Test port rangenc -zv example.com 20-25
Windows port testTest-NetConnection -ComputerName example.com -Port 80
Manual HTTP over telnettelnet example.com 80 then type GET / HTTP/1.0

The Layer-by-Layer Debugging Stack

Symptom: "It's not working"

    ├─ ping example.com          → Network reachable?
    ├─ nslookup / dig            → DNS resolving correctly?
    ├─ telnet / nc               → Port open? Firewall blocking?
    ├─ curl -v                   → HTTP response correct? Headers OK?
    └─ ss -tlnp                  → Is the server actually listening locally?

Previous: Lesson 7.2 — traceroute → Next: Lesson 8.1 — The Complete Web Request Journey →


This is Lesson 7.3 of the Networking Interview Prep Course — 8 chapters, 32 lessons.

On this page