use bnb::{BitEnum, bin, bitfield, u3, u4};
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u4)]
enum OpCode {
Query,
IQuery,
Status,
#[catch_all]
Other(u4),
}
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u4)]
enum RCode {
NoError,
FormErr,
ServFail,
NxDomain,
#[catch_all]
Other(u4),
}
#[bitfield(u16, bits = msb, bytes = big)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Flags {
qr: bool, opcode: OpCode, aa: bool, tc: bool, rd: bool, ra: bool, z: u3, rcode: RCode, }
#[bin(big, validate = header_soundness)]
#[derive(Debug, Clone, PartialEq)]
struct Header {
id: u16,
flags: Flags,
qdcount: u16,
ancount: u16,
nscount: u16,
arcount: u16,
}
fn header_soundness(h: &Header) -> Result<(), String> {
if h.flags.z() != u3::new(0) {
return Err(format!("reserved z bits must be 0, got {}", h.flags.z()));
}
Ok(())
}
#[bin(big, magic = 0xCAFEu16)]
#[derive(Debug, Clone, PartialEq)]
struct Frame {
#[brw(count_prefix = u8)]
payload: Vec<u8>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let flags = Flags::new()
.with_qr(true)
.with_opcode(OpCode::Query)
.with_rd(true)
.with_ra(true)
.with_rcode(RCode::NoError);
let header = Header::builder()
.id(0x1234)
.flags(flags)
.qdcount(1)
.ancount(1)
.nscount(0)
.arcount(0)
.build()?;
let bytes = header.to_bytes()?;
assert_eq!(bytes.len(), 12);
assert_eq!(&bytes[..4], &[0x12, 0x34, 0x81, 0x80]); println!("DNS header ({} bytes): {bytes:02x?}", bytes.len());
let parsed = Header::decode_exact(&bytes)?;
assert_eq!(parsed, header);
println!(
" round-trips: opcode={:?}, rcode={:?}",
parsed.flags.opcode(),
parsed.flags.rcode()
);
let malformed = Header::builder()
.id(0)
.flags(Flags::new().with_z(u3::new(0b101)))
.qdcount(0)
.ancount(0)
.nscount(0)
.arcount(0)
.build();
assert!(malformed.is_err());
println!(
" builder rejects reserved-bits-set: {}",
malformed.unwrap_err()
);
assert!(Header::decode_exact(&header_with_reserved_bits()).is_ok());
let frame = Frame::builder()
.payload(vec![0xDE, 0xAD, 0xBE, 0xEF])
.build()?;
let framed = frame.to_bytes()?;
assert_eq!(framed, [0xCA, 0xFE, 0x04, 0xDE, 0xAD, 0xBE, 0xEF]); println!("Frame ({} bytes): {framed:02x?}", framed.len());
let back = Frame::decode_exact(&framed)?;
assert_eq!(back, frame);
assert_eq!(back.payload.len(), 4);
let err = Frame::decode_exact(&[0x00, 0x00, 0x00]).unwrap_err();
println!(" bad magic → {err}");
println!("all round-trips verified ✓");
Ok(())
}
fn header_with_reserved_bits() -> Vec<u8> {
let mut bytes = vec![0u8; 12];
bytes[3] = 0b0111_0000; bytes
}