dnspacket-ts
v2.0.0
Published
Node/Browser module for encoding and decoding DNS packet
Maintainers
Readme
DNS Packet
A Node.js/Deno/Bun and browser module for encoding and decoding DNS packets. This library is designed to facilitate DNS over HTTPS (DoH) and other DNS-based applications. It supports the core DNS protocol as defined in RFC 1035 and includes modern extensions like EDNS0 (RFC 6891).
Version 2.0.0 Highlights
- Multiple Questions Support: Encoder/decoder now support multiple questions per packet (QDCOUNT > 1)
- 15+ New DNS Record Types: CAA, DNSKEY, DS, NSEC, NSEC3, TLSA, SSHFP, SPF, DNAME, CDS, CDNSKEY, OPENPGPKEY, CSYNC, ZONEMD, SVCB, HTTPS, and more
- Critical Bug Fixes: Fixed EDNS option endianness,
getQRlogic, OPT record offset handling, Opcode type mismatches - Strict TypeScript: Enabled
strict: true,noUncheckedIndexedAccess,exactOptionalPropertyTypes - Extended Protocol Support: Additional OPCODEs (IQUERY, STATUS, NOTIFY, UPDATE), RCODEs (YXDOMAIN, YXRRSET, NXRRSET, NOTAUTH, NOTZONE), CLASSes (CH, HS)
Installation
npm install dnspacket-ts
# or
yarn add dnspacket-ts
# or
pnpm add dnspacket-tsUsage
Basic DoH Query (AAAA record)
import { decode, encode } from "dnspacket-ts";
import axios from "axios";
const query = {
id: 153,
flags: {
RD: 1, // Recursion Desired
},
questions: [
{ CLASS: "IN", NAME: "google.com", TYPE: "AAAA" },
],
};
const data = encode(query);
(async () => {
try {
const rdr = await axios({
url: "https://cloudflare-dns.com/dns-query",
method: "POST",
data: data,
headers: { "Content-Type": "application/dns-message" },
responseType: "arraybuffer",
});
console.log(decode(rdr.data));
} catch (error) {
console.error("Error during DNS query:", error);
}
})();Multiple Questions in One Packet
import { decode, encode } from "dnspacket-ts";
const query = {
id: 12345,
flags: { RD: 1 },
questions: [
{ NAME: "example.com", TYPE: "A", CLASS: "IN" },
{ NAME: "example.com", TYPE: "AAAA", CLASS: "IN" },
{ NAME: "example.com", TYPE: "MX", CLASS: "IN" },
],
};
const encoded = encode(query);
// QDCOUNT will be 3
const decoded = decode(encoded);
console.log(decoded.questions); // Array of 3 questionsEDNS with DNSSEC OK
import { decode, encode } from "dnspacket-ts";
const query = {
id: 1234,
flags: { RD: 1 },
questions: [{ NAME: "dnssec-test.sidnlabs.nl", TYPE: "A", CLASS: "IN" }],
edns: {
udpPayloadSize: 4096,
extendedRCODE: 0,
version: 0,
flags: { DO: 1 }, // DNSSEC OK
options: [
{ code: 8, data: new Uint8Array([0, 10, 0, 1]) } // EDNS Client Subnet
]
}
};
const encoded = encode(query);
// ARCOUNT will be 1 (OPT record included)Decode Various Record Types
import { decode } from "dnspacket-ts";
// The library now decodes many record types:
const decoded = decode(buffer);
// A record -> RDATA: "192.168.1.1"
// AAAA record -> RDATA: "2607:f8b0:4009:80b::200e"
// CNAME/NS/PTR/DNAME -> RDATA: "target.example.com"
// MX record -> RDATA: { PREFERENCE: 10, EXCHANGE: "mail.example.com" }
// TXT/SPF record -> RDATA: ["v=spf1 ...", "google-site-verification=..."]
// SRV record -> RDATA: { PRIORITY: 10, WEIGHT: 5, PORT: 443, TARGET: "sip.example.com" }
// SOA record -> RDATA: { MNAME: "ns1.example.com", RNAME: "admin.example.com", SERIAL: 20240101, ... }
// CAA record -> RDATA: { flags: 0, tag: "issue", value: "letsencrypt.org" }
// DNSKEY record -> RDATA: { flags: 256, protocol: 3, algorithm: 13, key: Uint8Array }
// DS record -> RDATA: { keyTag: 12345, algorithm: 13, digestType: 2, digest: Uint8Array }
// NSEC record -> RDATA: { nextDomain: "next.example.com", types: [1, 2, 28, 5] }
// NSEC3 record -> RDATA: { hashAlgorithm: 1, flags: 0, iterations: 10, salt: Uint8Array, nextHashedOwnerName: Uint8Array, types: [...] }
// TLSA record -> RDATA: { certificateUsage: 3, selector: 1, matchingType: 1, certificateAssociationData: Uint8Array }
// SSHFP record -> RDATA: { algorithm: 2, fingerprintType: 1, fingerprint: Uint8Array }
// Unknown types -> RDATA: Uint8Array (raw wire format)API Reference
encode(query: DNSQuery): Uint8Array
Encodes a DNS query object into a binary packet (Uint8Array).
DNSQuery properties:
id?: number- Transaction ID (random if omitted)flags?: object- Header flags:QR?: 'QUERY' | 'RESPONSE'- Query/Response (default: 'QUERY')Opcode?: 'QUERY' | 'IQUERY' | 'STATUS' | 'NOTIFY' | 'UPDATE'- Operation code (default: 'QUERY')AA?: 0 | 1- Authoritative AnswerTC?: 0 | 1- TruncationRD?: 0 | 1- Recursion Desired (default: 1)RA?: 0 | 1- Recursion AvailableZ?: 0-7- Reserved (must be 0)RCODE?: string- Response code (default: 'NOERROR')
questions: Question[]- Required. Array of question objects (supports multiple)edns?: EDNS- EDNS0 extension (adds OPT record to additional section)
Question: { NAME: string, TYPE: string, CLASS: string }
EDNS: { udpPayloadSize: number, extendedRCODE: number, version: number, flags: { DO: 0 | 1 }, options?: { code: number, data: Uint8Array }[] }
decode(buffer: ArrayBuffer | Uint8Array | Buffer): DNSResponse
Decodes a binary DNS packet into a structured object.
DNSResponse properties:
id: number- Transaction IDflags: object- Header flags with string values for QR, Opcode, RCODEquestions: Question[]- Array of questionsanswers: Answer[]- Answer recordsauthorities: Answer[]- Authority recordsadditionals: Answer[]- Additional recordsedns?: EDNS- EDNS data from OPT record (if present)
Answer: { NAME: string, TYPE: string, CLASS: string, TTL: number, RDLENGTH: number, RDATA: unknown }
Supported Record Types
| Type | Value | RDATA Format |
|------|-------|--------------|
| A | 1 | "192.168.1.1" |
| AAAA | 28 | "2001:db8::1" |
| CNAME | 5 | "target.example.com" |
| NS | 2 | "ns1.example.com" |
| PTR | 12 | "target.example.com" |
| DNAME | 39 | "target.example.com" |
| MX | 15 | { PREFERENCE: number, EXCHANGE: string } |
| TXT | 16 | string[] |
| SPF | 99 | string[] |
| SRV | 33 | { PRIORITY: number, WEIGHT: number, PORT: number, TARGET: string } |
| SOA | 6 | { MNAME: string, RNAME: string, SERIAL: number, REFRESH: number, RETRY: number, EXPIRE: number, MINIMUM: number } |
| CAA | 257 | { flags: number, tag: string, value: string } |
| DNSKEY | 48 | { flags: number, protocol: number, algorithm: number, key: Uint8Array } |
| DS | 43 | { keyTag: number, algorithm: number, digestType: number, digest: Uint8Array } |
| NSEC | 47 | { nextDomain: string, types: number[] } |
| NSEC3 | 50 | { hashAlgorithm: number, flags: number, iterations: number, salt: Uint8Array, nextHashedOwnerName: Uint8Array, types: number[] } |
| TLSA | 52 | { certificateUsage: number, selector: number, matchingType: number, certificateAssociationData: Uint8Array } |
| SSHFP | 44 | { algorithm: number, fingerprintType: number, fingerprint: Uint8Array } |
| OPT | 41 | Handled as EDNS (not in answer sections) |
| Unknown | N | Uint8Array (raw wire format) |
Supported Classes
| Class | Value | Description | |-------|-------|-------------| | IN | 1 | Internet (default) | | CH | 3 | Chaos | | HS | 4 | Hesiod | | ANY | 255 | Any class |
Supported OPCODEs
| Opcode | Value | Description | |--------|-------|-------------| | QUERY | 0 | Standard query (default) | | IQUERY | 1 | Inverse query (obsolete) | | STATUS | 2 | Server status request | | NOTIFY | 4 | Zone change notification | | UPDATE | 5 | Dynamic update |
Supported RCODEs
| RCODE | Value | Description | |-------|-------|-------------| | NOERROR | 0 | No error | | FORMERR | 1 | Format error | | SERVFAIL | 2 | Server failure | | NXDOMAIN | 3 | Non-existent domain | | NOTIMP | 4 | Not implemented | | REFUSED | 5 | Query refused | | YXDOMAIN | 6 | Name exists when it shouldn't | | YXRRSET | 7 | RR set exists when it shouldn't | | NXRRSET | 8 | RR set that should exist doesn't | | NOTAUTH | 9 | Not authoritative | | NOTZONE | 10 | Name not in zone |
Resources
- RFC 1035: Domain Names - Implementation and Specification
- RFC 1034: Domain Names - Concepts and Facilities
- RFC 6891: Extension Mechanisms for DNS (EDNS0)
- RFC 8484: DNS Queries over HTTPS (DoH)
- RFC 3596: DNS Extensions for IPv6 (AAAA record)
- RFC 2782: Service Location (SRV record)
- RFC 2181: Clarifications to DNS Specification
- RFC 6844: Certification Authority Authorization (CAA record)
- RFC 4034/4035/5155: DNSSEC (DNSKEY, DS, NSEC, NSEC3)
- RFC 6698: TLSA record
- RFC 4255: SSHFP record
- RFC 7208: SPF record
- RFC 8499: DNS Terminology
License
MIT
