use crate::{PacketFlag, OPCODE, RCODE};
use super::header::masks;
pub fn id(buffer: &[u8]) -> crate::Result<u16> {
check_buffer_len(buffer).map(id_unchecked)
}
pub fn id_unchecked(buffer: &[u8]) -> u16 {
u16::from_be_bytes(buffer[..2].try_into().unwrap())
}
pub fn questions(buffer: &[u8]) -> crate::Result<u16> {
check_buffer_len(buffer).map(questions_unchecked)
}
pub fn questions_unchecked(buffer: &[u8]) -> u16 {
u16::from_be_bytes(buffer[4..6].try_into().unwrap())
}
#[cfg(test)]
pub(crate) fn set_questions(buffer: &mut [u8], question_count: u16) {
buffer[4..6].copy_from_slice(&question_count.to_be_bytes());
}
pub fn answers(buffer: &[u8]) -> crate::Result<u16> {
check_buffer_len(buffer).map(answers_unchecked)
}
pub fn answers_unchecked(buffer: &[u8]) -> u16 {
u16::from_be_bytes(buffer[6..8].try_into().unwrap())
}
#[cfg(test)]
pub(crate) fn set_answers(buffer: &mut [u8], answers_count: u16) {
buffer[6..8].copy_from_slice(&answers_count.to_be_bytes());
}
pub fn name_servers(buffer: &[u8]) -> crate::Result<u16> {
check_buffer_len(buffer).map(name_servers_unchecked)
}
pub fn name_servers_unchecked(buffer: &[u8]) -> u16 {
u16::from_be_bytes(buffer[8..10].try_into().unwrap())
}
#[cfg(test)]
pub(crate) fn set_name_servers(buffer: &mut [u8], name_servers_count: u16) {
buffer[8..10].copy_from_slice(&name_servers_count.to_be_bytes());
}
pub fn additional_records(buffer: &[u8]) -> crate::Result<u16> {
check_buffer_len(buffer).map(additional_records_unchecked)
}
pub fn additional_records_unchecked(buffer: &[u8]) -> u16 {
u16::from_be_bytes(buffer[10..12].try_into().unwrap())
}
#[cfg(test)]
pub(crate) fn set_additional_records(buffer: &mut [u8], additional_records_count: u16) {
buffer[10..12].copy_from_slice(&additional_records_count.to_be_bytes());
}
pub fn has_flags(buffer: &[u8], flags: PacketFlag) -> crate::Result<bool> {
check_buffer_len(buffer).map(|_| has_flags_unchecked(buffer, flags))
}
pub fn has_flags_unchecked(buffer: &[u8], flags: PacketFlag) -> bool {
let bits = u16::from_be_bytes(buffer[2..4].try_into().unwrap());
PacketFlag::from_bits_truncate(bits).contains(flags)
}
pub fn rcode(buffer: &[u8]) -> crate::Result<RCODE> {
check_buffer_len(buffer).map(rcode_unchecked)
}
pub fn rcode_unchecked(buffer: &[u8]) -> RCODE {
let flags = u16::from_be_bytes(buffer[2..4].try_into().unwrap());
(flags & masks::RESPONSE_CODE_MASK).into()
}
pub fn opcode(buffer: &[u8]) -> crate::Result<OPCODE> {
check_buffer_len(buffer).map(opcode_unchecked)
}
pub fn opcode_unchecked(buffer: &[u8]) -> OPCODE {
let flags = u16::from_be_bytes(buffer[2..4].try_into().unwrap());
((flags & masks::OPCODE_MASK) >> masks::OPCODE_MASK.trailing_zeros()).into()
}
fn check_buffer_len(buffer: &[u8]) -> crate::Result<&[u8]> {
if buffer.len() < 12 {
Err(crate::SimpleDnsError::InvalidHeaderData)
} else {
Ok(buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_write_questions_count() {
let mut buffer = [0u8; 12];
set_questions(&mut buffer, 1);
assert_eq!(1, questions(&buffer).unwrap());
}
#[test]
fn read_write_answers_count() {
let mut buffer = [0u8; 12];
set_answers(&mut buffer, 1);
assert_eq!(1, answers(&buffer).unwrap());
}
#[test]
fn read_write_name_servers_count() {
let mut buffer = [0u8; 12];
set_name_servers(&mut buffer, 1);
assert_eq!(1, name_servers(&buffer).unwrap());
}
#[test]
fn read_write_additional_records_count() {
let mut buffer = [0u8; 12];
set_additional_records(&mut buffer, 1);
assert_eq!(1, additional_records(&buffer).unwrap());
}
#[test]
fn id_returns_error_for_short_buffer() {
assert!(id(&[0u8; 11]).is_err());
}
#[test]
fn questions_returns_error_for_short_buffer() {
assert!(questions(&[0u8; 11]).is_err());
}
#[test]
fn answers_returns_error_for_short_buffer() {
assert!(answers(&[0u8; 11]).is_err());
}
#[test]
fn name_servers_returns_error_for_short_buffer() {
assert!(name_servers(&[0u8; 11]).is_err());
}
#[test]
fn additional_records_returns_error_for_short_buffer() {
assert!(additional_records(&[0u8; 11]).is_err());
}
#[test]
fn has_flags_returns_error_for_short_buffer() {
assert!(has_flags(&[0u8; 11], PacketFlag::RESPONSE).is_err());
}
#[test]
fn rcode_returns_error_for_short_buffer() {
assert!(rcode(&[0u8; 11]).is_err());
}
#[test]
fn opcode_returns_error_for_short_buffer() {
assert!(opcode(&[0u8; 11]).is_err());
}
#[test]
#[should_panic]
fn id_unchecked_panics_for_short_buffer() {
id_unchecked(&[]);
}
#[test]
#[should_panic]
fn questions_unchecked_panics_for_short_buffer() {
questions_unchecked(&[0u8; 4]);
}
#[test]
#[should_panic]
fn answers_unchecked_panics_for_short_buffer() {
answers_unchecked(&[0u8; 6]);
}
#[test]
#[should_panic]
fn name_servers_unchecked_panics_for_short_buffer() {
name_servers_unchecked(&[0u8; 8]);
}
#[test]
#[should_panic]
fn additional_records_unchecked_panics_for_short_buffer() {
additional_records_unchecked(&[0u8; 10]);
}
#[test]
#[should_panic]
fn has_flags_unchecked_panics_for_short_buffer() {
has_flags_unchecked(&[0u8; 1], PacketFlag::RESPONSE);
}
#[test]
#[should_panic]
fn rcode_unchecked_panics_for_short_buffer() {
rcode_unchecked(&[0u8; 1]);
}
#[test]
#[should_panic]
fn opcode_unchecked_panics_for_short_buffer() {
opcode_unchecked(&[0u8; 1]);
}
}