1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
use std::collections::HashMap;
use super::header::*;
use super::question::*;
use super::resource::*;
use super::*;
use crate::error::*;
// A Builder allows incrementally packing a DNS message.
//
// Example usage:
// b := NewBuilder(Header{...})
// b.enable_compression()
// // Optionally start a section and add things to that section.
// // Repeat adding sections as necessary.
// buf, err := b.Finish()
// // If err is nil, buf[2:] will contain the built bytes.
#[derive(Default)]
pub struct Builder {
// msg is the storage for the message being built.
pub msg: Option<Vec<u8>>,
// section keeps track of the current section being built.
pub section: Section,
// header keeps track of what should go in the header when Finish is
// called.
pub header: HeaderInternal,
// start is the starting index of the bytes allocated in msg for header.
pub start: usize,
// compression is a mapping from name suffixes to their starting index
// in msg.
pub compression: Option<HashMap<String, usize>>,
}
impl Builder {
// NewBuilder creates a new builder with compression disabled.
//
// Note: Most users will want to immediately enable compression with the
// enable_compression method. See that method's comment for why you may or may
// not want to enable compression.
//
// The DNS message is appended to the provided initial buffer buf (which may be
// nil) as it is built. The final message is returned by the (*Builder).Finish
// method, which may return the same underlying array if there was sufficient
// capacity in the slice.
pub fn new(h: &Header) -> Self {
let (id, bits) = h.pack();
Builder {
msg: Some(vec![0; HEADER_LEN]),
start: 0,
section: Section::Header,
header: HeaderInternal {
id,
bits,
..Default::default()
},
compression: None,
}
//var hb [HEADER_LEN]byte
//b.msg = append(b.msg, hb[:]...)
//return b
}
// enable_compression enables compression in the Builder.
//
// Leaving compression disabled avoids compression related allocations, but can
// result in larger message sizes. Be careful with this mode as it can cause
// messages to exceed the UDP size limit.
//
// According to RFC 1035, section 4.1.4, the use of compression is optional, but
// all implementations must accept both compressed and uncompressed DNS
// messages.
//
// Compression should be enabled before any sections are added for best results.
pub fn enable_compression(&mut self) {
self.compression = Some(HashMap::new());
}
fn start_check(&self, section: Section) -> Result<()> {
if self.section <= Section::NotStarted {
return Err(Error::ErrNotStarted);
}
if self.section > section {
return Err(Error::ErrSectionDone);
}
Ok(())
}
// start_questions prepares the builder for packing Questions.
pub fn start_questions(&mut self) -> Result<()> {
self.start_check(Section::Questions)?;
self.section = Section::Questions;
Ok(())
}
// start_answers prepares the builder for packing Answers.
pub fn start_answers(&mut self) -> Result<()> {
self.start_check(Section::Answers)?;
self.section = Section::Answers;
Ok(())
}
// start_authorities prepares the builder for packing Authorities.
pub fn start_authorities(&mut self) -> Result<()> {
self.start_check(Section::Authorities)?;
self.section = Section::Authorities;
Ok(())
}
// start_additionals prepares the builder for packing Additionals.
pub fn start_additionals(&mut self) -> Result<()> {
self.start_check(Section::Additionals)?;
self.section = Section::Additionals;
Ok(())
}
fn increment_section_count(&mut self) -> Result<()> {
let section = self.section;
let (count, err) = match section {
Section::Questions => (&mut self.header.questions, Error::ErrTooManyQuestions),
Section::Answers => (&mut self.header.answers, Error::ErrTooManyAnswers),
Section::Authorities => (&mut self.header.authorities, Error::ErrTooManyAuthorities),
Section::Additionals => (&mut self.header.additionals, Error::ErrTooManyAdditionals),
Section::NotStarted => return Err(Error::ErrNotStarted),
Section::Done => return Err(Error::ErrSectionDone),
Section::Header => return Err(Error::ErrSectionHeader),
};
if *count == u16::MAX {
Err(err)
} else {
*count += 1;
Ok(())
}
}
// question adds a single question.
pub fn add_question(&mut self, q: &Question) -> Result<()> {
if self.section < Section::Questions {
return Err(Error::ErrNotStarted);
}
if self.section > Section::Questions {
return Err(Error::ErrSectionDone);
}
let msg = self.msg.take();
if let Some(mut msg) = msg {
msg = q.pack(msg, &mut self.compression, self.start)?;
self.increment_section_count()?;
self.msg = Some(msg);
}
Ok(())
}
fn check_resource_section(&self) -> Result<()> {
if self.section < Section::Answers {
return Err(Error::ErrNotStarted);
}
if self.section > Section::Additionals {
return Err(Error::ErrSectionDone);
}
Ok(())
}
// Resource adds a single resource.
pub fn add_resource(&mut self, r: &mut Resource) -> Result<()> {
self.check_resource_section()?;
if let Some(body) = &r.body {
r.header.typ = body.real_type();
} else {
return Err(Error::ErrNilResourceBody);
}
if let Some(msg) = self.msg.take() {
let (mut msg, len_off) = r.header.pack(msg, &mut self.compression, self.start)?;
let pre_len = msg.len();
if let Some(body) = &r.body {
msg = body.pack(msg, &mut self.compression, self.start)?;
r.header.fix_len(&mut msg, len_off, pre_len)?;
self.increment_section_count()?;
}
self.msg = Some(msg);
}
Ok(())
}
// Finish ends message building and generates a binary message.
pub fn finish(&mut self) -> Result<Vec<u8>> {
if self.section < Section::Header {
return Err(Error::ErrNotStarted);
}
self.section = Section::Done;
// Space for the header was allocated in NewBuilder.
let buf = self.header.pack(vec![]);
assert_eq!(buf.len(), HEADER_LEN);
if let Some(mut msg) = self.msg.take() {
msg[..HEADER_LEN].copy_from_slice(&buf[..HEADER_LEN]);
Ok(msg)
} else {
Err(Error::ErrEmptyBuilderMsg)
}
}
}