use anyhow::{bail, Context, Result};
use std::convert::TryFrom;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::vec;
use bytes::{BufMut, BytesMut};
use packed_struct::prelude::*;
use crate::codec::{character_string, domain_name, message};
use crate::specs::message::{IntEnum, OPTOption, ResourceData, OPT};
use crate::specs::{enums_generated, enums_generated::ResourceType, rdata};
pub fn write_rdata(
resource_type: &IntEnum<u16, ResourceType>,
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::RawData(raw_data) = rdata {
buf.extend_from_slice(&raw_data);
return Ok(());
}
match resource_type {
IntEnum::Enum(e) => match e {
ResourceType::A => write_a(rdata, buf),
ResourceType::NS => write_ns(rdata, buf, ptr_offsets),
ResourceType::CNAME => write_cname(rdata, buf, ptr_offsets),
ResourceType::SOA => write_soa(rdata, buf, ptr_offsets),
ResourceType::PTR => write_ptr(rdata, buf, ptr_offsets),
ResourceType::HINFO => write_hinfo(rdata, buf),
ResourceType::MX => write_mx(rdata, buf, ptr_offsets),
ResourceType::TXT => write_txt(rdata, buf),
ResourceType::RP => write_rp(rdata, buf, ptr_offsets),
ResourceType::AFSDB => write_afsdb(rdata, buf, ptr_offsets),
ResourceType::AAAA => write_aaaa(rdata, buf),
ResourceType::SRV => write_srv(rdata, buf),
ResourceType::DNAME => write_dname(rdata, buf),
ResourceType::OPT => {
bail!(
"Writing resource.type={:?} must be done using rdata::write_opt",
resource_type
);
}
ResourceType::DS => write_ds(rdata, buf),
ResourceType::RRSIG => write_rrsig(rdata, buf),
ResourceType::NSEC => write_nsec(rdata, buf),
ResourceType::DNSKEY => write_dnskey(rdata, buf),
_ => bail!("Writing resource.type={:?} is unsupported", resource_type),
},
IntEnum::Unknown(i) => bail!("Writing unknown resource.type={} is unsupported", i),
}
}
pub fn read_rdata(
buf: &[u8],
resource_type: u16,
rdata_offset: usize,
rdata_len: usize,
) -> Result<ResourceData> {
match enums_generated::resourcetype_int(resource_type as usize) {
Some(ResourceType::A) => Ok(ResourceData::A(read_a(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::NS) => Ok(ResourceData::NS(read_ns(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::CNAME) => Ok(ResourceData::CNAME(read_cname(
buf,
rdata_offset,
rdata_len,
)?)),
Some(ResourceType::SOA) => Ok(ResourceData::SOA(read_soa(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::PTR) => Ok(ResourceData::PTR(read_ptr(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::HINFO) => Ok(ResourceData::HINFO(read_hinfo(
buf,
rdata_offset,
rdata_len,
)?)),
Some(ResourceType::MX) => Ok(ResourceData::MX(read_mx(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::TXT) => Ok(ResourceData::TXT(read_txt(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::RP) => Ok(ResourceData::RP(read_rp(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::AFSDB) => Ok(ResourceData::AFSDB(read_afsdb(
buf,
rdata_offset,
rdata_len,
)?)),
Some(ResourceType::AAAA) => {
Ok(ResourceData::AAAA(read_aaaa(buf, rdata_offset, rdata_len)?))
}
Some(ResourceType::SRV) => Ok(ResourceData::SRV(read_srv(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::DNAME) => Ok(ResourceData::DNAME(read_dname(
buf,
rdata_offset,
rdata_len,
)?)),
Some(ResourceType::OPT) => bail!(
"Reading resource.type={} must be done using rdata::read_opt",
resource_type
),
Some(ResourceType::DS) => Ok(ResourceData::DS(read_ds(buf, rdata_offset, rdata_len)?)),
Some(ResourceType::RRSIG) => Ok(ResourceData::RRSIG(read_rrsig(
buf,
rdata_offset,
rdata_len,
)?)),
Some(ResourceType::NSEC) => {
Ok(ResourceData::NSEC(read_nsec(buf, rdata_offset, rdata_len)?))
}
Some(ResourceType::DNSKEY) => Ok(ResourceData::DNSKEY(read_dnskey(
buf,
rdata_offset,
rdata_len,
)?)),
_ => Ok(ResourceData::RawData(
(&buf[rdata_offset..rdata_offset + rdata_len]).to_vec(),
)),
}
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataABits {
#[packed_field(bits = "0:7")]
address1: u8,
#[packed_field(bits = "8:15")]
address2: u8,
#[packed_field(bits = "16:23")]
address3: u8,
#[packed_field(bits = "24:31")]
address4: u8,
}
pub fn write_a_ip(ip: &Ipv4Addr, buf: &mut BytesMut) -> Result<()> {
let octets = ip.octets();
let packed_bits = RDataABits {
address1: octets[0],
address2: octets[1],
address3: octets[2],
address4: octets[3],
}
.pack()?;
buf.extend_from_slice(&packed_bits);
Ok(())
}
fn write_a(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::A(a) = rdata {
let packed_bits = RDataABits {
address1: a.address1,
address2: a.address2,
address3: a.address3,
address4: a.address4,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
Ok(())
} else {
bail!("Expected A record but was: {}", rdata);
}
}
fn read_a(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::A> {
let bits_size = 4; if rdata_len != bits_size {
bail!(
"A record rdata requires {} bytes, got {}",
bits_size,
rdata_len
);
}
let bits = RDataABits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack A record rdata bits")?;
Ok(rdata::A {
address1: bits.address1,
address2: bits.address2,
address3: bits.address3,
address4: bits.address4,
})
}
fn write_ns(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::NS(ns) = rdata {
domain_name::write(ns.nsdname.as_str(), buf, ptr_offsets, "rdata.ns.nsdname")
} else {
bail!("Expected NS record but was: {}", rdata);
}
}
fn read_ns(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::NS> {
let (name_bytes_consumed, name_str) = domain_name::read(buf, rdata_offset, "rdata.ns.nsdname")?;
if name_bytes_consumed != rdata_len {
bail!(
"Expected NS record data to be {} bytes, but was {} bytes",
rdata_len,
name_bytes_consumed
);
}
Ok(rdata::NS { nsdname: name_str })
}
fn write_cname(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::CNAME(cname) = rdata {
domain_name::write(cname.cname.as_str(), buf, ptr_offsets, "rdata.cname.cname")
} else {
bail!("Expected CNAME record but was: {}", rdata);
}
}
fn read_cname(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::CNAME> {
let (name_bytes_consumed, name_str) =
domain_name::read(buf, rdata_offset, "rdata.cname.cname")?;
if name_bytes_consumed != rdata_len {
bail!(
"Expected CNAME record data to be {} bytes, but was {} bytes",
rdata_len,
name_bytes_consumed
);
}
Ok(rdata::CNAME { cname: name_str })
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataSOABits {
#[packed_field(bits = "0:31")]
serial: u32,
#[packed_field(bits = "32:63")]
refresh: u32,
#[packed_field(bits = "64:95")]
retry: u32,
#[packed_field(bits = "96:127")]
expire: u32,
#[packed_field(bits = "128:159")]
minimum: u32,
}
fn write_soa(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::SOA(soa) = rdata {
write_soa_fields(
&SOAFields {
mname: soa.mname.as_str(),
rname: soa.rname.as_str(),
serial: soa.serial,
refresh: soa.refresh,
retry: soa.retry,
expire: soa.expire,
minimum: soa.minimum,
},
buf,
ptr_offsets,
)
} else {
bail!("Expected SOA record but was: {}", rdata);
}
}
pub struct SOAFields<'a> {
pub mname: &'a str,
pub rname: &'a str,
pub serial: u32,
pub refresh: u32,
pub retry: u32,
pub expire: u32,
pub minimum: u32,
}
pub fn write_soa_fields<'a>(
soa: &SOAFields<'a>,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
domain_name::write(soa.mname, buf, ptr_offsets, "rdata.soa.mname")?;
domain_name::write(soa.rname, buf, ptr_offsets, "rdata.soa.rname")?;
let packed_bits = RDataSOABits {
serial: soa.serial,
refresh: soa.refresh,
retry: soa.retry,
expire: soa.expire,
minimum: soa.minimum,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
Ok(())
}
fn read_soa(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::SOA> {
let bits_size = 20; if rdata_len < bits_size + 2 {
bail!(
"SOA record rdata requires at least {} bytes, got {}",
bits_size + 2,
rdata_len
);
}
let mut offset = rdata_offset;
let (mname_bytes_consumed, mname_str) = domain_name::read(buf, offset, "rdata.soa.mname")?;
offset += mname_bytes_consumed;
let (rname_bytes_consumed, rname_str) = domain_name::read(buf, offset, "rdata.soa.rname")?;
offset += rname_bytes_consumed;
if bits_size + (offset - rdata_offset) != rdata_len {
bail!(
"Expected SOA record data to be {} bytes, but was {} + ({} - {}) bytes",
rdata_len,
bits_size,
offset,
rdata_offset,
);
}
let bits = RDataSOABits::unpack_from_slice(&buf[offset..offset + bits_size])
.context("couldn't unpack SOA record rdata bits")?;
Ok(rdata::SOA {
mname: mname_str,
rname: rname_str,
serial: bits.serial,
refresh: bits.refresh,
retry: bits.retry,
expire: bits.expire,
minimum: bits.minimum,
})
}
fn write_ptr(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::PTR(ptr) = rdata {
domain_name::write(
ptr.ptrdname.as_str(),
buf,
ptr_offsets,
"rdata.ptr.ptrdname",
)
} else {
bail!("Expected PTR record but was: {}", rdata);
}
}
fn read_ptr(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::PTR> {
let (name_bytes_consumed, ptrdname) =
domain_name::read(buf, rdata_offset, "rdata.ptr.ptrdname")?;
if name_bytes_consumed != rdata_len {
bail!(
"Expected PTR record data to be {} bytes, but was {} bytes",
rdata_len,
name_bytes_consumed
);
}
Ok(rdata::PTR { ptrdname })
}
fn write_hinfo(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::HINFO(hinfo) = rdata {
character_string::write(hinfo.cpu.as_slice(), buf, "rdata.hinfo.cpu")?;
character_string::write(hinfo.os.as_slice(), buf, "rdata.hinfo.os")
} else {
bail!("Expected HINFO record but was: {}", rdata);
}
}
fn read_hinfo(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::HINFO> {
let (cpu_bytes_consumed, cpu) = character_string::read(&buf, rdata_offset, "rdata.hinfo.cpu")?;
let (os_bytes_consumed, os) =
character_string::read(&buf, rdata_offset + cpu_bytes_consumed, "rdata.hinfo.os")?;
if cpu_bytes_consumed + os_bytes_consumed != rdata_len {
bail!(
"Expected HINFO record data to be {} bytes, but was {} + {} bytes",
rdata_len,
cpu_bytes_consumed,
os_bytes_consumed
);
}
Ok(rdata::HINFO {
cpu: cpu.to_vec(),
os: os.to_vec(),
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataMXBits {
#[packed_field(bits = "0:15")]
preference: u16,
}
fn write_mx(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::MX(mx) = rdata {
let packed_bits = RDataMXBits {
preference: mx.preference,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
domain_name::write(mx.exchange.as_str(), buf, ptr_offsets, "rdata.mx.exchange")
} else {
bail!("Expected MX record but was: {}", rdata);
}
}
fn read_mx(buf: &[u8], mut rdata_offset: usize, rdata_len: usize) -> Result<rdata::MX> {
let bits_size = 2; if rdata_len < bits_size + 1 {
bail!(
"MX record rdata requires at least {} bytes, got {}",
bits_size + 1,
rdata_len
);
}
let bits = RDataMXBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack MX record rdata bits")?;
rdata_offset += bits_size;
let (exchange_bytes_consumed, exchange_str) =
domain_name::read(buf, rdata_offset, "rdata.mx.exchange")?;
if bits_size + exchange_bytes_consumed != rdata_len {
bail!(
"Expected MX record data to be {} bytes, but was {} + {} bytes",
rdata_len,
bits_size,
exchange_bytes_consumed
);
}
Ok(rdata::MX {
preference: bits.preference,
exchange: exchange_str,
})
}
fn write_txt(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::TXT(txt) = rdata {
for entry in &txt.entries {
write_txt_entry(entry.data.as_slice(), buf)?;
}
Ok(())
} else {
bail!("Expected TXT record but was: {}", rdata);
}
}
pub fn write_txt_entry(entry: &[u8], buf: &mut BytesMut) -> Result<()> {
character_string::write(entry, buf, "rdata.txt.entries[]")
}
fn read_txt(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::TXT> {
let mut total_consumed: usize = 0;
let mut entries = vec::Vec::new();
while total_consumed < rdata_len {
let (consumed, entry) =
character_string::read(buf, rdata_offset + total_consumed, "rdata.txt[]")?;
entries.push(rdata::TXTEntry {
data: entry.to_vec(),
});
total_consumed += consumed;
}
if total_consumed != rdata_len {
bail!(
"Expected TXT record data to be {} bytes, but was {} bytes",
rdata_len,
total_consumed
);
}
Ok(rdata::TXT { entries })
}
fn write_rp(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::RP(rp) = rdata {
domain_name::write(
rp.mbox_dname.as_str(),
buf,
ptr_offsets,
"rdata.rp.mbox_dname",
)?;
domain_name::write(
rp.txt_dname.as_str(),
buf,
ptr_offsets,
"rdata.rp.txt_dname",
)
} else {
bail!("Expected RP record but was: {}", rdata);
}
}
fn read_rp(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::RP> {
if rdata_len < 2 {
bail!(
"RP record rdata requires at least {} bytes, got {}",
2,
rdata_len
);
}
let mut offset = rdata_offset;
let (mboxname_bytes_consumed, mboxname_str) =
domain_name::read(buf, offset, "rdata.rp.mbox_dname")?;
offset += mboxname_bytes_consumed;
let (txtname_bytes_consumed, txtname_str) =
domain_name::read(buf, offset, "rdata.rp.txt_dname")?;
offset += txtname_bytes_consumed;
if (offset - rdata_offset) != rdata_len {
bail!(
"Expected RP record data to be {} bytes, but was {} - {} bytes",
rdata_len,
offset,
rdata_offset,
);
}
Ok(rdata::RP {
mbox_dname: mboxname_str,
txt_dname: txtname_str,
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataAFSDBBits {
#[packed_field(bits = "0:15")]
subtype: u16,
}
fn write_afsdb(
rdata: &ResourceData,
buf: &mut BytesMut,
ptr_offsets: &mut domain_name::LabelOffsets,
) -> Result<()> {
if let ResourceData::AFSDB(afsdb) = rdata {
let packed_bits = RDataAFSDBBits {
subtype: afsdb.subtype,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
domain_name::write(
afsdb.hostname.as_str(),
buf,
ptr_offsets,
"rdata.afsdb.hostname",
)
} else {
bail!("Expected AFSDB record but was: {}", rdata);
}
}
fn read_afsdb(buf: &[u8], mut rdata_offset: usize, rdata_len: usize) -> Result<rdata::AFSDB> {
let bits_size = 2; if rdata_len < bits_size + 1 {
bail!(
"AFSDB record rdata requires at least {} bytes, got {}",
bits_size + 1,
rdata_len
);
}
let bits = RDataAFSDBBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack AFSDB record rdata bits")?;
rdata_offset += bits_size;
let (hostname_bytes_consumed, hostname) =
domain_name::read(buf, rdata_offset, "rdata.afsdb.hostname")?;
if bits_size + hostname_bytes_consumed != rdata_len {
bail!(
"Expected AFSDB record data to be {} bytes, but was {} + {} bytes",
rdata_len,
bits_size,
hostname_bytes_consumed
);
}
Ok(rdata::AFSDB {
subtype: bits.subtype,
hostname,
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataAAAABits {
#[packed_field(bits = "0:15")]
address1: u16,
#[packed_field(bits = "16:31")]
address2: u16,
#[packed_field(bits = "32:47")]
address3: u16,
#[packed_field(bits = "48:63")]
address4: u16,
#[packed_field(bits = "64:79")]
address5: u16,
#[packed_field(bits = "80:95")]
address6: u16,
#[packed_field(bits = "96:111")]
address7: u16,
#[packed_field(bits = "112:127")]
address8: u16,
}
pub fn write_aaaa_ip(ip: &Ipv6Addr, buf: &mut BytesMut) -> Result<()> {
let segments = ip.segments();
let packed_bits = RDataAAAABits {
address1: segments[0],
address2: segments[1],
address3: segments[2],
address4: segments[3],
address5: segments[4],
address6: segments[5],
address7: segments[6],
address8: segments[7],
}
.pack()?;
buf.extend_from_slice(&packed_bits);
Ok(())
}
fn write_aaaa(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::AAAA(aaaa) = rdata {
let packed_bits = RDataAAAABits {
address1: aaaa.address1,
address2: aaaa.address2,
address3: aaaa.address3,
address4: aaaa.address4,
address5: aaaa.address5,
address6: aaaa.address6,
address7: aaaa.address7,
address8: aaaa.address8,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
Ok(())
} else {
bail!("Expected AAAA record but was: {}", rdata);
}
}
fn read_aaaa(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::AAAA> {
let bits_size = 16; if rdata_len < bits_size {
bail!(
"AAAA record rdata requires at least {} bytes, got {}",
bits_size,
rdata_len
);
}
let bits = RDataAAAABits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack AAAA record rdata bits")?;
Ok(rdata::AAAA {
address1: bits.address1,
address2: bits.address2,
address3: bits.address3,
address4: bits.address4,
address5: bits.address5,
address6: bits.address6,
address7: bits.address7,
address8: bits.address8,
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataSRVBits {
#[packed_field(bits = "0:15")]
priority: u16,
#[packed_field(bits = "16:31")]
weight: u16,
#[packed_field(bits = "32:47")]
port: u16,
}
fn write_srv(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::SRV(srv) = rdata {
let packed_bits = RDataSRVBits {
priority: srv.priority,
weight: srv.weight,
port: srv.port,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
domain_name::write_nopointer(srv.target.as_str(), buf, "rdata.srv.target")?;
Ok(())
} else {
bail!("Expected SRV record but was: {}", rdata);
}
}
fn read_srv(buf: &[u8], mut rdata_offset: usize, rdata_len: usize) -> Result<rdata::SRV> {
let bits_size = 6; if rdata_len < bits_size + 1 {
bail!(
"SRV record rdata requires at least {} bytes, got {}",
bits_size + 1,
rdata_len
);
}
let bits = RDataSRVBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack SRV record rdata bits")?;
rdata_offset += bits_size;
let (target_bytes_consumed, target) =
domain_name::read_noptr(buf, rdata_offset, "rdata.srv.target")?;
if bits_size + target_bytes_consumed != rdata_len {
bail!(
"Expected SRV record data to be {} bytes, but was {} + {} bytes",
rdata_len,
bits_size,
target_bytes_consumed
);
}
Ok(rdata::SRV {
priority: bits.priority,
weight: bits.weight,
port: bits.priority,
target,
})
}
fn write_dname(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::DNAME(dname) = rdata {
domain_name::write_nopointer(dname.dname.as_str(), buf, "rdata.dname.dname")?;
Ok(())
} else {
bail!("Expected DNAME record but was: {}", rdata);
}
}
fn read_dname(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::DNAME> {
let (name_bytes_consumed, name_str) =
domain_name::read_noptr(buf, rdata_offset, "rdata.dname.dname")?;
if name_bytes_consumed != rdata_len {
bail!(
"Expected DNAME record data to be {} bytes, but was {} bytes",
rdata_len,
name_bytes_consumed
);
}
Ok(rdata::DNAME { dname: name_str })
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataOPTEntryBits {
#[packed_field(bits = "0:15")]
code: u16,
#[packed_field(bits = "16:31")]
length: u16,
}
pub fn write_opt(opt: &OPT, buf: &mut BytesMut) -> Result<()> {
for entry in &opt.option {
let packed_bits = RDataOPTEntryBits {
code: match entry.code {
IntEnum::Enum(e) => e as u16,
IntEnum::Unknown(i) => i,
},
length: u16::try_from(entry.data.len())
.with_context(|| "opt.data length doesn't fit")?,
}
.pack()?;
buf.reserve(packed_bits.len() + entry.data.len());
buf.put_slice(&packed_bits);
buf.put_slice(&entry.data);
}
Ok(())
}
pub fn read_opt(
buf: &[u8],
rdata_offset: usize,
rdata_len: usize,
opt_class_ttl_bits: message::ResourceClassTTLOPTBits,
) -> Result<OPT> {
let mut total_consumed: usize = 0;
let mut entries = vec::Vec::new();
while total_consumed < rdata_len {
let bits_size = 4; if rdata_len < total_consumed + bits_size {
bail!(
"OPT entry header requires at least {} bytes, got {}/{} consumed",
bits_size,
total_consumed,
rdata_len
);
}
let bits = RDataOPTEntryBits::unpack_from_slice(
&buf[rdata_offset + total_consumed..rdata_offset + total_consumed + bits_size],
)
.context("couldn't unpack OPT record rdata bits")?;
total_consumed += bits_size;
if rdata_len < total_consumed + (bits.length as usize) {
bail!(
"OPT entry data requires at least {} bytes, got {}/{} consumed",
bits.length,
total_consumed,
rdata_len
);
}
entries.push(OPTOption {
code: match enums_generated::optoptioncode_int(bits.code as usize) {
Some(e) => IntEnum::Enum(e),
None => IntEnum::Unknown(bits.code),
},
data: (&buf[rdata_offset + total_consumed
..rdata_offset + total_consumed + (bits.length as usize)])
.to_vec(),
});
total_consumed += bits.length as usize;
}
if total_consumed != rdata_len {
bail!(
"Expected OPT record data to be {} bytes, but was {} bytes",
rdata_len,
total_consumed
);
}
Ok(OPT {
option: entries,
udp_size: opt_class_ttl_bits.udp_size,
response_code: opt_class_ttl_bits.response_code,
version: opt_class_ttl_bits.version,
dnssec_ok: opt_class_ttl_bits.dnssec_ok,
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataDSBits {
#[packed_field(bits = "0:15")]
key_tag: u16,
#[packed_field(bits = "16:23")]
algorithm: u8,
#[packed_field(bits = "24:31")]
digest_type: u8,
}
fn write_ds(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::DS(ds) = rdata {
let packed_bits = RDataDSBits {
key_tag: ds.key_tag,
algorithm: ds.algorithm,
digest_type: ds.digest_type,
}
.pack()?;
buf.reserve(packed_bits.len() + ds.digest.len());
buf.put_slice(&packed_bits);
buf.put_slice(&ds.digest);
Ok(())
} else {
bail!("Expected DS record but was: {}", rdata);
}
}
fn read_ds(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::DS> {
let bits_size = 4; if rdata_len < bits_size {
bail!(
"DS record rdata requires at least {} bytes, got {}",
bits_size,
rdata_len
);
}
let bits = RDataDSBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack DS record rdata bits")?;
Ok(rdata::DS {
key_tag: bits.key_tag,
algorithm: bits.algorithm,
digest_type: bits.digest_type,
digest: (&buf[rdata_offset + bits_size..rdata_offset + rdata_len]).to_vec(),
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataRRSIGBits {
#[packed_field(bits = "0:15")]
type_covered: u16,
#[packed_field(bits = "16:23")]
algorithm: u8,
#[packed_field(bits = "24:31")]
labels: u8,
#[packed_field(bits = "32:63")]
original_ttl: u32,
#[packed_field(bits = "64:95")]
signature_expiration: u32,
#[packed_field(bits = "96:127")]
signature_inception: u32,
#[packed_field(bits = "128:143")]
key_tag: u16,
}
fn write_rrsig(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::RRSIG(rrsig) = rdata {
let packed_bits = RDataRRSIGBits {
type_covered: rrsig.type_covered,
algorithm: rrsig.algorithm,
labels: rrsig.labels,
original_ttl: rrsig.original_ttl,
signature_expiration: rrsig.signature_expiration,
signature_inception: rrsig.signature_inception,
key_tag: rrsig.key_tag,
}
.pack()?;
buf.extend_from_slice(&packed_bits);
domain_name::write_nopointer(rrsig.signers_name.as_str(), buf, "rdata.rrsig.signers_name")?;
buf.extend_from_slice(rrsig.signature.as_slice());
Ok(())
} else {
bail!("Expected RRSIG record but was: {}", rdata);
}
}
fn read_rrsig(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::RRSIG> {
let bits_size = 18; if rdata_len < bits_size {
bail!(
"RRSIG record rdata requires at least {} bytes, got {}",
bits_size,
rdata_len
);
}
let bits = RDataRRSIGBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack RRSIG record rdata bits")?;
let mut total_consumed = bits_size;
let (signers_name_bytes_consumed, signers_name) = domain_name::read_noptr(
buf,
rdata_offset + total_consumed,
"rdata.rrsig.signers_name",
)?;
total_consumed += signers_name_bytes_consumed;
Ok(rdata::RRSIG {
type_covered: bits.type_covered,
algorithm: bits.algorithm,
labels: bits.labels,
original_ttl: bits.original_ttl,
signature_expiration: bits.signature_expiration,
signature_inception: bits.signature_inception,
key_tag: bits.key_tag,
signers_name,
signature: (&buf[rdata_offset + total_consumed..rdata_offset + rdata_len]).to_vec(),
})
}
fn write_nsec(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::NSEC(nsec) = rdata {
domain_name::write_nopointer(
nsec.next_domain_name.as_str(),
buf,
"rdata.nsec.next_domain_name",
)?;
buf.extend_from_slice(&nsec.type_bit_maps);
Ok(())
} else {
bail!("Expected NSEC record but was: {}", rdata);
}
}
fn read_nsec(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::NSEC> {
let (next_domain_bytes_consumed, next_domain_name) =
domain_name::read_noptr(buf, rdata_offset, "rdata.nsec.next_domain_name")?;
Ok(rdata::NSEC {
next_domain_name,
type_bit_maps: (&buf[rdata_offset + next_domain_bytes_consumed..rdata_offset + rdata_len])
.to_vec(),
})
}
#[derive(PackedStruct)]
#[packed_struct(endian = "msb", bit_numbering = "msb0")]
pub struct RDataDNSKEYBits {
#[packed_field(bits = "0:15")]
flags: u16,
#[packed_field(bits = "16:23")]
protocol: u8,
#[packed_field(bits = "24:31")]
algorithm: u8,
}
fn write_dnskey(rdata: &ResourceData, buf: &mut BytesMut) -> Result<()> {
if let ResourceData::DNSKEY(dnskey) = rdata {
let packed_bits = RDataDNSKEYBits {
flags: dnskey.flags,
protocol: dnskey.protocol,
algorithm: dnskey.algorithm,
}
.pack()?;
buf.reserve(packed_bits.len() + dnskey.public_key.len());
buf.put_slice(&packed_bits);
buf.put_slice(&dnskey.public_key);
Ok(())
} else {
bail!("Expected DNSKEY record but was: {}", rdata);
}
}
fn read_dnskey(buf: &[u8], rdata_offset: usize, rdata_len: usize) -> Result<rdata::DNSKEY> {
let bits_size = 4; if rdata_len < bits_size {
bail!(
"DNSKEY record rdata requires at least {} bytes, got {}",
bits_size,
rdata_len
);
}
let bits = RDataDNSKEYBits::unpack_from_slice(&buf[rdata_offset..rdata_offset + bits_size])
.context("couldn't unpack DNSKEY record rdata bits")?;
Ok(rdata::DNSKEY {
flags: bits.flags,
protocol: bits.protocol,
algorithm: bits.algorithm,
public_key: (&buf[rdata_offset + bits_size..rdata_offset + rdata_len]).to_vec(),
})
}