@portable/dns-packet
Functions
DnsPacket.decode
Decodes a DNS message from its wire format. Compression pointers are followed, and each must
point backwards, so a pointer loop can't hang the decoder. Bytes after the message are ignored.
Throws TruncatedError when the input ends early (with framing: 'tcp', also when the whole
message hasn't arrived yet) and InvalidInputError for malformed input.
function decode(
bytes: Uint8Array,
options: decode.Options = {},
): DecodedPacket;| Option | Type | Default | Description |
|---|---|---|---|
framing | "none" | "tcp" | 'none' | 'tcp' reads the 2-byte length prefix used over TCP and TLS first. |
import * as DnsPacket from '@portable/dns-packet';
const response = await fetch('https://cloudflare-dns.com/dns-query', {
method: 'POST',
headers: { 'content-type': 'application/dns-message' },
body: query,
});
const packet = DnsPacket.decode(
new Uint8Array(await response.arrayBuffer()),
);
for (const answer of packet.answers) {
if (answer.type === 'A') {
console.log(answer.data);
}
}DnsPacket.encode
Encodes a DNS message into its wire format, as sent over UDP or as a DNS-over-HTTPS body.
Throws InvalidInputError for a name, address or record that can't be encoded, and
UnsupportedError for an EDNS option with no data and no built-in encoder.
function encode(
packet: Packet,
options: encode.Options = {},
): Uint8Array<ArrayBuffer>;| Option | Type | Default | Description |
|---|---|---|---|
compress | boolean | false | Replace repeated names with pointers to their first copy (RFC 1035 section 4.1.4). Applies to owner names and to names inside NS, CNAME, PTR, MX and SOA records. |
framing | "none" | "tcp" | 'none' | 'tcp' adds the 2-byte length prefix used over TCP and TLS (RFC 1035 section 4.2.2). |
import * as DnsPacket from '@portable/dns-packet';
const query = DnsPacket.encode({
id: 1,
flags: { rd: true },
questions: [{ name: 'example.com', type: 'A' }],
});Errors
DnsPacket.InvalidInputError
Thrown for malformed input. When decoding: an unknown label type, a name over 255 bytes, a compression pointer that doesn't point backwards (so loops are rejected), or record data that doesn't match its type. When encoding: an empty label or one over 63 bytes, an invalid IP address, a TXT string over 255 bytes, an OPT record whose name isn't the root, and so on.
class InvalidInputError extends Errors.InvalidInputError {
readonly kind: string;
}import * as DnsPacket from '@portable/dns-packet';
try {
DnsPacket.encode({
answers: [{ type: 'A', name: 'example.com', data: '300.0.0.1' }],
});
} catch (error) {
if (error instanceof DnsPacket.InvalidInputError) {
console.log(error.message);
}
}DnsPacket.TruncatedError
Thrown by decode when the input ends before the message does, or a length field points past
the end. With framing: 'tcp', this also means the whole message hasn't arrived yet.
class TruncatedError extends Errors.TruncatedError {
readonly kind: string;
}import * as DnsPacket from '@portable/dns-packet';
try {
DnsPacket.decode(new Uint8Array(4));
} catch (error) {
if (error instanceof DnsPacket.TruncatedError) {
console.log('incomplete message');
}
}DnsPacket.UnsupportedError
Thrown by encode for an EDNS option that has no data and no built-in encoder. Only
CLIENT_SUBNET, TCP_KEEPALIVE, PADDING and KEY_TAG are built in.
class UnsupportedError extends Errors.UnsupportedError {
readonly kind: string;
}import * as DnsPacket from '@portable/dns-packet';
try {
//
## Types
### `DnsPacket.Opcode`
A header opcode name. An opcode without a name is written `OPCODE<number>`.
```ts
type Opcode = | | | | | | number;DnsPacket.OptionCode
An EDNS option code name. A code without a name is written OPTION<number>.
type OptionCode = | | | | | | | | | | | | | | | number;DnsPacket.Rcode
A header response code name. A code without a name is written RCODE<number>.
type Rcode = | | | | | | | | | | | | number;DnsPacket.RecordClass
A class name. A class without a name is written CLASS<number>, as in RFC 3597.
type RecordClass = | | | | | | number;DnsPacket.RecordType
A record type name. A type without a name is written TYPE<number>, as in RFC 3597.
type RecordType = keyof typeof typeNumbers | number;DnsPacket.DecodedOptRecord
An OPT record as decode returns it.
type DecodedOptRecord = \{
type: ;
name: ;
udpPayloadSize: number;
extendedRcode: number;
version: number;
dnssecOk: boolean;
options: EdnsOption[];
\};DnsPacket.DecodedPacket
A DNS message as decode returns it, with every field present.
type DecodedPacket = \{
id: number;
type: | ;
opcode: Opcode;
rcode: Rcode;
flags: \{
[key in keyof Flags]-?: boolean;
\};
questions: DecodedQuestion[];
answers: DecodedResourceRecord[];
authorities: DecodedResourceRecord[];
additionals: DecodedResourceRecord[];
\};DnsPacket.DecodedQuestion
A question as decode returns it.
type DecodedQuestion = \{
name: string;
type: RecordType;
class: RecordClass;
qu: boolean;
\};DnsPacket.DecodedResourceRecord
A resource record as decode returns it.
type DecodedResourceRecord =
| \{
[key in KnownType]: DecodedFields & \{
type: key;
data: DecodedRecordData[key];
\};
\}[KnownType]
| (DecodedFields & \{
type: UnknownType;
data: Uint8Array;
\})
| DecodedOptRecord;DnsPacket.Flags
Header flags. Each is one bit of the header, and a missing flag is clear.
type Flags = \{
aa?: boolean | undefined;
tc?: boolean | undefined;
rd?: boolean | undefined;
ra?: boolean | undefined;
ad?: boolean | undefined;
cd?: boolean | undefined;
\};DnsPacket.OptRecord
The EDNS OPT pseudo-record (RFC 6891). It goes in additionals.
type OptRecord = \{
type: ;
name?: | undefined;
udpPayloadSize?: number | undefined;
extendedRcode?: number | undefined;
version?: number | undefined;
dnssecOk?: boolean | undefined;
options?: readonly EdnsOption[] | undefined;
\};DnsPacket.Packet
A DNS message to encode. Missing fields take their defaults and missing sections are empty.
type Packet = \{
id?: number | undefined;
type?: | | undefined;
opcode?: Opcode | undefined;
rcode?: Rcode | undefined;
flags?: Flags | undefined;
questions?: readonly Question[] | undefined;
answers?: readonly ResourceRecord[] | undefined;
authorities?: readonly ResourceRecord[] | undefined;
additionals?: readonly ResourceRecord[] | undefined;
\};DnsPacket.Question
A question: the name, type and class being looked up.
type Question = \{
name: string;
type: RecordType;
class?: RecordClass | undefined;
qu?: boolean | undefined;
\};DnsPacket.ResourceRecord
A resource record in the answer, authority or additional section.
type ResourceRecord =
| \{
[key in KnownType]: Fields & \{
type: key;
data: RecordData[key];
\};
\}[KnownType]
| (Fields & \{
type: UnknownType;
data: Uint8Array;
\})
| OptRecord;DnsPacket.UnknownType
A record type with no codec here. Its data is the raw RDATA.
type UnknownType = Exclude<RecordType, KnownType | >;DnsPacket.DecodedRecordData
The data of each record type as decode returns it.
type DecodedRecordData = Omit<RecordData> & \{
TXT: Uint8Array[];
\};DnsPacket.EdnsOption
An EDNS option inside an OPT record. CLIENT_SUBNET, TCP_KEEPALIVE, PADDING and KEY_TAG have
fields. For any other code, data holds the raw bytes.
type EdnsOption = \{
code: ;
ip: string;
sourcePrefixLength: number;
scopePrefixLength?: number | undefined;
\} | \{
code: ;
timeout?: number | undefined;
\} | \{
code: ;
length: number;
\} | \{
code: ;
tags: readonly number[];
\} | \{
code: Exclude<OptionCode, | | | >;
data: Uint8Array;
\};DnsPacket.RecordData
The data of each record type that has a codec. decode returns the same shapes, except that
TXT strings are always Uint8Array.
type RecordData = \{
A: string;
AAAA: string;
CNAME: string;
DNAME: string;
NS: string;
PTR: string;
MX: \{
preference: number;
exchange: string;
\};
SOA: \{
mname: string;
rname: string;
serial: number;
refresh: number;
retry: number;
expire: number;
minimum: number;
\};
SRV: \{
priority: number;
weight: number;
port: number;
target: string;
\};
TXT: readonly (string | Uint8Array)[];
NULL: Uint8Array;
HINFO: \{
cpu: string;
os: string;
\};
CAA: \{
flags: number;
tag: string;
value: string;
\};
NAPTR: \{
order: number;
preference: number;
flags: string;
services: string;
regexp: string;
replacement: string;
\};
TLSA: \{
usage: number;
selector: number;
matchingType: number;
certificate: Uint8Array;
\};
SSHFP: \{
algorithm: number;
fingerprintType: number;
fingerprint: Uint8Array;
\};
DS: DigestData;
CDS: DigestData;
DNSKEY: KeyData;
CDNSKEY: KeyData;
RRSIG: \{
typeCovered: RecordType;
algorithm: number;
labels: number;
originalTTL: number;
expiration: number;
inception: number;
keyTag: number;
signersName: string;
signature: Uint8Array;
\};
RP: \{
mbox: string;
txt: string;
\};
NSEC: \{
nextDomain: string;
types: readonly RecordType[];
\};
NSEC3: \{
algorithm: number;
flags: number;
iterations: number;
salt: Uint8Array;
nextHashedOwner: Uint8Array;
types: readonly RecordType[];
\};
\};