Module domain::base::message_builder

source ·
Expand description

Building a new DNS message.

The types in this module allow building a DNS message consecutively from its parts. Since messages consist of five parts, a number of types are involved. The concept is that you start out with a MessageBuilder and work your way step by step through the sections by trading the builder in for on of another type representing the following section. The sequence is MessageBuilder, QuestionBuilder, AnswerBuilder, AuthorityBuilder, and finally AdditionalBuilder.

You can skip forward over unwanted sections. You can also go backwards, but then you’ll loose anything you built before. The naming of the methods that do these things is consistent across types: builder takes you to the message builder. The four methods question, answer, additional, and authority progress or return to the respective section. Finally, finish completes building.

Each of the section builders offers a push method to add elements to the section. For the question section, the method accepts anything that resembles a Question while the three record sections except something that looks like a Record. Apart from actual values of these types, tuples of the components also work, such as a pair of a domain name and a record type for a question or a triple of the owner name, TTL, and record data for a record. If you already have a question or record, you can use the push_ref method to add

The push method of the record section builders is also available via the RecordSectionBuilder trait so you can build code that works with all three record sections.

The AdditionalBuilder has a special feature that helps building the OPT record for EDNS. Its opt method allows a closure to build this record on the fly via the OptBuilder type.

Building happens atop any octets builder, so the type of buffer to use for building can be chosen. The module also provides a few helper types that provide optional features for building messages. All of these are wrappers around an octets builder and are octets builders themselves, so you can mix and match.

First, the StreamTarget builds a message for use with streaming transport protocols, e.g., TCP, where the actual message is preceded by a 16 bit length counter. The stream target keeps this counter up-to-date and makes sure the message doesn’t become longer than what the counter can provide for.

Two further types, TreeCompressor and StaticCompressor, provide name compression. This is a mechanism to decrease the size of a DNS message by avoiding repeating domain names: Instead of including a domain name or suffix of a domain name that has been mentioned already, a pointer to the position of the original mention is provided. Since this process is somewhat expensive as you have to remember which names have already been used, it isn’t enabled by default and provided via separate octets builders instead which we call compressors.

Currently, there are two different compressors. TreeCompressor stores all names it encountered in a binary tree. While it can handle any number of names, it does require an allocator and therefore cannot be used in a no_std environment. StaticCompressor, meanwhile, has a static table for up to 24 names. It is thus becoming ineffective on large messages with lots of different names. However, 24 should be good enough for most normal messages.

§Example

The following example builds a message with both name compression and the stream length and simply puts two A records into it.

use std::str::FromStr;
use domain::base::{
    Dname, MessageBuilder, Rtype, StaticCompressor, StreamTarget
};
use domain::rdata::A;

// Make a domain name we can use later on.
let name = Dname::<Vec<u8>>::from_str("example.com").unwrap();

// Create a message builder wrapping a compressor wrapping a stream
// target.
let mut msg = MessageBuilder::from_target(
    StaticCompressor::new(
        StreamTarget::new_vec()
    )
).unwrap();

// Set the RD bit in the header and proceed to the question section.
msg.header_mut().set_rd(true);
let mut msg = msg.question();

// Add a question and proceed to the answer section.
msg.push((&name, Rtype::A)).unwrap();
let mut msg = msg.answer();

// Add two answer and proceed to the additional sections
msg.push((&name, 86400, A::from_octets(192, 0, 2, 1))).unwrap();
msg.push((&name, 86400, A::from_octets(192, 0, 2, 2))).unwrap();
let mut msg = msg.additional();

// Add an OPT record.
msg.opt(|opt| {
    opt.set_udp_payload_size(4096);
    Ok(())
}).unwrap();

// Convert the builder into the actual message.
let target = msg.finish().into_target();

// A stream target can provide access to the data with or without the
// length counter:
let _ = target.as_stream_slice(); // With length
let _ = target.as_dgram_slice(); // Without length

Structs§

Enums§

Traits§