Compare commits
2 Commits
1f6593263a
...
bc367e7814
Author | SHA1 | Date | |
---|---|---|---|
bc367e7814 | |||
8e1d070f85 |
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@ -31,4 +31,26 @@ jobs:
|
|||||||
- name: Cargo Test
|
- name: Cargo Test
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
|
doc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ build ] # for ignoring bad builds
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Build Docs
|
||||||
|
run: cargo doc --no-deps --document-private-items
|
||||||
|
|
||||||
|
- name: Deploy To Pages
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: ./target/doc
|
60
Cargo.lock
generated
60
Cargo.lock
generated
@ -50,6 +50,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.18"
|
version = "4.4.18"
|
||||||
@ -122,6 +128,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"dnstplib",
|
"dnstplib",
|
||||||
"log",
|
"log",
|
||||||
|
"rand",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -133,6 +140,17 @@ dependencies = [
|
|||||||
"urlencoding",
|
"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]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -172,6 +190,12 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.76"
|
version = "1.0.76"
|
||||||
@ -190,6 +214,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.196"
|
version = "1.0.196"
|
||||||
@ -296,6 +350,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
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]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
# dnstp
|
# dnstp
|
||||||
|
|
||||||
Transmitting files over dns piece by piece.
|
[![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.
|
@ -9,4 +9,5 @@ edition = "2021"
|
|||||||
dnstplib = { path = "../dnstp" }
|
dnstplib = { path = "../dnstp" }
|
||||||
clap = { version = "4.4.18", features = ["derive"] }
|
clap = { version = "4.4.18", features = ["derive"] }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
simplelog = "0.12.1"
|
simplelog = "0.12.1"
|
||||||
|
rand = "0.8.5"
|
@ -4,10 +4,14 @@ use std::thread;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{info, LevelFilter};
|
use log::{info, LevelFilter};
|
||||||
|
use rand::RngCore;
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
use dnstplib::dns_socket::DNSSocket;
|
use dnstplib::message::header::{Direction, DNSHeader, Opcode, ResponseCode};
|
||||||
use dnstplib::raw_request::NetworkMessage;
|
use dnstplib::message::question::{DNSQuestion, QClass, QType};
|
||||||
use dnstplib::response_processor::ResponseProcesor;
|
use dnstplib::message::request::DNSRequest;
|
||||||
|
use dnstplib::net::socket::DNSSocket;
|
||||||
|
use dnstplib::net::raw_request::NetworkMessage;
|
||||||
|
use dnstplib::processor::ResponseProcesor;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[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"));
|
socket.run_rx(processor.get_message_channel().expect("couldn't get message processing channel"));
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
loop {
|
loop {
|
||||||
|
|
||||||
info!("sending...");
|
info!("sending...");
|
||||||
|
|
||||||
let mut send_buf = [0; 512];
|
let message = DNSRequest {
|
||||||
send_buf[0] = 'a' as u8;
|
header: DNSHeader {
|
||||||
send_buf[1] = 'b' as u8;
|
id: rng.next_u32() as u16,
|
||||||
send_buf[2] = 'c' as u8;
|
direction: Direction::Request,
|
||||||
send_buf[3] = 'd' as u8;
|
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 {
|
tx_channel.send(Box::from(NetworkMessage {
|
||||||
buffer: Box::from(send_buf),
|
buffer: Box::from(bytes),
|
||||||
peer: args.address.parse().unwrap()
|
peer: args.address.parse().unwrap()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ use simplelog::*;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use dnstplib::dns_socket::DNSSocket;
|
use dnstplib::net::socket::DNSSocket;
|
||||||
use dnstplib::request_processor::RequestProcesor;
|
use dnstplib::processor::RequestProcesor;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
|
29
dnstp/src/byte.rs
Normal file
29
dnstp/src/byte.rs
Normal file
@ -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;
|
||||||
|
}
|
@ -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<DNSQuestion>,
|
|
||||||
pub peer: SocketAddr
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
pub mod dns_socket;
|
|
||||||
pub mod request_parser;
|
pub mod request_parser;
|
||||||
pub mod dns_header;
|
|
||||||
pub mod request_processor;
|
mod byte;
|
||||||
pub mod response_processor;
|
pub mod processor;
|
||||||
pub mod raw_request;
|
pub mod message;
|
||||||
pub mod dns_question;
|
pub mod net;
|
||||||
pub mod dns_request;
|
mod string;
|
43
dnstp/src/message/answer.rs
Normal file
43
dnstp/src/message/answer.rs
Normal file
@ -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<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DNSAnswer {
|
||||||
|
|
||||||
|
pub fn to_bytes(& self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use crate::byte::apply_split_bytes;
|
||||||
|
use crate::message::header::Direction::Response;
|
||||||
|
|
||||||
pub const HEADER_SIZE: usize = 12;
|
pub const HEADER_SIZE: usize = 12;
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ pub enum Opcode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u16> for Opcode {
|
impl TryFrom<u16> for Opcode {
|
||||||
type Error = ();
|
type Error = u16;
|
||||||
|
|
||||||
fn try_from(v: u16) -> Result<Self, Self::Error> {
|
fn try_from(v: u16) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v {
|
||||||
@ -25,7 +27,7 @@ impl TryFrom<u16> for Opcode {
|
|||||||
x if x == Opcode::RQuery as u16 => Ok(Opcode::RQuery),
|
x if x == Opcode::RQuery as u16 => Ok(Opcode::RQuery),
|
||||||
x if x == Opcode::Status as u16 => Ok(Opcode::Status),
|
x if x == Opcode::Status as u16 => Ok(Opcode::Status),
|
||||||
x if x == Opcode::Reserved as u16 => Ok(Opcode::Reserved),
|
x if x == Opcode::Reserved as u16 => Ok(Opcode::Reserved),
|
||||||
_ => Err(()),
|
_ => Err(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +48,7 @@ pub enum ResponseCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u16> for ResponseCode {
|
impl TryFrom<u16> for ResponseCode {
|
||||||
type Error = ();
|
type Error = u16;
|
||||||
|
|
||||||
fn try_from(v: u16) -> Result<Self, Self::Error> {
|
fn try_from(v: u16) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v {
|
||||||
@ -61,7 +63,7 @@ impl TryFrom<u16> for ResponseCode {
|
|||||||
x if x == ResponseCode::NXRRSet as u16 => Ok(ResponseCode::NXRRSet),
|
x if x == ResponseCode::NXRRSet as u16 => Ok(ResponseCode::NXRRSet),
|
||||||
x if x == ResponseCode::NotAuth as u16 => Ok(ResponseCode::NotAuth),
|
x if x == ResponseCode::NotAuth as u16 => Ok(ResponseCode::NotAuth),
|
||||||
x if x == ResponseCode::NotZone as u16 => Ok(ResponseCode::NotZone),
|
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 answer_record_count: u16,
|
||||||
pub authority_record_count: u16,
|
pub authority_record_count: u16,
|
||||||
pub additional_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
|
||||||
|
}
|
||||||
}
|
}
|
6
dnstp/src/message/mod.rs
Normal file
6
dnstp/src/message/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
pub mod header;
|
||||||
|
pub mod question;
|
||||||
|
pub mod request;
|
||||||
|
pub mod answer;
|
||||||
|
pub mod response;
|
@ -1,5 +1,6 @@
|
|||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
use urlencoding::{encode, decode};
|
use urlencoding::{encode, decode};
|
||||||
|
use crate::string::encode_domain_name;
|
||||||
|
|
||||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||||
pub enum QType {
|
pub enum QType {
|
||||||
@ -19,7 +20,7 @@ pub enum QType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for QType {
|
impl TryFrom<u8> for QType {
|
||||||
type Error = ();
|
type Error = u8;
|
||||||
|
|
||||||
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v {
|
||||||
@ -36,7 +37,7 @@ impl TryFrom<u8> for QType {
|
|||||||
x if x == QType::RP as u8 => Ok(QType::RP),
|
x if x == QType::RP as u8 => Ok(QType::RP),
|
||||||
x if x == QType::AAAA as u8 => Ok(QType::AAAA),
|
x if x == QType::AAAA as u8 => Ok(QType::AAAA),
|
||||||
x if x == QType::SRV as u8 => Ok(QType::SRV),
|
x if x == QType::SRV as u8 => Ok(QType::SRV),
|
||||||
_ => Err(()),
|
_ => Err(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,23 +50,23 @@ pub enum QClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for QClass {
|
impl TryFrom<u8> for QClass {
|
||||||
type Error = ();
|
type Error = u8;
|
||||||
|
|
||||||
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v {
|
||||||
x if x == QClass::Internet as u8 => Ok(QClass::Internet),
|
x if x == QClass::Internet as u8 => Ok(QClass::Internet),
|
||||||
x if x == QClass::Chaos as u8 => Ok(QClass::Chaos),
|
x if x == QClass::Chaos as u8 => Ok(QClass::Chaos),
|
||||||
x if x == QClass::Hesiod as u8 => Ok(QClass::Hesiod),
|
x if x == QClass::Hesiod as u8 => Ok(QClass::Hesiod),
|
||||||
_ => Err(()),
|
_ => Err(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
|
||||||
pub struct DNSQuestion {
|
pub struct DNSQuestion {
|
||||||
qname: String,
|
pub qname: String,
|
||||||
qtype: QType,
|
pub qtype: QType,
|
||||||
qclass: QClass
|
pub qclass: QClass
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DNSQuestion {
|
impl DNSQuestion {
|
||||||
@ -80,20 +81,7 @@ impl DNSQuestion {
|
|||||||
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8>
|
pub fn to_bytes(&self) -> Vec<u8>
|
||||||
{
|
{
|
||||||
let mut ret: Vec<u8> = Vec::with_capacity(self.qname.len() + 2 + 3);
|
let mut ret = encode_domain_name(&self.qname);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
ret.push(self.qtype as u8);
|
ret.push(self.qtype as u8);
|
||||||
ret.push(self.qclass as u8);
|
ret.push(self.qclass as u8);
|
||||||
@ -102,11 +90,30 @@ impl DNSQuestion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn questions_from_bytes(bytes: Vec<u8>, total_questions: u16) -> Result<Vec<DNSQuestion>, ()>
|
pub fn questions_to_bytes(questions: &Vec<DNSQuestion>) -> Vec<u8>
|
||||||
|
{
|
||||||
|
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<u8>, total_questions: u16) -> Result<Vec<DNSQuestion>, QuestionParseError>
|
||||||
{
|
{
|
||||||
if bytes.len() < 4
|
if bytes.len() < 4
|
||||||
{
|
{
|
||||||
return Err(());
|
return Err(QuestionParseError::ShortLength(bytes.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut questions: Vec<DNSQuestion> = Vec::with_capacity(total_questions as usize);
|
let mut questions: Vec<DNSQuestion> = Vec::with_capacity(total_questions as usize);
|
||||||
@ -150,7 +157,7 @@ pub fn questions_from_bytes(bytes: Vec<u8>, total_questions: u16) -> Result<Vec<
|
|||||||
match (qtype_b.try_into(), byte.try_into()) {
|
match (qtype_b.try_into(), byte.try_into()) {
|
||||||
(Ok(qtype), Ok(qclass)) => {
|
(Ok(qtype), Ok(qclass)) => {
|
||||||
questions.push(DNSQuestion {
|
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,
|
qtype,
|
||||||
qclass
|
qclass
|
||||||
});
|
});
|
||||||
@ -162,8 +169,11 @@ pub fn questions_from_bytes(bytes: Vec<u8>, total_questions: u16) -> Result<Vec<
|
|||||||
current_qclass = None;
|
current_qclass = None;
|
||||||
trailers_reached = false;
|
trailers_reached = false;
|
||||||
}
|
}
|
||||||
_ => {
|
(Err(qtype_e), _) => {
|
||||||
return Err(());
|
return Err(QuestionParseError::QTypeParse(qtype_e));
|
||||||
|
}
|
||||||
|
(_, Err(qclass_e)) => {
|
||||||
|
return Err(QuestionParseError::QClassParse(qclass_e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
23
dnstp/src/message/request.rs
Normal file
23
dnstp/src/message/request.rs
Normal file
@ -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<DNSQuestion>,
|
||||||
|
pub peer: SocketAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DNSRequest {
|
||||||
|
|
||||||
|
pub fn to_bytes(& self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
25
dnstp/src/message/response.rs
Normal file
25
dnstp/src/message/response.rs
Normal file
@ -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<DNSQuestion>,
|
||||||
|
pub answers: Vec<DNSAnswer>,
|
||||||
|
pub peer: SocketAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DNSResponse {
|
||||||
|
|
||||||
|
pub fn to_bytes(& self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
6
dnstp/src/net/mod.rs
Normal file
6
dnstp/src/net/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod socket;
|
||||||
|
// pub mod processor;
|
||||||
|
//
|
||||||
|
// pub mod processor::request_processor;
|
||||||
|
// pub mod processor::response_processor;
|
||||||
|
pub mod raw_request;
|
@ -3,6 +3,6 @@ use std::net::SocketAddr;
|
|||||||
pub type NetworkMessagePtr = Box<NetworkMessage>;
|
pub type NetworkMessagePtr = Box<NetworkMessage>;
|
||||||
|
|
||||||
pub struct NetworkMessage {
|
pub struct NetworkMessage {
|
||||||
pub buffer: Box<[u8; 512]>,
|
pub buffer: Box<Vec<u8>>,
|
||||||
pub peer: SocketAddr
|
pub peer: SocketAddr
|
||||||
}
|
}
|
@ -2,12 +2,10 @@ use std::net::{SocketAddr, UdpSocket};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::thread::{JoinHandle};
|
use std::thread::{JoinHandle};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
|
|
||||||
use std::str;
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
|
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
|
||||||
use crate::dns_header::HEADER_SIZE;
|
use crate::message::header::HEADER_SIZE;
|
||||||
use crate::raw_request::{NetworkMessage, NetworkMessagePtr};
|
use crate::net::raw_request::{NetworkMessage, NetworkMessagePtr};
|
||||||
|
|
||||||
pub struct DNSSocket {
|
pub struct DNSSocket {
|
||||||
addresses: Vec<SocketAddr>,
|
addresses: Vec<SocketAddr>,
|
||||||
@ -77,13 +75,12 @@ impl DNSSocket {
|
|||||||
Some(s) => {
|
Some(s) => {
|
||||||
let mut cancelled = false;
|
let mut cancelled = false;
|
||||||
while !cancelled {
|
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));
|
let res = s.recv_from(&mut (*buf));
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok((read_count, peer)) => {
|
Ok((read_count, peer)) => {
|
||||||
// let res_str = str::from_utf8(&(*buf)).unwrap();
|
|
||||||
// info!("received [{}] from [{}]", res_str, peer);
|
|
||||||
|
|
||||||
if read_count > HEADER_SIZE {
|
if read_count > HEADER_SIZE {
|
||||||
message_sender.send(Box::new(NetworkMessage {
|
message_sender.send(Box::new(NetworkMessage {
|
||||||
@ -131,7 +128,6 @@ impl DNSSocket {
|
|||||||
while !cancelled {
|
while !cancelled {
|
||||||
|
|
||||||
for m in &msg_rx {
|
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){
|
if let Err(e) = s.send_to(&(*m.buffer), m.peer){
|
||||||
error!("error sending response to [{}], {}", m.peer, e);
|
error!("error sending response to [{}], {}", m.peer, e);
|
||||||
}
|
}
|
6
dnstp/src/processor/mod.rs
Normal file
6
dnstp/src/processor/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
pub mod request;
|
||||||
|
pub mod response;
|
||||||
|
|
||||||
|
pub use request::RequestProcesor;
|
||||||
|
pub use response::ResponseProcesor;
|
78
dnstp/src/processor/request.rs
Normal file
78
dnstp/src/processor/request.rs
Normal file
@ -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<Sender<NetworkMessagePtr>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestProcesor {
|
||||||
|
pub fn new() -> RequestProcesor {
|
||||||
|
RequestProcesor{
|
||||||
|
message_channel: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, sending_channel: Sender<NetworkMessagePtr>)
|
||||||
|
{
|
||||||
|
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = 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<Sender<NetworkMessagePtr>>
|
||||||
|
{
|
||||||
|
self.message_channel.clone()
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ use std::sync::mpsc::{Receiver, Sender};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::str;
|
use std::str;
|
||||||
use crate::raw_request::NetworkMessagePtr;
|
use crate::net::raw_request::NetworkMessagePtr;
|
||||||
|
|
||||||
pub struct ResponseProcesor {
|
pub struct ResponseProcesor {
|
||||||
message_channel: Option<Sender<NetworkMessagePtr>>
|
message_channel: Option<Sender<NetworkMessagePtr>>
|
@ -1,43 +1,40 @@
|
|||||||
use crate::dns_header::{Direction, DNSHeader, Opcode, ResponseCode};
|
use crate::byte;
|
||||||
use crate::dns_header::Direction::Response;
|
use crate::message::header::{Direction, DNSHeader, Opcode, ResponseCode};
|
||||||
use crate::dns_question::{DNSQuestion, questions_from_bytes};
|
use crate::message::question::{QuestionParseError, questions_from_bytes};
|
||||||
use crate::dns_request::DNSRequest;
|
use crate::message::request::DNSRequest;
|
||||||
use crate::raw_request::NetworkMessage;
|
use crate::net::raw_request::NetworkMessage;
|
||||||
|
use crate::request_parser::RequestParseError::{HeaderParse, QuesionsParse};
|
||||||
|
|
||||||
fn two_byte_extraction(buffer: &[u8], idx: usize) -> u16
|
pub const ID_START: usize = 0;
|
||||||
{
|
pub const FLAGS_START: usize = 2;
|
||||||
((buffer[idx] as u16) << 8) | buffer[idx + 1] as u16
|
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<DNSHeader, HeaderParseError>
|
||||||
{
|
{
|
||||||
((num >> 8) as u8, (num & 0b0000000011111111) as u8)
|
let id = byte::two_byte_extraction(header, ID_START);
|
||||||
}
|
|
||||||
|
|
||||||
const ID_START: usize = 0;
|
let flags = byte::two_byte_extraction(header, FLAGS_START);
|
||||||
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<DNSHeader, ()>
|
|
||||||
{
|
|
||||||
let id = two_byte_extraction(header, ID_START);
|
|
||||||
|
|
||||||
let flags = two_byte_extraction(header, FLAGS_START);
|
|
||||||
let direction = if flags & (0b1 << DIRECTION_SHIFT) == 0 {Direction::Request} else { Direction::Response };
|
let direction = if flags & (0b1 << DIRECTION_SHIFT) == 0 {Direction::Request} else { Direction::Response };
|
||||||
|
|
||||||
let opcode: Result<Opcode, ()> = ((flags & (0b1111 << OPCODE_SHIFT)) >> OPCODE_SHIFT).try_into();
|
let opcode: Result<Opcode, u16> = ((flags & (0b1111 << OPCODE_SHIFT)) >> OPCODE_SHIFT).try_into();
|
||||||
if let Err(e) = opcode {
|
if let Err(e) = opcode {
|
||||||
return Err(e);
|
return Err(HeaderParseError::OpcodeParse(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
let authoritative = (flags & (0b1 << AUTHORITATIVE_SHIFT)) != 0;
|
let authoritative = (flags & (0b1 << AUTHORITATIVE_SHIFT)) != 0;
|
||||||
@ -47,16 +44,16 @@ pub fn parse_header(header: &[u8; 12]) -> Result<DNSHeader, ()>
|
|||||||
|
|
||||||
let zeroes = (flags & (0b111 << ZEROES_SHIFT)) == 0;
|
let zeroes = (flags & (0b111 << ZEROES_SHIFT)) == 0;
|
||||||
|
|
||||||
let response: Result<ResponseCode, ()> = (flags & 0b1111).try_into();
|
let response: Result<ResponseCode, u16> = (flags & 0b1111).try_into();
|
||||||
if let Err(e) = response
|
if let Err(e) = response
|
||||||
{
|
{
|
||||||
return Err(e);
|
return Err(HeaderParseError::ResponseCodeParse(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
let question_count = two_byte_extraction(header, QUESTION_COUNT_START);
|
let question_count = byte::two_byte_extraction(header, QUESTION_COUNT_START);
|
||||||
let answer_record_count = two_byte_extraction(header, ANSWER_RECORD_COUNT_START);
|
let answer_record_count = byte::two_byte_extraction(header, ANSWER_RECORD_COUNT_START);
|
||||||
let authority_record_count = two_byte_extraction(header, AUTHORITY_RECORD_COUNT_START);
|
let authority_record_count = byte::two_byte_extraction(header, AUTHORITY_RECORD_COUNT_START);
|
||||||
let additional_record_count = two_byte_extraction(header, ADDITIONAL_RECORD_COUNT_START);
|
let additional_record_count = byte::two_byte_extraction(header, ADDITIONAL_RECORD_COUNT_START);
|
||||||
|
|
||||||
Ok(DNSHeader {
|
Ok(DNSHeader {
|
||||||
id,
|
id,
|
||||||
@ -79,45 +76,13 @@ pub fn parse_header(header: &[u8; 12]) -> Result<DNSHeader, ()>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_split_bytes(buffer: &mut [u8], value: u16, index: usize)
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
|
||||||
{
|
pub enum RequestParseError {
|
||||||
let val = two_byte_split(value);
|
HeaderParse(HeaderParseError),
|
||||||
buffer[index] = val.0;
|
QuesionsParse(QuestionParseError),
|
||||||
buffer[index + 1] = val.1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_header_to_bytes(header: &DNSHeader) -> [u8; 12]
|
pub fn parse_request(msg: NetworkMessage) -> Result<DNSRequest, RequestParseError>
|
||||||
{
|
|
||||||
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<DNSRequest, ()>
|
|
||||||
{
|
{
|
||||||
let header = parse_header(msg.buffer[0..12].try_into().unwrap());
|
let header = parse_header(msg.buffer[0..12].try_into().unwrap());
|
||||||
|
|
||||||
@ -134,15 +99,16 @@ pub fn parse_request(msg: NetworkMessage) -> Result<DNSRequest, ()>
|
|||||||
peer: msg.peer
|
peer: msg.peer
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(_) => Err(())
|
Err(e) => Err(QuesionsParse(e))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => Err(())
|
Err(e) => Err(HeaderParse(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::byte::{two_byte_extraction, two_byte_split};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -188,7 +154,7 @@ mod tests {
|
|||||||
additional_record_count: 4
|
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();
|
let header_again = parse_header(&parsed_bytes).unwrap();
|
||||||
|
|
||||||
|
@ -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<Sender<NetworkMessagePtr>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestProcesor {
|
|
||||||
pub fn new() -> RequestProcesor {
|
|
||||||
RequestProcesor{
|
|
||||||
message_channel: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self, sending_channel: Sender<NetworkMessagePtr>)
|
|
||||||
{
|
|
||||||
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = 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<Sender<NetworkMessagePtr>>
|
|
||||||
{
|
|
||||||
self.message_channel.clone()
|
|
||||||
}
|
|
||||||
}
|
|
21
dnstp/src/string.rs
Normal file
21
dnstp/src/string.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use urlencoding::encode;
|
||||||
|
|
||||||
|
pub fn encode_domain_name(name: &String) -> Vec<u8>
|
||||||
|
{
|
||||||
|
let mut ret: Vec<u8> = 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user