dnslookup
Raw DNS query CLI and Node.js library that builds and sends UDP packets directly per RFC 1035. No dependencies beyond Node.js. Useful for debugging DNS servers (e.g., Unifi, Pi-hole) by seeing exactly what's happening at the protocol level.
By design this is a thin wrapper over the wire protocol: no caching, no retries, no fallback chains. v4 transport only (the DNS server endpoint must be IPv4), but AAAA records are fully supported.
Install
npm install -g @bobfrankston/dnslookup
Usage
dnslookup <hostname> [hostname2 ...] [options]
Options
-t <type> | Record type: A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, ANY (default: A) |
-s <server> | DNS server IP (default: system DNS) |
-x | Hex dump of query and response packets |
-v | Verbose: show authority and additional sections |
-detail | Force detailed output (default for single name) |
-table | Force table output (default for multiple names) |
--timeout <ms> | Query timeout in ms (default: 5000) |
Single name (detail mode)
$ dnslookup rmf39.aaz.lt
── DNS Query ──
Server: 172.20.0.1:53
Name: rmf39.aaz.lt
Type: A (1)
RTT: 9.67ms
Response: 46 bytes
── Response Header ──
ID: 0x37bf
Flags: 0x8180 [QR RD RA]
Status: NoError
Counts: Questions=1 Answers=1 Authority=0 Additional=0
── Answers (1) ──
rmf39.aaz.lt 43m28s A 108.26.220.160
── Result ──
A 108.26.220.160
Multiple names (table mode)
$ dnslookup rmf39 rmf39.aaz.lt example.com google.com
Name Server Type RTT TTL Status
rmf39 172.20.0.1 A 10.1ms - NXDOMAIN
rmf39.aaz.lt 172.20.0.1 A 1.6ms 43m36s Found 108.26.220.160
example.com 172.20.0.1 A 25.6ms 5m0s Found 104.18.26.120
104.18.27.120
google.com 172.20.0.1 A 20.1ms 3m10s Found 142.250.217.14
Compare DNS servers
dnslookup myhost -s 172.20.0.1
dnslookup myhost -s 8.8.8.8
dnslookup myhost -s 10.255.255.254
Hex dump for packet inspection
dnslookup example.com -x
API
Install as a regular dependency and import:
npm install @bobfrankston/dnslookup
import { lookup, lookupx, runQuery, QType } from "@bobfrankston/dnslookup";
lookup(hostname, options?) → Promise<string[]>
Simplest case. Returns an array of address strings (DNS may legitimately return multiple). Throws on transport failure or DNS error (NXDOMAIN, ServerFailure, …).
await lookup("google.com");
await lookup("google.com", { type: "AAAA" });
await lookup("example.com", { server: "8.8.8.8", timeout: 2000 });
lookupx(hostname, options?) → Promise<LookupExtended>
Extended form: returns the same address list plus the diagnostic detail you'd see in the CLI's detail view (server, RTT, response size, header ID/flags/status, section counts, full record arrays). Throws only on transport failure; DNS errors come back as status (e.g. "NXDOMAIN") with addresses: [].
const r = await lookupx("google.com");
runQuery(hostname, server, qtype, timeout) → Promise<QueryResult>
Lowest-level entry point. Returns the full parsed response (header, answers, authority, additional, RTT, raw query/response packets). Does not throw on DNS errors — failures are reported via the status and error fields. Use QType.A / QType.AAAA / etc. for qtype.
Options (LookupOptions)
server | string | first system resolver (dns.getServers()[0]) | Must be an IPv4 address |
type | "A" | "AAAA" | "NS" | "CNAME" | "SOA" | "PTR" | "MX" | "TXT" | "ANY" | "A" | Same set as the -t CLI flag |
timeout | number (ms) | 5000 | |
Exports
lookup, lookupx, runQuery, QType, QTypeName, and the types LookupOptions, LookupExtended, QueryResult, DnsRecord, DnsHeader.
How it works
- Constructs raw DNS query packets per RFC 1035
- Sends via UDP to port 53 using
node:dgram
- Parses response including DNS name compression (pointer offsets)
- Supports record types: A, AAAA, MX, CNAME, NS, SOA, TXT, PTR
- Zero runtime dependencies
License
MIT