diff --git a/Cargo.lock b/Cargo.lock index f1d1f2a..c42ea7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.4.18" @@ -122,6 +128,7 @@ dependencies = [ "clap", "dnstplib", "log", + "rand", "simplelog", ] @@ -133,6 +140,17 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.4.1" @@ -172,6 +190,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.76" @@ -190,6 +214,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "serde" version = "1.0.196" @@ -296,6 +350,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/README.md b/README.md index 89d9cad..994c0c6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # dnstp -Transmitting files over dns piece by piece. \ No newline at end of file +[![Build Binaries](https://github.com/Sarsoo/dnstp/actions/workflows/build.yml/badge.svg)](https://github.com/Sarsoo/dnstp/actions/workflows/build.yml) + +Transmitting files over dns piece by piece. Should be a pretty subtle way of sending files. + +I remember I was listening to, I think, [Security This Week with Carl Franklin](https://securitythisweek.com/). One of the hosts mentioned doing data exfiltration from a tight network by breaking the file down and sending it over DNS. I wanted to see how this could work. [Read More](https://www.securityweek.com/multigrain-pos-malware-exfiltrates-card-data-over-dns/). + +I also wanted to play with a big rust project for standard targets with threading. Although I had a lot of fun with my browser-based checkers game, [Draught](https://draught.sarsoo.xyz), working against WASM has some restrictions. \ No newline at end of file diff --git a/dnstp-client/Cargo.toml b/dnstp-client/Cargo.toml index 818f3ae..08fcb46 100644 --- a/dnstp-client/Cargo.toml +++ b/dnstp-client/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" dnstplib = { path = "../dnstp" } clap = { version = "4.4.18", features = ["derive"] } log = "0.4.20" -simplelog = "0.12.1" \ No newline at end of file +simplelog = "0.12.1" +rand = "0.8.5" \ No newline at end of file diff --git a/dnstp-client/src/main.rs b/dnstp-client/src/main.rs index 9f3ecc0..c311adf 100644 --- a/dnstp-client/src/main.rs +++ b/dnstp-client/src/main.rs @@ -4,10 +4,14 @@ use std::thread; use std::time::Duration; use clap::Parser; use log::{info, LevelFilter}; +use rand::RngCore; use simplelog::*; -use dnstplib::dns_socket::DNSSocket; -use dnstplib::raw_request::NetworkMessage; -use dnstplib::response_processor::ResponseProcesor; +use dnstplib::message::header::{Direction, DNSHeader, Opcode, ResponseCode}; +use dnstplib::message::question::{DNSQuestion, QClass, QType}; +use dnstplib::message::request::DNSRequest; +use dnstplib::net::socket::DNSSocket; +use dnstplib::net::raw_request::NetworkMessage; +use dnstplib::processor::ResponseProcesor; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -40,18 +44,41 @@ fn main() { socket.run_rx(processor.get_message_channel().expect("couldn't get message processing channel")); + let mut rng = rand::thread_rng(); loop { info!("sending..."); - let mut send_buf = [0; 512]; - send_buf[0] = 'a' as u8; - send_buf[1] = 'b' as u8; - send_buf[2] = 'c' as u8; - send_buf[3] = 'd' as u8; + let message = DNSRequest { + header: DNSHeader { + id: rng.next_u32() as u16, + direction: Direction::Request, + opcode: Opcode::Query, + authoritative: false, + truncation: false, + recursion_desired: true, + recursion_available: false, + valid_zeroes: true, + response: ResponseCode::NoError, + question_count: 1, + answer_record_count: 0, + authority_record_count: 0, + additional_record_count: 0 + }, + questions: vec![ + DNSQuestion { + qname: "duck.com".to_string(), + qtype: QType::A, + qclass: QClass::Internet + } + ], + peer: address + }; + + let bytes = message.to_bytes(); tx_channel.send(Box::from(NetworkMessage { - buffer: Box::from(send_buf), + buffer: Box::from(bytes), peer: args.address.parse().unwrap() })); diff --git a/dnstp-server/src/main.rs b/dnstp-server/src/main.rs index 70a3838..1f3dd28 100644 --- a/dnstp-server/src/main.rs +++ b/dnstp-server/src/main.rs @@ -6,8 +6,8 @@ use simplelog::*; use std::fs::File; use std::net::SocketAddr; -use dnstplib::dns_socket::DNSSocket; -use dnstplib::request_processor::RequestProcesor; +use dnstplib::net::socket::DNSSocket; +use dnstplib::processor::RequestProcesor; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] diff --git a/dnstp/src/byte.rs b/dnstp/src/byte.rs new file mode 100644 index 0000000..2db7700 --- /dev/null +++ b/dnstp/src/byte.rs @@ -0,0 +1,29 @@ + +const BYTEMASK_32: u32 = 0b11111111; +const BYTEMASK_16: u16 = 0b11111111; + +pub fn two_byte_extraction(buffer: &[u8], idx: usize) -> u16 +{ + ((buffer[idx] as u16) << 8) | buffer[idx + 1] as u16 +} + +pub fn two_byte_split(num: u16) -> (u8, u8) +{ + ((num >> 8) as u8, + (num & BYTEMASK_16) as u8) +} + +pub fn four_byte_split(num: u32) -> (u8, u8, u8, u8) +{ + ((num >> 24) as u8, + ((num >> 16) & BYTEMASK_32) as u8, + ((num >> 8) & BYTEMASK_32) as u8, + (num & BYTEMASK_32) as u8) +} + +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; +} diff --git a/dnstp/src/dns_request.rs b/dnstp/src/dns_request.rs deleted file mode 100644 index 8d5fda8..0000000 --- a/dnstp/src/dns_request.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::net::SocketAddr; -use crate::dns_header::DNSHeader; -use crate::dns_question::DNSQuestion; - -#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] -pub struct DNSRequest { - pub header: DNSHeader, - pub questions: Vec, - pub peer: SocketAddr -} \ No newline at end of file diff --git a/dnstp/src/lib.rs b/dnstp/src/lib.rs index 9e0fd87..ac72d94 100644 --- a/dnstp/src/lib.rs +++ b/dnstp/src/lib.rs @@ -1,8 +1,7 @@ -pub mod dns_socket; pub mod request_parser; -pub mod dns_header; -pub mod request_processor; -pub mod response_processor; -pub mod raw_request; -pub mod dns_question; -pub mod dns_request; \ No newline at end of file + +mod byte; +pub mod processor; +pub mod message; +pub mod net; +mod string; \ No newline at end of file diff --git a/dnstp/src/message/answer.rs b/dnstp/src/message/answer.rs new file mode 100644 index 0000000..2e2a2b9 --- /dev/null +++ b/dnstp/src/message/answer.rs @@ -0,0 +1,43 @@ +use crate::byte::{four_byte_split, two_byte_split}; +use crate::message::question::{QClass, QType}; +use crate::string::encode_domain_name; + +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub struct DNSAnswer { + pub name: String, + pub answer_type: QType, + pub class: QClass, + pub ttl: u32, + pub rd_length: u16, + pub r_data: Vec +} + +impl DNSAnswer { + + pub fn to_bytes(& self) -> Vec + { + let mut ret = encode_domain_name(&self.name); + + 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); + + let (ttl_1, ttl_2, ttl_3, ttl_4) = four_byte_split(self.ttl); + ret.push(ttl_1); + ret.push(ttl_2); + 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); + + ret.append(&mut self.r_data.clone()); + + return ret + } +} \ No newline at end of file diff --git a/dnstp/src/dns_header.rs b/dnstp/src/message/header.rs similarity index 60% rename from dnstp/src/dns_header.rs rename to dnstp/src/message/header.rs index 25848a5..230fce5 100644 --- a/dnstp/src/dns_header.rs +++ b/dnstp/src/message/header.rs @@ -1,4 +1,6 @@ use std::convert::TryFrom; +use crate::byte::apply_split_bytes; +use crate::message::header::Direction::Response; pub const HEADER_SIZE: usize = 12; @@ -17,7 +19,7 @@ pub enum Opcode { } impl TryFrom for Opcode { - type Error = (); + type Error = u16; fn try_from(v: u16) -> Result { match v { @@ -25,7 +27,7 @@ impl TryFrom for Opcode { x if x == Opcode::RQuery as u16 => Ok(Opcode::RQuery), x if x == Opcode::Status as u16 => Ok(Opcode::Status), x if x == Opcode::Reserved as u16 => Ok(Opcode::Reserved), - _ => Err(()), + _ => Err(v), } } } @@ -46,7 +48,7 @@ pub enum ResponseCode { } impl TryFrom for ResponseCode { - type Error = (); + type Error = u16; fn try_from(v: u16) -> Result { match v { @@ -61,7 +63,7 @@ impl TryFrom for ResponseCode { x if x == ResponseCode::NXRRSet as u16 => Ok(ResponseCode::NXRRSet), x if x == ResponseCode::NotAuth as u16 => Ok(ResponseCode::NotAuth), x if x == ResponseCode::NotZone as u16 => Ok(ResponseCode::NotZone), - _ => Err(()), + _ => Err(v), } } } @@ -81,4 +83,37 @@ pub struct DNSHeader { pub answer_record_count: u16, pub authority_record_count: u16, pub additional_record_count: u16, +} + +impl DNSHeader { + pub fn to_bytes(&self) -> [u8; 12] + { + let mut header_bytes: [u8; 12] = [0; 12]; + + apply_split_bytes(&mut header_bytes, self.id, crate::request_parser::ID_START); + + let mut flags: u16 = 0; + + if self.direction == Response { + flags |= 0b1 << crate::request_parser::DIRECTION_SHIFT; + } + + flags |= (self.opcode as u16) << crate::request_parser::OPCODE_SHIFT; + + flags |= (self.authoritative as u16) << crate::request_parser::AUTHORITATIVE_SHIFT; + flags |= (self.truncation as u16) << crate::request_parser::TRUNCATION_SHIFT; + flags |= (self.recursion_desired as u16) << crate::request_parser::RECURSION_DESIRED_SHIFT; + flags |= (self.recursion_available as u16) << crate::request_parser::RECURSION_AVAILABLE_SHIFT; + + flags |= self.response as u16; + + apply_split_bytes(&mut header_bytes, flags, crate::request_parser::FLAGS_START); + + apply_split_bytes(&mut header_bytes, self.question_count, crate::request_parser::QUESTION_COUNT_START); + apply_split_bytes(&mut header_bytes, self.answer_record_count, crate::request_parser::ANSWER_RECORD_COUNT_START); + apply_split_bytes(&mut header_bytes, self.authority_record_count, crate::request_parser::AUTHORITY_RECORD_COUNT_START); + apply_split_bytes(&mut header_bytes, self.additional_record_count, crate::request_parser::ADDITIONAL_RECORD_COUNT_START); + + header_bytes + } } \ No newline at end of file diff --git a/dnstp/src/message/mod.rs b/dnstp/src/message/mod.rs new file mode 100644 index 0000000..953a9c6 --- /dev/null +++ b/dnstp/src/message/mod.rs @@ -0,0 +1,6 @@ + +pub mod header; +pub mod question; +pub mod request; +pub mod answer; +pub mod response; diff --git a/dnstp/src/dns_question.rs b/dnstp/src/message/question.rs similarity index 86% rename from dnstp/src/dns_question.rs rename to dnstp/src/message/question.rs index c8a3aa6..85a0ccf 100644 --- a/dnstp/src/dns_question.rs +++ b/dnstp/src/message/question.rs @@ -1,5 +1,6 @@ use std::ops::Sub; use urlencoding::{encode, decode}; +use crate::string::encode_domain_name; #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)] pub enum QType { @@ -19,7 +20,7 @@ pub enum QType { } impl TryFrom for QType { - type Error = (); + type Error = u8; fn try_from(v: u8) -> Result { match v { @@ -36,7 +37,7 @@ impl TryFrom for QType { x if x == QType::RP as u8 => Ok(QType::RP), x if x == QType::AAAA as u8 => Ok(QType::AAAA), x if x == QType::SRV as u8 => Ok(QType::SRV), - _ => Err(()), + _ => Err(v), } } } @@ -49,23 +50,23 @@ pub enum QClass { } impl TryFrom for QClass { - type Error = (); + type Error = u8; fn try_from(v: u8) -> Result { match v { x if x == QClass::Internet as u8 => Ok(QClass::Internet), x if x == QClass::Chaos as u8 => Ok(QClass::Chaos), x if x == QClass::Hesiod as u8 => Ok(QClass::Hesiod), - _ => Err(()), + _ => Err(v), } } } #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] pub struct DNSQuestion { - qname: String, - qtype: QType, - qclass: QClass + pub qname: String, + pub qtype: QType, + pub qclass: QClass } impl DNSQuestion { @@ -80,20 +81,7 @@ impl DNSQuestion { pub fn to_bytes(&self) -> Vec { - let mut ret: Vec = Vec::with_capacity(self.qname.len() + 2 + 3); - - for part in self.qname.split(".") - { - let encoded_string = encode(part); - let count = encoded_string.len(); - - ret.push(count as u8); - for x in encoded_string.bytes() { - ret.push(x); - }; - } - - ret.push(0); + let mut ret = encode_domain_name(&self.qname); ret.push(self.qtype as u8); ret.push(self.qclass as u8); @@ -102,11 +90,30 @@ impl DNSQuestion { } } -pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result, ()> +pub fn questions_to_bytes(questions: &Vec) -> Vec +{ + let mut ret = Vec::with_capacity(20); + + for q in questions + { + ret.append(&mut q.to_bytes()); + } + + ret +} + +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum QuestionParseError { + ShortLength(usize), + QTypeParse(u8), + QClassParse(u8) +} + +pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result, QuestionParseError> { if bytes.len() < 4 { - return Err(()); + return Err(QuestionParseError::ShortLength(bytes.len())); } let mut questions: Vec = Vec::with_capacity(total_questions as usize); @@ -150,7 +157,7 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result { questions.push(DNSQuestion { - qname: String::from_utf8(current_query.unwrap()).unwrap(), + qname: decode(String::from_utf8(current_query.unwrap()).unwrap().as_str()).unwrap().to_string(), qtype, qclass }); @@ -162,8 +169,11 @@ pub fn questions_from_bytes(bytes: Vec, total_questions: u16) -> Result { - return Err(()); + (Err(qtype_e), _) => { + return Err(QuestionParseError::QTypeParse(qtype_e)); + } + (_, Err(qclass_e)) => { + return Err(QuestionParseError::QClassParse(qclass_e)); } } } diff --git a/dnstp/src/message/request.rs b/dnstp/src/message/request.rs new file mode 100644 index 0000000..cf89344 --- /dev/null +++ b/dnstp/src/message/request.rs @@ -0,0 +1,23 @@ +use std::net::SocketAddr; +use crate::message::header::DNSHeader; +use crate::message::question::{DNSQuestion, questions_to_bytes}; + +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub struct DNSRequest { + pub header: DNSHeader, + pub questions: Vec, + pub peer: SocketAddr +} + +impl DNSRequest { + + pub fn to_bytes(& self) -> Vec + { + let mut header_bytes = self.header.to_bytes().to_vec(); + let mut body_bytes = questions_to_bytes(&self.questions); + + header_bytes.append(&mut body_bytes); + + return header_bytes + } +} \ No newline at end of file diff --git a/dnstp/src/message/response.rs b/dnstp/src/message/response.rs new file mode 100644 index 0000000..3f0dd9f --- /dev/null +++ b/dnstp/src/message/response.rs @@ -0,0 +1,25 @@ +use std::net::SocketAddr; +use crate::message::answer::DNSAnswer; +use crate::message::header::DNSHeader; +use crate::message::question::{DNSQuestion, questions_to_bytes}; + +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub struct DNSResponse { + pub header: DNSHeader, + pub questions: Vec, + pub answers: Vec, + pub peer: SocketAddr +} + +impl DNSResponse { + + pub fn to_bytes(& self) -> Vec + { + let mut header_bytes = self.header.to_bytes().to_vec(); + let mut body_bytes = questions_to_bytes(&self.questions); + + header_bytes.append(&mut body_bytes); + + return header_bytes + } +} \ No newline at end of file diff --git a/dnstp/src/net/mod.rs b/dnstp/src/net/mod.rs new file mode 100644 index 0000000..2b04e4a --- /dev/null +++ b/dnstp/src/net/mod.rs @@ -0,0 +1,6 @@ +pub mod socket; +// pub mod processor; +// +// pub mod processor::request_processor; +// pub mod processor::response_processor; +pub mod raw_request; diff --git a/dnstp/src/raw_request.rs b/dnstp/src/net/raw_request.rs similarity index 80% rename from dnstp/src/raw_request.rs rename to dnstp/src/net/raw_request.rs index 07d83a5..8a54fdf 100644 --- a/dnstp/src/raw_request.rs +++ b/dnstp/src/net/raw_request.rs @@ -3,6 +3,6 @@ use std::net::SocketAddr; pub type NetworkMessagePtr = Box; pub struct NetworkMessage { - pub buffer: Box<[u8; 512]>, + pub buffer: Box>, pub peer: SocketAddr } \ No newline at end of file diff --git a/dnstp/src/dns_socket.rs b/dnstp/src/net/socket.rs similarity index 91% rename from dnstp/src/dns_socket.rs rename to dnstp/src/net/socket.rs index b7ebc54..05899b8 100644 --- a/dnstp/src/dns_socket.rs +++ b/dnstp/src/net/socket.rs @@ -2,12 +2,10 @@ use std::net::{SocketAddr, UdpSocket}; use std::thread; use std::thread::{JoinHandle}; use log::{debug, error, info}; - -use std::str; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender, TryRecvError}; -use crate::dns_header::HEADER_SIZE; -use crate::raw_request::{NetworkMessage, NetworkMessagePtr}; +use crate::message::header::HEADER_SIZE; +use crate::net::raw_request::{NetworkMessage, NetworkMessagePtr}; pub struct DNSSocket { addresses: Vec, @@ -77,13 +75,12 @@ impl DNSSocket { Some(s) => { let mut cancelled = false; while !cancelled { - let mut buf = Box::new([0; 512]); + let mut buf = Box::new(Vec::with_capacity(512)); + buf.resize(512, 0); let res = s.recv_from(&mut (*buf)); match res { Ok((read_count, peer)) => { - // let res_str = str::from_utf8(&(*buf)).unwrap(); - // info!("received [{}] from [{}]", res_str, peer); if read_count > HEADER_SIZE { message_sender.send(Box::new(NetworkMessage { @@ -131,7 +128,6 @@ impl DNSSocket { while !cancelled { for m in &msg_rx { - info!("sending [{}] to [{}]", str::from_utf8(&(*(*m).buffer)).unwrap(), (*m).peer); if let Err(e) = s.send_to(&(*m.buffer), m.peer){ error!("error sending response to [{}], {}", m.peer, e); } diff --git a/dnstp/src/processor/mod.rs b/dnstp/src/processor/mod.rs new file mode 100644 index 0000000..ef5afd7 --- /dev/null +++ b/dnstp/src/processor/mod.rs @@ -0,0 +1,6 @@ + +pub mod request; +pub mod response; + +pub use request::RequestProcesor; +pub use response::ResponseProcesor; diff --git a/dnstp/src/processor/request.rs b/dnstp/src/processor/request.rs new file mode 100644 index 0000000..2824b18 --- /dev/null +++ b/dnstp/src/processor/request.rs @@ -0,0 +1,78 @@ +use std::sync::mpsc; +use std::sync::mpsc::{Receiver, Sender}; +use std::thread; +use log::{error, info}; +use crate::message::question::QuestionParseError; +use crate::net::raw_request::NetworkMessagePtr; +use crate::request_parser::{HeaderParseError, parse_request, RequestParseError}; + +pub struct RequestProcesor { + message_channel: Option> +} + +impl RequestProcesor { + pub fn new() -> RequestProcesor { + RequestProcesor{ + message_channel: None + } + } + + pub fn run(&mut self, sending_channel: Sender) + { + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + self.message_channel = Some(tx); + + thread::spawn(move || { + + for m in rx + { + let peer = m.peer.clone(); + + 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); + } + } + } + } + } + } + + // match sending_channel.send(m) { + // Ok(_) => {} + // Err(_) => {} + // } + } + + info!("message processing thread finishing") + }); + } + + pub fn get_message_channel(&mut self) -> Option> + { + self.message_channel.clone() + } +} \ No newline at end of file diff --git a/dnstp/src/response_processor.rs b/dnstp/src/processor/response.rs similarity index 95% rename from dnstp/src/response_processor.rs rename to dnstp/src/processor/response.rs index 5c3b56d..991b18e 100644 --- a/dnstp/src/response_processor.rs +++ b/dnstp/src/processor/response.rs @@ -3,7 +3,7 @@ use std::sync::mpsc::{Receiver, Sender}; use std::thread; use log::info; use std::str; -use crate::raw_request::NetworkMessagePtr; +use crate::net::raw_request::NetworkMessagePtr; pub struct ResponseProcesor { message_channel: Option> diff --git a/dnstp/src/request_parser.rs b/dnstp/src/request_parser.rs index b2fcd19..bd4490f 100644 --- a/dnstp/src/request_parser.rs +++ b/dnstp/src/request_parser.rs @@ -1,43 +1,40 @@ -use crate::dns_header::{Direction, DNSHeader, Opcode, ResponseCode}; -use crate::dns_header::Direction::Response; -use crate::dns_question::{DNSQuestion, questions_from_bytes}; -use crate::dns_request::DNSRequest; -use crate::raw_request::NetworkMessage; +use crate::byte; +use crate::message::header::{Direction, DNSHeader, Opcode, ResponseCode}; +use crate::message::question::{QuestionParseError, questions_from_bytes}; +use crate::message::request::DNSRequest; +use crate::net::raw_request::NetworkMessage; +use crate::request_parser::RequestParseError::{HeaderParse, QuesionsParse}; -fn two_byte_extraction(buffer: &[u8], idx: usize) -> u16 -{ - ((buffer[idx] as u16) << 8) | buffer[idx + 1] as u16 +pub const ID_START: usize = 0; +pub const FLAGS_START: usize = 2; +pub const DIRECTION_SHIFT: usize = 15; +pub const OPCODE_SHIFT: usize = 11; +pub const AUTHORITATIVE_SHIFT: usize = 10; +pub const TRUNCATION_SHIFT: usize = 9; +pub const RECURSION_DESIRED_SHIFT: usize = 8; +pub const RECURSION_AVAILABLE_SHIFT: usize = 7; +pub const ZEROES_SHIFT: usize = 4; +pub const QUESTION_COUNT_START: usize = 4; +pub const ANSWER_RECORD_COUNT_START: usize = 6; +pub const AUTHORITY_RECORD_COUNT_START: usize = 8; +pub const ADDITIONAL_RECORD_COUNT_START: usize = 10; + +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum HeaderParseError { + OpcodeParse(u16), + ResponseCodeParse(u16), } -fn two_byte_split(num: u16) -> (u8, u8) +pub fn parse_header(header: &[u8; 12]) -> Result { - ((num >> 8) as u8, (num & 0b0000000011111111) as u8) -} + let id = byte::two_byte_extraction(header, ID_START); -const ID_START: usize = 0; -const FLAGS_START: usize = 2; -const DIRECTION_SHIFT: usize = 15; -const OPCODE_SHIFT: usize = 11; -const AUTHORITATIVE_SHIFT: usize = 10; -const TRUNCATION_SHIFT: usize = 9; -const RECURSION_DESIRED_SHIFT: usize = 8; -const RECURSION_AVAILABLE_SHIFT: usize = 7; -const ZEROES_SHIFT: usize = 4; -const QUESTION_COUNT_START: usize = 4; -const ANSWER_RECORD_COUNT_START: usize = 6; -const AUTHORITY_RECORD_COUNT_START: usize = 8; -const ADDITIONAL_RECORD_COUNT_START: usize = 10; - -pub fn parse_header(header: &[u8; 12]) -> Result -{ - let id = two_byte_extraction(header, ID_START); - - let flags = two_byte_extraction(header, FLAGS_START); + let flags = byte::two_byte_extraction(header, FLAGS_START); let direction = if flags & (0b1 << DIRECTION_SHIFT) == 0 {Direction::Request} else { Direction::Response }; - let opcode: Result = ((flags & (0b1111 << OPCODE_SHIFT)) >> OPCODE_SHIFT).try_into(); + let opcode: Result = ((flags & (0b1111 << OPCODE_SHIFT)) >> OPCODE_SHIFT).try_into(); if let Err(e) = opcode { - return Err(e); + return Err(HeaderParseError::OpcodeParse(e)); } let authoritative = (flags & (0b1 << AUTHORITATIVE_SHIFT)) != 0; @@ -47,16 +44,16 @@ pub fn parse_header(header: &[u8; 12]) -> Result let zeroes = (flags & (0b111 << ZEROES_SHIFT)) == 0; - let response: Result = (flags & 0b1111).try_into(); + let response: Result = (flags & 0b1111).try_into(); if let Err(e) = response { - return Err(e); + return Err(HeaderParseError::ResponseCodeParse(e)); } - let question_count = two_byte_extraction(header, QUESTION_COUNT_START); - let answer_record_count = two_byte_extraction(header, ANSWER_RECORD_COUNT_START); - let authority_record_count = two_byte_extraction(header, AUTHORITY_RECORD_COUNT_START); - let additional_record_count = two_byte_extraction(header, ADDITIONAL_RECORD_COUNT_START); + let question_count = byte::two_byte_extraction(header, QUESTION_COUNT_START); + let answer_record_count = byte::two_byte_extraction(header, ANSWER_RECORD_COUNT_START); + let authority_record_count = byte::two_byte_extraction(header, AUTHORITY_RECORD_COUNT_START); + let additional_record_count = byte::two_byte_extraction(header, ADDITIONAL_RECORD_COUNT_START); Ok(DNSHeader { id, @@ -79,45 +76,13 @@ pub fn parse_header(header: &[u8; 12]) -> Result }) } -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; +#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] +pub enum RequestParseError { + HeaderParse(HeaderParseError), + QuesionsParse(QuestionParseError), } -pub fn parse_header_to_bytes(header: &DNSHeader) -> [u8; 12] -{ - let mut header_bytes: [u8; 12] = [0; 12]; - - apply_split_bytes(&mut header_bytes, header.id, ID_START); - - let mut flags: u16 = 0; - - if header.direction == Response { - flags |= 0b1 << DIRECTION_SHIFT; - } - - flags |= (header.opcode as u16) << OPCODE_SHIFT; - - flags |= (header.authoritative as u16) << AUTHORITATIVE_SHIFT; - flags |= (header.truncation as u16) << TRUNCATION_SHIFT; - flags |= (header.recursion_desired as u16) << RECURSION_DESIRED_SHIFT; - flags |= (header.recursion_available as u16) << RECURSION_AVAILABLE_SHIFT; - - flags |= header.response as u16; - - apply_split_bytes(&mut header_bytes, flags, FLAGS_START); - - apply_split_bytes(&mut header_bytes, header.question_count, QUESTION_COUNT_START); - apply_split_bytes(&mut header_bytes, header.answer_record_count, ANSWER_RECORD_COUNT_START); - apply_split_bytes(&mut header_bytes, header.authority_record_count, AUTHORITY_RECORD_COUNT_START); - apply_split_bytes(&mut header_bytes, header.additional_record_count, ADDITIONAL_RECORD_COUNT_START); - - header_bytes -} - -pub fn parse_request(msg: NetworkMessage) -> Result +pub fn parse_request(msg: NetworkMessage) -> Result { let header = parse_header(msg.buffer[0..12].try_into().unwrap()); @@ -134,15 +99,16 @@ pub fn parse_request(msg: NetworkMessage) -> Result peer: msg.peer }) } - Err(_) => Err(()) + Err(e) => Err(QuesionsParse(e)) } }, - Err(_) => Err(()) + Err(e) => Err(HeaderParse(e)) } } #[cfg(test)] mod tests { + use crate::byte::{two_byte_extraction, two_byte_split}; use super::*; #[test] @@ -188,7 +154,7 @@ mod tests { additional_record_count: 4 }; - let parsed_bytes = parse_header_to_bytes(&header); + let parsed_bytes = header.to_bytes(); let header_again = parse_header(&parsed_bytes).unwrap(); diff --git a/dnstp/src/request_processor.rs b/dnstp/src/request_processor.rs deleted file mode 100644 index 7c05235..0000000 --- a/dnstp/src/request_processor.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::sync::mpsc; -use std::sync::mpsc::{Receiver, Sender}; -use std::thread; -use log::{error, info}; -use std::str; -use crate::dns_request::DNSRequest; -use crate::raw_request::NetworkMessagePtr; -use crate::request_parser::parse_request; - -pub struct RequestProcesor { - message_channel: Option> -} - -impl RequestProcesor { - pub fn new() -> RequestProcesor { - RequestProcesor{ - message_channel: None - } - } - - pub fn run(&mut self, sending_channel: Sender) - { - let (tx, rx): (Sender, Receiver) = mpsc::channel(); - self.message_channel = Some(tx); - - thread::spawn(move || { - - for mut m in rx - { - // info!("processing: {}", str::from_utf8(&(*(*m).buffer)).unwrap()); - - let request = parse_request(*m); - - match request { - Ok(r) => { - info!("received dns message: {:?}", r); - } - Err(_) => { - error!("failed to parse message"); - } - } - - // match sending_channel.send(m) { - // Ok(_) => {} - // Err(_) => {} - // } - } - - info!("message processing thread finishing") - }); - } - - pub fn get_message_channel(&mut self) -> Option> - { - self.message_channel.clone() - } -} \ No newline at end of file diff --git a/dnstp/src/string.rs b/dnstp/src/string.rs new file mode 100644 index 0000000..011e43d --- /dev/null +++ b/dnstp/src/string.rs @@ -0,0 +1,21 @@ +use urlencoding::encode; + +pub fn encode_domain_name(name: &String) -> Vec +{ + let mut ret: Vec = Vec::with_capacity(name.len() + 3); + + for part in name.split(".") + { + let encoded_string = encode(part); + let count = encoded_string.len(); + + ret.push(count as u8); + for x in encoded_string.bytes() { + ret.push(x); + }; + } + + ret.push(0); + + ret +} \ No newline at end of file