diff --git a/dnstp/src/byte.rs b/dnstp/src/byte/mod.rs similarity index 66% rename from dnstp/src/byte.rs rename to dnstp/src/byte/mod.rs index b85533e..f046323 100644 --- a/dnstp/src/byte.rs +++ b/dnstp/src/byte/mod.rs @@ -1,5 +1,8 @@ //! Utility functions for operating on bytes +#[cfg(test)] +mod tests; + /// 8-bit mask for wiping out bits past a byte. Formatted as a 32-bit number const BYTEMASK_32: u32 = 0b11111111; /// 8-bit mask for wiping out bits past a byte. Formatted as a 16-bit number @@ -8,7 +11,12 @@ const BYTEMASK_16: u16 = 0b11111111; /// Take two sequential bytes starting from idx in buffer and return a concatenated 2 byte number pub fn two_byte_extraction(buffer: &[u8], idx: usize) -> u16 { - ((buffer[idx] as u16) << 8) | buffer[idx + 1] as u16 + two_byte_combine(buffer[idx], buffer[idx + 1]) +} + +pub fn two_byte_combine(item_1: u8, item_2: u8) -> u16 +{ + ((item_1 as u16) << 8) | (item_2 as u16) } /// Take a 2 byte number and split it in to it's two 8 bit halves @@ -27,9 +35,24 @@ pub fn four_byte_split(num: u32) -> (u8, u8, u8, u8) (num & BYTEMASK_32) as u8) } +pub fn four_byte_combine(item_1: u8, item_2: u8, item_3: u8, item_4: u8) -> u32 +{ + ((item_1 as u32) << 24) | + ((item_2 as u32) << 16) | + ((item_3 as u32) << 8) | + item_4 as u32 +} + pub fn apply_split_bytes(buffer: &mut [u8], value: u16, index: usize) { let val = two_byte_split(value); buffer[index] = val.0; buffer[index + 1] = val.1; } + +pub fn push_split_bytes(buffer: &mut Vec, value: u16) +{ + let val = two_byte_split(value); + buffer.push(val.0); + buffer.push(val.1); +} diff --git a/dnstp/src/byte/tests.rs b/dnstp/src/byte/tests.rs new file mode 100644 index 0000000..a866eb0 --- /dev/null +++ b/dnstp/src/byte/tests.rs @@ -0,0 +1,21 @@ +use super::*; + +#[test] +fn combine() { + assert_eq!(769, two_byte_combine(3, 1)); +} + +#[test] +fn split() { + assert_eq!((3, 1), two_byte_split(769)); +} + +#[test] +fn back_and_forth() { + let aim = 30_000; + + let (split_1, split_2) = two_byte_split(aim); + let combined = two_byte_combine(split_1, split_2); + + assert_eq!(aim, combined); +} \ No newline at end of file diff --git a/dnstp/src/message/answer/mod.rs b/dnstp/src/message/answer/mod.rs index 1836914..a784a85 100644 --- a/dnstp/src/message/answer/mod.rs +++ b/dnstp/src/message/answer/mod.rs @@ -16,7 +16,7 @@ mod tests; use std::fmt::Debug; use std::fmt::Display; -use crate::byte::{four_byte_split, two_byte_split}; +use crate::byte::{four_byte_combine, four_byte_split, push_split_bytes, two_byte_combine}; use crate::message::question::{DNSQuestion, QClass, QType}; pub trait RData: Debug { @@ -37,16 +37,12 @@ impl ResourceRecord { pub fn to_bytes(&self) -> Vec { - let (name_1, name_2) = two_byte_split(self.name_offset | (0b11 << 14)); - let mut ret = vec![name_1, name_2]; + let mut data_bytes = self.r_data.to_bytes(); + let mut ret = Vec::with_capacity(2 + 2 + 2 + 4 + 2 + data_bytes.len()); - let type_split = two_byte_split(self.answer_type as u16); - ret.push(type_split.0); - ret.push(type_split.1); - - let class_split = two_byte_split(self.class as u16); - ret.push(class_split.0); - ret.push(class_split.1); + push_split_bytes(&mut ret, self.name_offset | (0b11 << 14)); + push_split_bytes(&mut ret, self.answer_type as u16); + push_split_bytes(&mut ret, self.class as u16); let (ttl_1, ttl_2, ttl_3, ttl_4) = four_byte_split(self.ttl); ret.push(ttl_1); @@ -54,11 +50,9 @@ impl ResourceRecord { ret.push(ttl_3); ret.push(ttl_4); - let rd_length_split = two_byte_split(self.rd_length); - ret.push(rd_length_split.0); - ret.push(rd_length_split.1); + push_split_bytes(&mut ret, self.rd_length); - ret.append(&mut self.r_data.to_bytes()); + ret.append(&mut data_bytes); return ret } @@ -91,11 +85,160 @@ pub fn records_to_bytes(answers: &Vec) -> Vec #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] pub enum RecordParseError { ShortLength(usize), - QTypeParse(u8), - QClassParse(u8) + QTypeParse(u16), + QClassParse(u16) } -pub fn answers_from_bytes(bytes: Vec, total_answers: u16) -> Result<(i32, Vec), RecordParseError> +pub fn records_from_bytes(bytes: Vec, total_answers: u16) -> Result<(Vec, Vec), RecordParseError> { - Ok((0, vec![])) + let mut ret = Vec::with_capacity(total_answers as usize); + let mut remaining = vec![]; + + let mut name_offset = (None, None); + let mut qtype = (None, None); + let mut qclass = (None, None); + let mut ttl = (None, None, None, None); + let mut data_length = (None, None); + let mut data_length_combined = None; + let mut data_length_remaining: u16 = 0; + let mut data = Vec::with_capacity(8); + + for byte in bytes { + if ret.len() == total_answers as usize { + remaining.push(byte); + continue; + } + + if data_length_remaining != 0 || data_length_combined == Some(0) { + + if data_length_remaining != 0 { + data.push(byte); + data_length_remaining -= 1; + } + + if data_length_remaining == 0 { + + match (two_byte_combine(qtype.0.unwrap(), qtype.1.unwrap()).try_into(), + two_byte_combine(qclass.0.unwrap(), qclass.1.unwrap()).try_into()) + { + (Ok(qtype_formed), Ok(qclass_formed)) => { + ret.push(ResourceRecord { + name_offset: two_byte_combine(name_offset.0.unwrap(), name_offset.1.unwrap()) & 0b11111111111111, + answer_type: qtype_formed, + class: qclass_formed, + ttl: four_byte_combine(ttl.0.unwrap(), ttl.1.unwrap(), ttl.2.unwrap(), ttl.3.unwrap()), + rd_length: data_length_combined.unwrap(), + r_data: Box::from(RawRData::from(data.clone())) + }); + + name_offset = (None, None); + qtype = (None, None); + qclass = (None, None); + ttl = (None, None, None, None); + data_length = (None, None); + data_length_combined = None; + data.clear(); + + + } + (Err(qtype_e), _) => { + return Err(RecordParseError::QTypeParse(qtype_e)); + } + (_, Err(qclass_e)) => { + return Err(RecordParseError::QClassParse(qclass_e)); + } + } + } + } + else { + match (name_offset, qtype, qclass, ttl, data_length) { + ((None, _), // NAME OFFSET + _, _, _, _) => { + name_offset.0 = Some(byte); + } + ((Some(_), None), // NAME OFFSET + _, _, _, _) => { + name_offset.1 = Some(byte); + } + ((Some(_), Some(_)), // QTYPE + (None, _), + _, _, _) => { + qtype.0 = Some(byte); + } + ((Some(_), Some(_)), // QTYPE + (Some(_), None), + _, _, _) => { + qtype.1 = Some(byte); + } + ((Some(_), Some(_)), // QCLASS + (Some(_), Some(_)), + (None, _), + _, _) => { + qclass.0 = Some(byte); + } + ((Some(_), Some(_)), // QCLASS + (Some(_), Some(_)), + (Some(_), None), + _, _) => { + qclass.1 = Some(byte); + } + ((Some(_), Some(_)), // TTL + (Some(_), Some(_)), + (Some(_), Some(_)), + (None, _, _, _), + _) => { + ttl.0 = Some(byte); + } + ((Some(_), Some(_)), // TTL + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), None, _, _), + _) => { + ttl.1 = Some(byte); + } + ((Some(_), Some(_)), // TTL + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_), None, _), + _) => { + ttl.2 = Some(byte); + } + ((Some(_), Some(_)), // TTL + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_), Some(_), None), + _) => { + ttl.3 = Some(byte); + } + ((Some(_), Some(_)), // DATA LENGTH + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_), Some(_), Some(_)), + (None, _)) => { + data_length.0 = Some(byte); + } + ((Some(_), Some(_)), // DATA LENGTH + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_), Some(_), Some(_)), + (Some(data_length_1), None)) => { + data_length.1 = Some(byte); + + data_length_combined = Some(two_byte_combine(data_length_1, byte)); + data_length_remaining = data_length_combined.unwrap(); + } + ((Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_)), + (Some(_), Some(_), Some(_), Some(_)), + (Some(_), Some(_))) => { + + + } + } + } + + } + + Ok((ret, remaining)) } \ No newline at end of file diff --git a/dnstp/src/message/answer/tests.rs b/dnstp/src/message/answer/tests.rs index 27f6daf..978ef48 100644 --- a/dnstp/src/message/answer/tests.rs +++ b/dnstp/src/message/answer/tests.rs @@ -3,7 +3,6 @@ use crate::message::question::{DNSQuestion, QClass, QType, questions_from_bytes} use super::*; #[test] -#[ignore] fn one_answer_back_and_forth() { let q = ResourceRecord { // name_offset: "google.com".to_string(), @@ -11,18 +10,62 @@ fn one_answer_back_and_forth() { answer_type: QType::A, class: QClass::Internet, ttl: 0, - rd_length: 0, - r_data: Box::from(RawRData::from(vec![])) + rd_length: 1, + r_data: Box::from(RawRData::from(vec![1])) }; let mut q_bytes = q.to_bytes(); q_bytes.append(&mut vec![0, 0, 0, 0, 0, 0]); - let (q_read, q_reconstructed) = answers_from_bytes(q_bytes, 0).unwrap(); + let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 1).unwrap(); assert_eq!(q.name_offset, q_reconstructed[0].name_offset); assert_eq!(q.answer_type, q_reconstructed[0].answer_type); assert_eq!(q.class, q_reconstructed[0].class); assert_eq!(q.ttl, q_reconstructed[0].ttl); assert_eq!(q.rd_length, q_reconstructed[0].rd_length); + assert_eq!(q.r_data.to_bytes(), q_reconstructed[0].r_data.to_bytes()); +} + +#[test] +fn two_answers_back_and_forth() { + let q = ResourceRecord { + // name_offset: "google.com".to_string(), + name_offset: 12, + answer_type: QType::A, + class: QClass::Internet, + ttl: 0, + rd_length: 1, + r_data: Box::from(RawRData::from(vec![1])) + }; + + let q_2 = ResourceRecord { + // name_offset: "google.com".to_string(), + name_offset: 12, + answer_type: QType::AAAA, + class: QClass::Internet, + ttl: 0, + rd_length: 3, + r_data: Box::from(RawRData::from(vec![1, 2, 3])) + }; + + let mut q_bytes = q.to_bytes(); + q_bytes.append(&mut q_2.to_bytes()); + q_bytes.append(&mut vec![0, 0, 0, 0, 0, 0]); + + let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 2).unwrap(); + + assert_eq!(q.name_offset, q_reconstructed[0].name_offset); + assert_eq!(q.answer_type, q_reconstructed[0].answer_type); + assert_eq!(q.class, q_reconstructed[0].class); + assert_eq!(q.ttl, q_reconstructed[0].ttl); + assert_eq!(q.rd_length, q_reconstructed[0].rd_length); + assert_eq!(q.r_data.to_bytes(), q_reconstructed[0].r_data.to_bytes()); + + assert_eq!(q_2.name_offset, q_reconstructed[1].name_offset); + assert_eq!(q_2.answer_type, q_reconstructed[1].answer_type); + assert_eq!(q_2.class, q_reconstructed[1].class); + assert_eq!(q_2.ttl, q_reconstructed[1].ttl); + assert_eq!(q_2.rd_length, q_reconstructed[1].rd_length); + assert_eq!(q_2.r_data.to_bytes(), q_reconstructed[1].r_data.to_bytes()); } \ No newline at end of file diff --git a/dnstp/src/message/mod.rs b/dnstp/src/message/mod.rs index d916ef7..20b5af7 100644 --- a/dnstp/src/message/mod.rs +++ b/dnstp/src/message/mod.rs @@ -6,7 +6,7 @@ pub mod answer; pub mod response; pub use question::{DNSQuestion, QClass, QType, QuestionParseError, questions_to_bytes, questions_from_bytes}; -pub use answer::{ResourceRecord, RawRData, RData, ARdata, AAAARdata, TXTRdata, RecordParseError, records_to_bytes, answers_from_bytes}; +pub use answer::{ResourceRecord, RawRData, RData, ARdata, AAAARdata, TXTRdata, RecordParseError, records_to_bytes, records_from_bytes}; pub use header::{DNSHeader, Direction, Opcode, ResponseCode, HEADER_SIZE}; pub use request::DNSRequest; pub use response::DNSResponse; \ No newline at end of file diff --git a/dnstp/src/message/question/mod.rs b/dnstp/src/message/question/mod.rs index d5d01a3..ba6bbf9 100644 --- a/dnstp/src/message/question/mod.rs +++ b/dnstp/src/message/question/mod.rs @@ -2,7 +2,7 @@ mod tests; use urlencoding::decode; -use crate::byte::{two_byte_extraction, two_byte_split}; +use crate::byte::{push_split_bytes, two_byte_combine, two_byte_extraction, two_byte_split}; use crate::string::encode_domain_name; #[repr(u16)] @@ -20,7 +20,8 @@ pub enum QType { TXT = 16, RP = 17, AAAA = 28, - SRV = 33 + SRV = 33, + ANY = 255, } impl TryFrom for QType { @@ -41,6 +42,7 @@ impl TryFrom for QType { x if x == QType::RP as u16 => Ok(QType::RP), x if x == QType::AAAA as u16 => Ok(QType::AAAA), x if x == QType::SRV as u16 => Ok(QType::SRV), + x if x == QType::ANY as u16 => Ok(QType::ANY), _ => Err(v), } } @@ -88,15 +90,8 @@ impl DNSQuestion { { let mut ret = encode_domain_name(&self.qname); - let (qtype_1, qtype_2) = two_byte_split(self.qtype as u16); - - ret.push(qtype_1); - ret.push(qtype_2); - - let (qclass_1, qclass_2) = two_byte_split(self.qclass as u16); - - ret.push(qclass_1); - ret.push(qclass_2); + push_split_bytes(&mut ret, self.qtype as u16); + push_split_bytes(&mut ret, self.qclass as u16); ret } @@ -121,7 +116,7 @@ pub enum QuestionParseError { QClassParse(u16) } -pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32, Vec), QuestionParseError> +pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(Vec, Vec), QuestionParseError> { if bytes.len() < 4 { @@ -129,6 +124,8 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32 } let mut questions: Vec = Vec::with_capacity(total_questions as usize); + let mut remaining = vec![]; + let mut current_query: Vec = Vec::with_capacity(10); let mut current_length: Option = None; @@ -137,10 +134,12 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32 let mut current_qclass: (Option, Option) = (None, None); let mut trailers_reached = false; - let mut byte_counter = 0; - for byte in bytes { - byte_counter += 1; + if questions.len() == total_questions as usize { + remaining.push(byte); + continue; + } + match current_length { None => { // next question, init lengths current_length = Some(byte); @@ -170,8 +169,8 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32 current_qclass.0 = Some(byte); } ((Some(qtype_1), Some(qtype_2)), (Some(qclass_1), None)) => { - match (two_byte_extraction(&[qtype_1, qtype_2], 0).try_into(), - two_byte_extraction(&[qclass_1, byte], 0).try_into()) { + match (two_byte_combine(qtype_1, qtype_2).try_into(), + two_byte_combine(qclass_1, byte).try_into()) { (Ok(qtype), Ok(qclass)) => { questions.push(DNSQuestion { qname: decode(String::from_utf8(current_query.clone()).unwrap().as_str()).unwrap().to_string(), @@ -179,10 +178,6 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32 qclass }); - if questions.len() == total_questions as usize { - break - } - current_length = None; remaining_length = byte; current_query.clear(); @@ -209,5 +204,5 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result<(i32 } } - Ok((byte_counter, questions)) + Ok((questions, remaining)) } \ No newline at end of file diff --git a/dnstp/src/message/question/tests.rs b/dnstp/src/message/question/tests.rs index 424c46e..702cff2 100644 --- a/dnstp/src/message/question/tests.rs +++ b/dnstp/src/message/question/tests.rs @@ -11,7 +11,7 @@ fn one_question_back_and_forth() { let mut q_bytes = q.to_bytes(); q_bytes.append(&mut vec![0, 0, 0, 0, 0, 0]); - let (q_read, q_reconstructed) = questions_from_bytes(q_bytes, 1).unwrap(); + let (q_reconstructed, q_remaining) = questions_from_bytes(q_bytes, 1).unwrap(); assert_eq!(q.qname, q_reconstructed[0].qname); assert_eq!(q.qclass, q_reconstructed[0].qclass); @@ -37,7 +37,7 @@ fn two_questions_back_and_forth() { q_bytes.append(&mut q2_bytes); - let (q_read, q_reconstructed) = questions_from_bytes(q_bytes, 2).unwrap(); + let (q_reconstructed, q_remaining) = questions_from_bytes(q_bytes, 2).unwrap(); assert_eq!(q.qname, q_reconstructed[0].qname); assert_eq!(q.qclass, q_reconstructed[0].qclass); @@ -75,7 +75,7 @@ fn three_questions_back_and_forth() { q_bytes.append(&mut q2_bytes); q_bytes.append(&mut q3_bytes); - let (q_read, q_reconstructed) = questions_from_bytes(q_bytes, 3).unwrap(); + let (q_reconstructed, q_remaining) = questions_from_bytes(q_bytes, 3).unwrap(); assert_eq!(q.qname, q_reconstructed[0].qname); assert_eq!(q.qclass, q_reconstructed[0].qclass); diff --git a/dnstp/src/message/request.rs b/dnstp/src/message/request.rs index 65c1eb8..259d4c2 100644 --- a/dnstp/src/message/request.rs +++ b/dnstp/src/message/request.rs @@ -1,11 +1,13 @@ use std::net::SocketAddr; -use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, Opcode, ResponseCode, QType, QClass}; +use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, Opcode, ResponseCode, QType, QClass, ResourceRecord}; -#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)] +#[derive(Debug)] pub struct DNSRequest { pub header: DNSHeader, pub questions: Vec, - pub additional_records: Vec, + pub answer_records: Vec, + pub authority_records: Vec, + pub additional_records: Vec, pub peer: SocketAddr } @@ -32,6 +34,8 @@ impl DNSRequest { qclass: QClass::Internet } ], + answer_records: vec![], + authority_records: vec![], additional_records: vec![], peer } @@ -50,6 +54,8 @@ impl DNSRequest { qtype: QType::A }) .collect(), + answer_records: vec![], + authority_records: vec![], additional_records: vec![], peer } diff --git a/dnstp/src/processor/request.rs b/dnstp/src/processor/request.rs index 55a173a..b3e7df1 100644 --- a/dnstp/src/processor/request.rs +++ b/dnstp/src/processor/request.rs @@ -5,7 +5,7 @@ use std::thread; use log::{error, info}; use crate::config::DomainConfig; -use crate::message::{QuestionParseError, DNSResponse}; +use crate::message::{QuestionParseError, DNSResponse, RecordParseError}; use crate::net::{NetworkMessage, NetworkMessagePtr}; use crate::request_parser::{HeaderParseError, parse_request, RequestParseError}; @@ -43,7 +43,14 @@ impl RequestProcesor { if r.questions.iter().any(|q| q.qname.ends_with(&base_domain_equality)) { + let mut response = DNSResponse::a_from_request(&r, |q| Ipv4Addr::from([127, 0, 0, 1])); + sending_channel.send(Box::from( + NetworkMessage { + buffer: Box::from(response.to_bytes()), + peer: response.peer + } + )); } else { let mut response = DNSResponse::a_from_request(&r, |q| Ipv4Addr::from([127, 0, 0, 1])); @@ -81,6 +88,22 @@ impl RequestProcesor { } } } + RequestParseError::RecordParse(rp) => { + match rp { + RecordParseError::ShortLength(sl) => { + error!("[{}] failed to parse records of received message, too short: [{} bytes]", peer, sl); + } + RecordParseError::QTypeParse(te) => { + error!("[{}] failed to parse records of received message, qtype error: [{}]", peer, te); + } + RecordParseError::QClassParse(ce) => { + error!("[{}] failed to parse records of received message, qclass error: [{}]", peer, ce); + } + } + } + RequestParseError::RecordCount(expected, actual) => { + error!("[{}] failed to parse records of received message, record count mismatch: [Expected:{}] [Actual:{}]", peer, expected, actual); + } } } } diff --git a/dnstp/src/processor/response.rs b/dnstp/src/processor/response.rs index a66a321..ee6ad2c 100644 --- a/dnstp/src/processor/response.rs +++ b/dnstp/src/processor/response.rs @@ -1,10 +1,13 @@ +use std::net::Ipv4Addr; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender}; use std::thread; -use log::info; +use log::{error, info}; use std::str; -use crate::message::DNSResponse; +use crate::message::{DNSResponse, QuestionParseError, RecordParseError}; +use crate::net::NetworkMessage; use crate::net::raw_request::NetworkMessagePtr; +use crate::request_parser::{HeaderParseError, parse_request, RequestParseError}; pub struct ResponseProcesor { message_channel: Option> @@ -26,12 +29,56 @@ impl ResponseProcesor { for mut m in rx { - info!("processing response"); + let peer = m.peer.clone(); - // match sending_channel.send(m) { - // Ok(_) => {} - // Err(_) => {} - // } + match parse_request(*m) { + Ok(r) => { + info!("received dns message: {:?}", r); + } + Err(e) => { + match e { + RequestParseError::HeaderParse(he) => { + match he { + HeaderParseError::OpcodeParse(oe) => { + error!("[{}] failed to parse opcode from received message: [{}]", peer, oe); + } + HeaderParseError::ResponseCodeParse(rce) => { + error!("[{}] failed to parse response code error from received message: [{}]", peer, rce); + } + } + } + RequestParseError::QuesionsParse(qe) => { + match qe { + QuestionParseError::ShortLength(sl) => { + error!("[{}] failed to parse questions of received message, too short: [{} bytes]", peer, sl); + } + QuestionParseError::QTypeParse(te) => { + error!("[{}] failed to parse questions of received message, qtype error: [{}]", peer, te); + } + QuestionParseError::QClassParse(ce) => { + error!("[{}] failed to parse questions of received message, qclass error: [{}]", peer, ce); + } + } + } + RequestParseError::RecordParse(rp) => { + match rp { + RecordParseError::ShortLength(sl) => { + error!("[{}] failed to parse records of received message, too short: [{} bytes]", peer, sl); + } + RecordParseError::QTypeParse(te) => { + error!("[{}] failed to parse records of received message, qtype error: [{}]", peer, te); + } + RecordParseError::QClassParse(ce) => { + error!("[{}] failed to parse records of received message, qclass error: [{}]", peer, ce); + } + } + } + RequestParseError::RecordCount(expected, actual) => { + error!("[{}] failed to parse records of received message, record count mismatch: [Expected:{}] [Actual:{}]", peer, expected, actual); + } + } + } + } } info!("message processing thread finishing") diff --git a/dnstp/src/request_parser.rs b/dnstp/src/request_parser.rs index ab602fa..f2126c9 100644 --- a/dnstp/src/request_parser.rs +++ b/dnstp/src/request_parser.rs @@ -1,5 +1,5 @@ use crate::byte; -use crate::message::{DNSRequest, Direction, DNSHeader, Opcode, ResponseCode, QuestionParseError, questions_from_bytes}; +use crate::message::{DNSRequest, Direction, DNSHeader, Opcode, ResponseCode, QuestionParseError, questions_from_bytes, records_from_bytes, RecordParseError, ResourceRecord}; use crate::net::NetworkMessage; use crate::request_parser::RequestParseError::{HeaderParse, QuesionsParse}; @@ -78,6 +78,8 @@ pub fn parse_header(header: &[u8; 12]) -> Result pub enum RequestParseError { HeaderParse(HeaderParseError), QuesionsParse(QuestionParseError), + RecordParse(RecordParseError), + RecordCount(u16, usize), } pub fn parse_request(msg: NetworkMessage) -> Result @@ -88,32 +90,52 @@ pub fn parse_request(msg: NetworkMessage) -> Result { let mut trimmed = msg.buffer.to_vec(); trimmed.drain(0 .. 12); - let buffer_size = trimmed.len(); + match questions_from_bytes(trimmed, header.question_count) { - Ok((bytes_read, questions)) => { + Ok((questions, remaining)) => { + if remaining.len() > 0 { - if buffer_size > bytes_read as usize { - Ok(DNSRequest { - header, - questions, - peer: msg.peer, - additional_records: vec![] - }) + let total_records = header.answer_record_count + header.authority_record_count + header.additional_record_count; + + match records_from_bytes(remaining, total_records){ + Ok((mut answers, _)) => { + + if answers.len() != total_records as usize { + return Err(RequestParseError::RecordCount(total_records, answers.len())); + } + else { + let answer_records = answers.drain(0 .. (header.answer_record_count as usize)).collect(); + let authority_records = answers.drain(0 .. (header.authority_record_count as usize)).collect(); + + return Ok(DNSRequest { + header, + questions, + peer: msg.peer, + answer_records, + authority_records, + additional_records: answers + }); + } + } + Err(e) => return Err(RequestParseError::RecordParse(e)) + } } else { - Ok(DNSRequest { + return Ok(DNSRequest { header, questions, peer: msg.peer, + answer_records: vec![], + authority_records: vec![], additional_records: vec![] - }) + }); } } - Err(e) => Err(QuesionsParse(e)) + Err(e) => return Err(QuesionsParse(e)) } }, - Err(e) => Err(HeaderParse(e)) + Err(e) => return Err(HeaderParse(e)) } }