Update examples with modernized code

This commit is contained in:
Emil Hernvall
2020-06-18 01:47:09 +02:00
parent 31369696d9
commit f815075ae4
10 changed files with 708 additions and 627 deletions

View File

@@ -11,6 +11,8 @@ pub struct BytePacketBuffer {
}
impl BytePacketBuffer {
/// This gives us a fresh buffer for holding the packet contents, and a
/// field for keeping track of where we are.
pub fn new() -> BytePacketBuffer {
BytePacketBuffer {
buf: [0; 512],
@@ -18,22 +20,26 @@ impl BytePacketBuffer {
}
}
/// Current position within buffer
fn pos(&self) -> usize {
self.pos
}
/// Step the buffer position forward a specific number of steps
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
/// Change the buffer position
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
/// Read a single byte and move the position one step forward
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err("End of buffer".into());
@@ -44,6 +50,7 @@ impl BytePacketBuffer {
Ok(res)
}
/// Get a single byte, without changing the buffer position
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err("End of buffer".into());
@@ -51,6 +58,7 @@ impl BytePacketBuffer {
Ok(self.buf[pos])
}
/// Get a range of bytes
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err("End of buffer".into());
@@ -58,12 +66,14 @@ impl BytePacketBuffer {
Ok(&self.buf[start..start + len as usize])
}
/// Read two bytes, stepping two steps forward
fn read_u16(&mut self) -> Result<u16> {
let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
Ok(res)
}
/// Read four bytes, stepping four steps forward
fn read_u32(&mut self) -> Result<u32> {
let res = ((self.read()? as u32) << 24)
| ((self.read()? as u32) << 16)
@@ -73,13 +83,28 @@ impl BytePacketBuffer {
Ok(res)
}
/// Read a qname
///
/// The tricky part: Reading domain names, taking labels into consideration.
/// Will take something like [3]www[6]google[3]com[0] and append
/// www.google.com to outstr.
fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
// Since we might encounter jumps, we'll keep track of our position
// locally as opposed to using the position within the struct. This
// allows us to move the shared position to a point past our current
// qname, while keeping track of our progress on the current qname
// using this variable.
let mut pos = self.pos();
let mut jumped = false;
let mut delim = "";
// track whether or not we've jumped
let mut jumped = false;
let max_jumps = 5;
let mut jumps_performed = 0;
// Our delimiter which we append for each label. Since we don't want a
// dot at the beginning of the domain name we'll leave it empty for now
// and set it to "." at the end of the first iteration.
let mut delim = "";
loop {
// Dns Packets are untrusted data, so we need to be paranoid. Someone
// can craft a packet with a cycle in the jump instructions. This guards
@@ -88,42 +113,56 @@ impl BytePacketBuffer {
return Err(format!("Limit of {} jumps exceeded", max_jumps).into());
}
// At this point, we're always at the beginning of a label. Recall
// that labels start with a length byte.
let len = self.get(pos)?;
// A two byte sequence, where the two highest bits of the first byte is
// set, represents a offset relative to the start of the buffer. We
// handle this by jumping to the offset, setting a flag to indicate
// that we shouldn't update the shared buffer position once done.
// If len has the two most significant bit are set, it represents a
// jump to some other offset in the packet:
if (len & 0xC0) == 0xC0 {
// When a jump is performed, we only modify the shared buffer
// position once, and avoid making the change later on.
// Update the buffer position to a point past the current
// label. We don't need to touch it any further.
if !jumped {
self.seek(pos + 2)?;
}
// Read another byte, calculate offset and perform the jump by
// updating our local position variable
let b2 = self.get(pos + 1)? as u16;
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
pos = offset as usize;
// Indicate that a jump was performed.
jumped = true;
jumps_performed += 1;
continue;
}
// The base scenario, where we're reading a single label and
// appending it to the output:
else {
// Move a single byte forward to move past the length byte.
pos += 1;
pos += 1;
// Domain names are terminated by an empty label of length 0,
// so if the length is zero we're done.
if len == 0 {
break;
}
// Names are terminated by an empty label of length 0
if len == 0 {
break;
// Append the delimiter to our output buffer first.
outstr.push_str(delim);
// Extract the actual ASCII bytes for this label and append them
// to the output buffer.
let str_buffer = self.get_range(pos, len as usize)?;
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
// Move forward the full length of the label.
pos += len as usize;
}
outstr.push_str(delim);
let str_buffer = self.get_range(pos, len as usize)?;
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
pos += len as usize;
}
if !jumped {
@@ -386,19 +425,19 @@ fn main() -> Result<()> {
f.read(&mut buffer.buf)?;
let packet = DnsPacket::from_buffer(&mut buffer)?;
println!("{:?}", packet.header);
println!("{:#?}", packet.header);
for q in packet.questions {
println!("{:?}", q);
println!("{:#?}", q);
}
for rec in packet.answers {
println!("{:?}", rec);
println!("{:#?}", rec);
}
for rec in packet.authorities {
println!("{:?}", rec);
println!("{:#?}", rec);
}
for rec in packet.resources {
println!("{:?}", rec);
println!("{:#?}", rec);
}
Ok(())