Co-authored-by: Tobias Ottenweller <tobi@ottenweller.net> Reviewed-on: #29 Co-authored-by: mice_on_drugs <tobi@ottenweller.net> Co-committed-by: mice_on_drugs <tobi@ottenweller.net>
166 lines
4.7 KiB
Rust
166 lines
4.7 KiB
Rust
use crate::models::DNSQuery;
|
|
use crate::models::DNSResponse;
|
|
use crate::proto::Coder;
|
|
|
|
pub struct UdpCoder {}
|
|
|
|
|
|
impl Coder for UdpCoder {
|
|
fn decode(datagram: &[u8]) -> Result<DNSQuery, DNSParseError> {
|
|
let message = Message::try_from(datagram)?;
|
|
Ok(DNSQuery {
|
|
id: message.header.id,
|
|
questions: vec![],
|
|
})
|
|
|
|
}
|
|
|
|
fn encode(respone: DNSResponse) -> Result<Vec<u8>, DNSParseError> {
|
|
todo!();
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum DNSParseError {
|
|
DatagramTooShort
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
|
enum MessageType {
|
|
Query,
|
|
Response,
|
|
}
|
|
|
|
impl From<bool> for MessageType {
|
|
fn from(value: bool) -> MessageType {
|
|
match value {
|
|
false => Self::Query,
|
|
true => Self::Response,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
struct Header {
|
|
/// used by the requester to match up replies to outstanding queries
|
|
id: u16,
|
|
/// specifies whether this message is a query or a response
|
|
message_type: MessageType,
|
|
opcode: u8,
|
|
/// specifies that the responding name server is an authority for the domain name in question section
|
|
authorative_answer: bool,
|
|
/// specifies that this message was truncated due to length greater than that permitted on the transmission channel
|
|
truncated: bool,
|
|
/// it directs the name server to pursue the query recursively
|
|
recursion_desired: bool,
|
|
/// denotes whether recursive query support is available in the name server
|
|
recursion_available: bool,
|
|
// no support of rfc4035 at the moment
|
|
//pub authentic_data: bool,
|
|
// no support of rfc4035 at the moment
|
|
//pub checking_disabled: bool,
|
|
response_code: u8,
|
|
/// TODO: add documuentation about this count
|
|
query_count: u16,
|
|
/// TODO: add documuentation about this count
|
|
answer_count: u16,
|
|
/// TODO: add documuentation about this count
|
|
name_server_count: u16,
|
|
/// TODO: add documuentation about this count
|
|
additional_count: u16,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Question {
|
|
name: String,
|
|
r#type: u16,
|
|
class: u16,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct ResourceRecord {
|
|
name: String,
|
|
r#type: u16,
|
|
class: u16,
|
|
ttl: u32,
|
|
data: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Message {
|
|
header: Header,
|
|
questions: Vec<Question>,
|
|
answers: Vec<ResourceRecord>,
|
|
authorities: Vec<ResourceRecord>,
|
|
additionals: Vec<ResourceRecord>,
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for Message {
|
|
type Error = DNSParseError;
|
|
|
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
|
todo!();
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for Header {
|
|
type Error = DNSParseError;
|
|
|
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
|
const QR_MASK: u8 = 0b10000000;
|
|
const OPCODE_MASK: u8 = 0b01111000;
|
|
const AA_MASK: u8 = 0b00000100;
|
|
const TC_MASK: u8 = 0b00000010;
|
|
const RD_MASK: u8 = 0b00000001;
|
|
|
|
const RA_MASK: u8 = 0b10000000;
|
|
// no support of rfc4035 at the moment
|
|
//const AD_MASK: u8 = 0b00100000;
|
|
//const CD_MASK: u8 = 0b00010000;
|
|
const RCODE_MASK: u8 = 0b00001111;
|
|
|
|
const OPCODE_OFFSET: u8 = 3;
|
|
|
|
if value.len() < 12 {
|
|
return Err(DNSParseError::DatagramTooShort);
|
|
}
|
|
|
|
let id = u16::from_be_bytes((&value[..2]).try_into().unwrap());
|
|
|
|
let message_type = MessageType::from(value[2] & QR_MASK != 0);
|
|
let opcode =(value[2] & OPCODE_MASK) >> OPCODE_OFFSET;
|
|
|
|
let authorative_answer = (value[2] & AA_MASK) != 0;
|
|
let truncated = (value[2] & TC_MASK) != 0;
|
|
let recursion_desired = (value[2] & RD_MASK) != 0;
|
|
let recursion_available = (value[3] & RA_MASK) != 0;
|
|
|
|
// no support for rfc4035 at the moment
|
|
//let authentic_data = (datagram[2] & AD_MASK) != 0;
|
|
//let checking_disabled = (datagram[2] & CD_MASK) != 0;
|
|
|
|
let response_code = value[3] & RCODE_MASK;
|
|
let query_count = u16::from_be_bytes((value[4..6]).try_into().unwrap());
|
|
let answer_count = u16::from_be_bytes((value[6..8]).try_into().unwrap());
|
|
let name_server_count = u16::from_be_bytes((value[8..10]).try_into().unwrap());
|
|
let additional_count = u16::from_be_bytes((value[10..12]).try_into().unwrap());
|
|
|
|
Ok(Header {
|
|
id,
|
|
message_type,
|
|
opcode,
|
|
authorative_answer,
|
|
truncated,
|
|
recursion_desired,
|
|
recursion_available,
|
|
//authentic_data, // no support of rfc4035 at the moment
|
|
//checking_disabled, // no support of rfc4035 at the moment
|
|
response_code,
|
|
query_count,
|
|
answer_count,
|
|
name_server_count,
|
|
additional_count,
|
|
})
|
|
}
|
|
}
|