silk.net.dns.message.
Read and write DNS messages in the wire format of RFC 1035.
When to use#
Use this module to make a query, to read the response of a DNS server, or to write a response,
for example in a test server. Message.query makes a query, Message.encode writes any
message, and Message.parse reads one.
Details#
A Name holds a domain name in wire form, without compression, in a fixed buffer. It does not
allocate. RecordData has a variant for each record type that node:dns reads: A, AAAA, NS,
CNAME, PTR, MX, TXT, SRV, SOA, NAPTR, CAA and TLSA. Other types keep their raw data in
RecordData.Unknown. An EDNS(0) OPT record (RFC 6891) is read into Message field edns, not
into the additional records.
Message.parse follows compression pointers, but only to earlier bytes, so it cannot loop.
Message.encode compresses the names of the questions and of the owner names, and the names in
NS, CNAME, PTR, MX and SOA data. It does not compress the names of other types, as RFC 3597
requires.
Errors#
MessageErrorwith aMessageReasonand the byte offset where the problem is.OutOfMemoryErrorif theAllocatorcannot supply the lists or the bytes.
Gotchas#
Names compare without case for ASCII letters only, as RFC 4343 requires. A name in text form
uses the escapes of RFC 1035 section 5.1: \. for a dot in a label, \\ for a backslash, and
\DDD for any byte in decimal.
Examples#
Write a query and read it back#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.collections.bytes { Bytes }
import silk.core.effect { Effect }
import silk.core.option { Option }
import silk.core.result { Result }
import silk.net.dns.message { CLASS_IN, Message, MessageError, Name, Question, TYPE_AAAA }
import silk.collections.list { List }
effect fn program() -> i32
! MessageError | OutOfMemoryError
? &mut Allocator {
let name = match Name.parse("example.com") {
Result<Name, MessageError>.Success { value } => value
Result<Name, MessageError>.Failure {..} => {
return 1
}
}
let question = Question { name: name, kind: TYPE_AAAA, class: CLASS_IN }
let query = run Message.query(4660, question, Option<u16>.some(1232))
let mut wire = Bytes.make()
run Message.encode(&query, &mut wire)
let parsed = run Message.parse(Bytes.asSlice(&wire))
if parsed.header.id != 4660 || !parsed.header.recursionDesired {
return 1
}
let questions = List<Question>.asSlice(&parsed.questions)
if questions.length != 1 || questions[0].kind != TYPE_AAAA {
return 1
}
if !Name.equals(&questions[0].name, &name) {
return 1
}
return 0
}
effect fn failed<E>(error: E) -> i32 {
drop error
return 1
}
pub fn main() -> i32 {
let mut allocator = Allocator.systemAllocatorProvider()
return run Effect.catchAll(program(), failed) |> Effect.provideMut<Allocator>(&mut allocator)
}Import as Message with import silk.net.dns.message { Message }.
Public declarations: 39.
TYPE_A#
pub const TYPE_A: u16An IPv4 address, code 1.
TYPE_NS#
pub const TYPE_NS: u16An authoritative name server, code 2.
TYPE_CNAME#
pub const TYPE_CNAME: u16The canonical name of an alias, code 5.
TYPE_SOA#
pub const TYPE_SOA: u16The start of a zone of authority, code 6.
TYPE_PTR#
pub const TYPE_PTR: u16A domain name pointer, such as a reverse lookup result, code 12.
TYPE_MX#
pub const TYPE_MX: u16A mail exchange, code 15.
TYPE_TXT#
pub const TYPE_TXT: u16Text strings, code 16.
TYPE_AAAA#
pub const TYPE_AAAA: u16An IPv6 address, code 28.
TYPE_SRV#
pub const TYPE_SRV: u16A service location (RFC 2782), code 33.
TYPE_NAPTR#
pub const TYPE_NAPTR: u16A naming authority pointer (RFC 3403), code 35.
TYPE_OPT#
pub const TYPE_OPT: u16The EDNS(0) pseudo-record (RFC 6891), code 41.
TYPE_TLSA#
pub const TYPE_TLSA: u16A TLS certificate association (RFC 6698), code 52.
TYPE_ANY#
pub const TYPE_ANY: u16A question for all records of a name, code 255.
TYPE_CAA#
pub const TYPE_CAA: u16A certification authority authorization (RFC 8659), code 257.
CLASS_IN#
pub const CLASS_IN: u16The Internet, code 1.
CLASS_CH#
pub const CLASS_CH: u16Chaos, code 3.
CLASS_HS#
pub const CLASS_HS: u16Hesiod, code 4.
CLASS_ANY#
pub const CLASS_ANY: u16A question for any class, code 255.
RCODE_NO_ERROR#
pub const RCODE_NO_ERROR: u16No error, code 0.
RCODE_FORMAT_ERROR#
pub const RCODE_FORMAT_ERROR: u16The server could not read the query, code 1.
RCODE_SERVER_FAILURE#
pub const RCODE_SERVER_FAILURE: u16The server failed to process the query, code 2.
RCODE_NAME_ERROR#
pub const RCODE_NAME_ERROR: u16The name does not exist, code 3. Also known as NXDOMAIN.
RCODE_NOT_IMPLEMENTED#
pub const RCODE_NOT_IMPLEMENTED: u16The server does not support this kind of query, code 4.
RCODE_REFUSED#
pub const RCODE_REFUSED: u16The server refuses to answer, code 5.
RCODE_BAD_VERSION#
pub const RCODE_BAD_VERSION: u16The server does not support the EDNS version of the query, code 16.
OPCODE_QUERY#
pub const OPCODE_QUERY: u8A standard query, code 0.
OPCODE_STATUS#
pub const OPCODE_STATUS: u8A server status request, code 2.
OPCODE_NOTIFY#
pub const OPCODE_NOTIFY: u8A zone change notification (RFC 1996), code 4.
OPCODE_UPDATE#
pub const OPCODE_UPDATE: u8A dynamic update (RFC 2136), code 5.
MessageReason#
pub enum MessageReasonThe problem that MessageError reports.
Truncated#
TruncatedThe message ends inside a field.
ReservedLabel#
ReservedLabelA label length byte uses the reserved forms 01 or 10 in its top two bits.
ForwardPointer#
ForwardPointerA compression pointer does not point to an earlier byte.
LabelTooLong#
LabelTooLongA label in text form is longer than 63 bytes.
NameTooLong#
NameTooLongA name is longer than 255 bytes in wire form.
EmptyLabel#
EmptyLabelA name in text form has an empty label, as in a..b.
InvalidEscape#
InvalidEscapeA name in text form has a \ that is not followed by a byte, or by a decimal escape from
\000 to \255.
BadRecordLength#
BadRecordLengthThe data of a record has the wrong length for its type.
BadOpt#
BadOptThe message has more than one OPT record, or an OPT record that is not in the additional section or not owned by the root name.
TooLarge#
TooLargeA count does not fit its 16-bit field, or a message is longer than 65535 bytes.
StringTooLong#
StringTooLongA character string is longer than 255 bytes.
BadResponseCode#
BadResponseCodeA response code does not fit 12 bits, or one above 15 is written without EDNS.
Implementation Debug for MessageReason#
impl Debug for MessageReasonOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as Truncated.
MessageError#
pub struct MessageErrorA DNS message or a name that cannot be read or written.
Field reason#
pub reason: MessageReasonThe problem.
Field offset#
pub offset: usizeThe byte offset of the problem: in the message for wire data, or in the text for a name in text form. When writing, the offset is the length of the output that was written before the problem.
Implementation Copy for MessageError#
impl Copy for MessageErrorImplementation Debug for MessageError#
impl Debug for MessageErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the fields, as MessageError { reason: ..., offset: ... }.
Name#
pub struct NameA domain name in wire form: a list of labels, each with its length byte, and a zero byte at the end.
Details#
A name holds at most 255 bytes in wire form, in a fixed buffer, so it does not allocate. A label can hold any byte, not only letters, digits and hyphens. Names compare without case for the ASCII letters only.
Implementation Copy for Name#
impl Copy for NameImplementation Name#
impl NameAssociated function root#
pub fn root() -> NameReturns the root name, which has no labels. Its text form is ..
Associated function parse#
pub fn parse(text: string) -> Result<Name, MessageError>Parses a name in text form, such as example.com or example.com..
Details#
A final dot is optional, and the name is always absolute. . and the empty text give the root
name. Use \. for a dot in a label, \\ for a backslash, and \DDD for any byte, with three
decimal digits.
Errors#
EmptyLabelfor two dots in a row, or a dot at the start of a name that is not the root.LabelTooLongfor a label of more than 63 bytes.NameTooLongif the name has more than 255 bytes in wire form.InvalidEscapefor a\at the end, or a decimal escape above 255.
Associated function parseBytes#
pub fn parseBytes(text: &[u8]) -> Result<Name, MessageError>Parses a name in text form from bytes.
When to use#
Use it for a name that you have as bytes, for example a line of a configuration file. For a
string, use Name.parse.
Details#
A final dot is optional, and the name is always absolute. . and empty input give the root
name. Use \. for a dot in a label, \\ for a backslash, and \DDD for any byte, with three
decimal digits.
Errors#
EmptyLabelfor two dots in a row, or a dot at the start of a name that is not the root.LabelTooLongfor a label of more than 63 bytes.NameTooLongif the name has more than 255 bytes in wire form.InvalidEscapefor a\at the end, or a decimal escape above 255.
Associated function fromDomain#
pub fn fromDomain(host: &DomainHost) -> NameReturns the name of a checked domain host, such as one from silk.net.address.Host.parse.
Associated function reverseIpv4#
pub fn reverseIpv4(address: &Ipv4Address) -> NameReturns the name for the reverse lookup of an IPv4 address, such as
4.3.2.1.in-addr.arpa for 1.2.3.4.
Associated function reverseIpv6#
pub fn reverseIpv6(address: &Ipv6Address) -> NameReturns the name for the reverse lookup of an IPv6 address: its 32 hex digits from the last one
to the first, then ip6.arpa, as RFC 3596 defines.
Method wire#
pub fn wire<'a>(self: &'a Self) -> &'a [u8]Returns the name in wire form, with the zero byte at the end and without compression.
Method labelCount#
pub fn labelCount(self: &Self) -> usizeReturns the number of labels. The root name has 0.
Method isRoot#
pub fn isRoot(self: &Self) -> boolReturns true if this is the root name.
Method equals#
pub fn equals(self: &Self, other: &Self) -> boolReturns true if the names are equal. ASCII letters compare without case, so Example.COM
equals example.com.
Method join#
pub fn join(self: &Self, suffix: &Name) -> Result<Name, MessageError>Returns this name followed by the labels of suffix, such as www.example.com for www and
example.com.
When to use#
Use it to add a search domain to a relative name.
Errors#
NameTooLongif the result has more than 255 bytes in wire form. The offset is 0.
Method appendText#
pub effect fn appendText(self: &Self, output: &mut Bytes) -> ()
! OutOfMemoryError
? &mut AllocatorAppends the name in text form to output, without a final dot. The root name is ..
Details#
A dot or a backslash in a label is written as \. or \\. A byte that is not a printable ASCII
character, or a space, is written as \DDD in decimal. Name.parse reads the text back to the
same name.
Question#
pub struct QuestionOne question of a message: a name, a record type and a class.
Field name#
pub name: NameThe name to look up.
Field kind#
pub kind: u16The record type, such as TYPE_A.
Field class#
pub class: u16The class, usually CLASS_IN.
Implementation Copy for Question#
impl Copy for QuestionImplementation Question#
impl QuestionMethod equals#
pub fn equals(self: &Self, other: &Self) -> boolReturns true if the questions have equal names, types and classes. Names compare without case.
RecordData#
pub union RecordDataThe data of a resource record, by type.
A#
AAn IPv4 address.
Field address#
pub address: Ipv4AddressThe address.
Aaaa#
AaaaAn IPv6 address.
Field address#
pub address: Ipv6AddressThe address.
Ns#
NsAn authoritative name server.
Field server#
pub server: NameThe name of the server.
Cname#
CnameThe canonical name of the owner, which is an alias.
Field target#
pub target: NameThe canonical name.
Ptr#
PtrA domain name pointer, such as the result of a reverse lookup.
Field target#
pub target: NameThe name that the record points to.
Mx#
MxA mail exchange.
Field preference#
pub preference: u16The preference. A lower value is preferred.
Field exchange#
pub exchange: NameThe name of the mail server.
Txt#
TxtText, as one or more character strings of at most 255 bytes each.
Field strings#
pub strings: List<Bytes>The strings, in order. The bytes are not always UTF-8.
Srv#
SrvA service location (RFC 2782).
Field priority#
pub priority: u16The priority. A lower value is tried first.
Field weight#
pub weight: u16The relative weight among targets of the same priority.
Field port#
pub port: u16The port of the service.
Field target#
pub target: NameThe name of the host of the service.
Soa#
SoaThe start of a zone of authority.
Field primary#
pub primary: NameThe name of the primary name server of the zone.
Field mailbox#
pub mailbox: NameThe mailbox of the person responsible for the zone, in name form.
Field serial#
pub serial: u32The version of the zone.
Field refresh#
pub refresh: u32The seconds between refreshes of a secondary server.
Field retry#
pub retry: u32The seconds before a failed refresh is tried again.
Field expire#
pub expire: u32The seconds after which a secondary server stops answering for the zone.
Field minimum#
pub minimum: u32The seconds to cache a negative answer (RFC 2308).
Naptr#
NaptrA naming authority pointer (RFC 3403).
Field order#
pub order: u16The order. A lower value is processed first.
Field preference#
pub preference: u16The preference among records of the same order.
Field flags#
pub flags: BytesThe flags string.
Field services#
pub services: BytesThe service string.
Field regexp#
pub regexp: BytesThe substitution expression.
Field replacement#
pub replacement: NameThe next name to look up.
Tlsa#
TlsaA TLS certificate association (RFC 6698).
Field usage#
pub usage: u8The certificate usage.
Field selector#
pub selector: u8The part of the certificate to match.
Field matchingType#
pub matchingType: u8How the data is matched.
Field data#
pub data: BytesThe certificate association data.
Caa#
CaaA certification authority authorization (RFC 8659).
Field flags#
pub flags: u8The flags. Bit 128 is the critical flag.
Field tag#
pub tag: BytesThe property tag, such as issue.
Field value#
pub value: BytesThe property value.
Unknown#
UnknownA record of another type, with its data as it is on the wire.
Field kind#
pub kind: u16The record type.
Field data#
pub data: BytesThe data. Names in it can be compressed and then point into the message.
Implementation RecordData#
impl RecordDataMethod kind#
pub fn kind(self: &Self) -> u16Returns the record type of the data, such as TYPE_A for RecordData.A.
Record#
pub struct RecordA resource record: an owner name, a class, a time to live, and typed data.
Field name#
pub name: NameThe name that owns the record.
Field class#
pub class: u16The class, usually CLASS_IN.
Field ttl#
pub ttl: u32The seconds that the record can be cached.
Field data#
pub data: RecordDataThe data. Its variant gives the record type.
EdnsOption#
pub struct EdnsOptionOne option of an EDNS(0) OPT record.
Field code#
pub code: u16The option code, such as 10 for a cookie (RFC 7873).
Field data#
pub data: BytesThe option data.
Edns#
pub struct EdnsThe EDNS(0) information of a message (RFC 6891), from its OPT record.
Field udpPayloadSize#
pub udpPayloadSize: u16The largest UDP payload, in bytes, that the sender can receive.
Field version#
pub version: u8The EDNS version. Only version 0 is defined.
Field dnssecOk#
pub dnssecOk: boolThe DO bit: the sender can read DNSSEC records.
Field options#
pub options: List<EdnsOption>The options, in order.
Implementation Edns#
impl EdnsAssociated function make#
pub fn make(udpPayloadSize: u16) -> EdnsReturns version 0 with udpPayloadSize, the DO bit off, and no options.
Header#
pub struct HeaderThe fixed header of a message, without the section counts.
Field id#
pub id: u16The identifier that a response copies from its query.
Field response#
pub response: boolThe QR bit: true for a response, false for a query.
Field opcode#
pub opcode: u8The operation, such as OPCODE_QUERY. It uses 4 bits.
Field authoritative#
pub authoritative: boolThe AA bit: the server is an authority for the name.
Field truncated#
pub truncated: boolThe TC bit: the message was cut short to fit a UDP datagram.
Field recursionDesired#
pub recursionDesired: boolThe RD bit: the client asks for recursion.
Field recursionAvailable#
pub recursionAvailable: boolThe RA bit: the server can recurse.
Field authenticData#
pub authenticData: boolThe AD bit: the server checked the DNSSEC data (RFC 4035).
Field checkingDisabled#
pub checkingDisabled: boolThe CD bit: the client does not want DNSSEC checks (RFC 4035).
Field responseCode#
pub responseCode: u16The response code, such as RCODE_NAME_ERROR. With EDNS it has 12 bits: the upper 8
bits are in the OPT record.
Implementation Copy for Header#
impl Copy for HeaderImplementation Header#
impl HeaderAssociated function query#
pub fn query(id: u16) -> HeaderReturns the header of a query with id, with the RD bit on and all other bits off.
Associated function read#
pub fn read(input: &[u8]) -> Result<Header, MessageError>Reads the 12-byte header at the start of input.
When to use#
Use it to check the identifier or the TC bit of a datagram before you read the whole message.
Details#
The response code is the 4-bit code of the header only. Message.parse adds the extended bits
of the OPT record.
Errors#
Truncatedifinputhas fewer than 12 bytes.
Message#
pub struct MessageA DNS message: a header, four sections of questions and records, and optional EDNS information.
Field header#
pub header: HeaderThe header.
Field questions#
pub questions: List<Question>The question section.
Field answers#
pub answers: List<Record>The answer section.
Field authorities#
pub authorities: List<Record>The authority section.
Field additionals#
pub additionals: List<Record>The additional section, without the OPT record.
Field edns#
pub edns: Option<Edns>The EDNS information. Message.encode writes it as an OPT record at the end of the additional
section.
Implementation Message#
impl MessageAssociated function make#
pub fn make(header: Header) -> MessageReturns a message with header and empty sections, without EDNS.
Associated function query#
pub effect fn query(id: u16, question: Question, udpPayloadSize: Option<u16>) -> Message
! OutOfMemoryError
? &mut AllocatorReturns a standard query for question, with the RD bit on.
Details#
With udpPayloadSize, the query has an EDNS(0) OPT record that tells the server the largest UDP
response the client accepts. 1232 bytes is a size that avoids IP fragmentation on most networks.
Without it, a UDP response has at most 512 bytes.
Associated function parse#
pub effect fn parse(input: &[u8]) -> Message
! MessageError | OutOfMemoryError
? &mut AllocatorReads a whole message.
Details#
The input must be one message, as in one UDP datagram, or one TCP message without its two-byte
length prefix. Bytes after the last record are ignored. An OPT record in the additional section
is read into edns, and its extended response code bits are added to the header.
Errors#
MessageErrorif the message is cut short, has a bad name or record, or has a bad OPT record. The offset is the position of the problem ininput.OutOfMemoryErrorif theAllocatorcannot supply the sections.
Method encode#
pub effect fn encode(self: &Self, output: &mut Bytes) -> ()
! MessageError | OutOfMemoryError
? &mut AllocatorWrites the message in wire form at the end of output.
Details#
Names are compressed where RFC 3597 allows it. When edns is set, an OPT record is written at
the end of the additional section, with the upper bits of the response code. The output is one
UDP payload; for TCP, write the two-byte length first.
Errors#
TooLargeif a section has more than 65535 entries, or the message is longer than 65535 bytes.StringTooLongif a TXT, NAPTR or CAA string is longer than 255 bytes.BadRecordLengthif the data of anUnknownrecord is longer than 65535 bytes.BadResponseCodeif the response code is above 4095, or above 15 withoutedns.OutOfMemoryErrorif theAllocatorcannot growoutput.
Gotchas#
On an error, output can hold part of the message. Truncate it to its old length to undo.
Category: Networking.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.