serialising public key to string, adding docs, shifting stuff about

This commit is contained in:
Andy Pack 2024-02-09 20:47:46 +00:00
parent dcb257a784
commit 328c011109
Signed by: sarsoo
GPG Key ID: A55BA3536A5E0ED7
23 changed files with 417 additions and 282 deletions

View File

@ -9,3 +9,5 @@ I remember I was listening to, I think, [Security This Week with Carl Franklin](
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. 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.
[Read the Docs](https://github.com/Sarsoo/dnstp/settings/pages) [Read the Docs](https://github.com/Sarsoo/dnstp/settings/pages)
One of my aims was to see whether arbitrary data could be transmitted using more or less compliant DNS messages, i.e.not just sending junk over UDP to port 53. The closer to compliant DNS that the messages are, the more subtle the process is. In my own network, I have NAT rules that will redirect any DNS messages that are destined for external DNS servers to my own internal ones first. If the packets are crap and malformed, they could be rejected before they even reach my subtle server.

View File

@ -1,4 +1,7 @@
use std::fs::{File, OpenOptions}; //! # Client Side
//!
use std::fs::OpenOptions;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -6,6 +9,7 @@ use clap::Parser;
use log::{info, LevelFilter}; use log::{info, LevelFilter};
use rand::RngCore; use rand::RngCore;
use simplelog::*; use simplelog::*;
use dnstplib::DomainConfig;
use dnstplib::message::DNSMessage; use dnstplib::message::DNSMessage;
use dnstplib::net::{DNSSocket, NetworkMessage}; use dnstplib::net::{DNSSocket, NetworkMessage};
@ -53,19 +57,24 @@ 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 domain = vec![args.key_endpoint, args.base_domain].join("."); let domain_config = DomainConfig {
base_domain: args.base_domain,
key_endpoint: args.key_endpoint
};
let domain = domain_config.get_fq_key_endpoint();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
loop { loop {
info!("sending..."); info!("sending...");
let message = DNSMessage::from_hostname(address, rng.next_u32() as u16, domain.clone()); let message = DNSMessage::req_from_hostname(address, rng.next_u32() as u16, domain.clone());
let bytes = message.to_bytes(); let bytes = message.to_bytes();
tx_channel.send(Box::from(NetworkMessage { tx_channel.send(Box::new(NetworkMessage {
buffer: Box::from(bytes), buffer: Box::new(bytes),
peer: args.address.parse().unwrap() peer: args.address.parse().unwrap()
})); }));

View File

@ -1,11 +1,14 @@
//! DNS server component for processing requests and replying with DNS records //! # Server Side
//! DNS server component for processing requests and replying with DNS records.
//!
//! The aim is to have clients exfil to this server and to allow pulling down data from the server.
use clap::Parser; use clap::Parser;
use std::{thread}; use std::{thread};
use log::info; use log::info;
use simplelog::*; use simplelog::*;
use std::fs::{File, OpenOptions}; use std::fs::OpenOptions;
use std::net::SocketAddr; use std::net::SocketAddr;
use dnstplib::DomainConfig; use dnstplib::DomainConfig;

View File

@ -5,3 +5,10 @@ pub struct DomainConfig {
pub base_domain: String, pub base_domain: String,
pub key_endpoint: String, pub key_endpoint: String,
} }
impl DomainConfig {
pub fn get_fq_key_endpoint(&self) -> String
{
vec![self.key_endpoint.clone(), self.base_domain.clone()].join(".")
}
}

View File

@ -1,25 +1,45 @@
//! Method for handling cryptography including ECDH shared secret derivation and symmetric key encryption //! Method for handling cryptography including ECDH shared secret derivation and symmetric key encryption
//!
//! **Step 1**: [`get_random_asym_pair`] Generate a public/private key pair on each side
//!
//! **Step 2**: Swap the [`p256::EncodedPoint`]s from step 1 between parties
//!
//! **Step 3**: [`get_shared_asym_secret`] Combine one private key with the other public key to end up at the same shared secret
//!
//! **Step 4**: [`asym_to_sym_key`] Take the [`p256::NistP256`] shared asymmetric secret key and use it as a symmetric key ready for encryption decryption
//!
//! **Step 5**: [`generate_aes_nonce`] Get a nonce to use when encrypting
//!
//! **Step 6**: [`encrypt`] Use the key from step 4 with the nonce from step 5 to encrypt arbitrary data
//!
//! **Step 7**: [`decrypt`] Use the same key from step 6 and ***the same nonce from step 6*** to decrypt the outputted ciphertext from step 6.
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::str::FromStr;
use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret, NistP256}; use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret, NistP256};
use p256::elliptic_curve::ecdh::SharedSecret; use p256::elliptic_curve::ecdh::SharedSecret;
use aes_gcm_siv::{aead::{Aead, KeyInit}, AeadCore, Aes256GcmSiv, Nonce}; use aes_gcm_siv::{aead::{Aead, KeyInit}, AeadCore, Aes256GcmSiv, Nonce};
use rand_core::OsRng; use rand_core::OsRng;
pub fn get_random_asym_pair() -> (EphemeralSecret, EncodedPoint) pub const PUBLIC_KEY_OPENING: &str = "-----BEGIN PUBLIC KEY-----\n";
pub const PUBLIC_KEY_CLOSING: &str = "\n-----END PUBLIC KEY-----\n";
/// Generate a public/private key pair
pub fn get_random_asym_pair() -> (EphemeralSecret, String)
{ {
let secret = EphemeralSecret::random(&mut OsRng); let secret = EphemeralSecret::random(&mut OsRng);
let public_point = EncodedPoint::from(secret.public_key()); let public_point = secret.public_key().to_string();
(secret, public_point) (secret, public_point)
} }
pub fn get_shared_asym_secret(secret: EphemeralSecret, opposing_public_key: EncodedPoint) -> Result<SharedSecret<NistP256>, ()> { /// Use one private key and an opposing public key to arrive at the same shared secret
pub fn get_shared_asym_secret(secret: EphemeralSecret, opposing_public_key: String) -> Result<SharedSecret<NistP256>, ()> {
match PublicKey::from_sec1_bytes(opposing_public_key.as_ref()) { match PublicKey::from_str(&opposing_public_key) {
Ok(other_public) => { Ok(other_public) => {
Ok(secret.diffie_hellman(&other_public)) Ok(secret.diffie_hellman(&other_public))
} }
@ -37,16 +57,19 @@ pub fn get_shared_asym_secret(secret: EphemeralSecret, opposing_public_key: Enco
// Nonce::from(nonce_buffer) // Nonce::from(nonce_buffer)
// } // }
/// Generate a safe nonce to use in symmetric encryption
pub fn generate_aes_nonce() -> Nonce pub fn generate_aes_nonce() -> Nonce
{ {
Aes256GcmSiv::generate_nonce(OsRng) Aes256GcmSiv::generate_nonce(OsRng)
} }
/// Turn the asymmetric shared secret into a symmetric encryption key
pub fn asym_to_sym_key(secret: &SharedSecret<NistP256>) -> Aes256GcmSiv pub fn asym_to_sym_key(secret: &SharedSecret<NistP256>) -> Aes256GcmSiv
{ {
Aes256GcmSiv::new(secret.raw_secret_bytes()) Aes256GcmSiv::new(secret.raw_secret_bytes())
} }
/// Symmetrically encrypt data using a key derived from ECDH
pub fn encrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()> pub fn encrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()>
{ {
match key.encrypt(nonce, bytes.as_ref()) { match key.encrypt(nonce, bytes.as_ref()) {
@ -59,6 +82,7 @@ pub fn encrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec
} }
} }
/// Symmetrically decrypt data using a key derived from ECDH
pub fn decrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()> pub fn decrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()>
{ {
match key.decrypt(nonce, bytes.as_ref()) { match key.decrypt(nonce, bytes.as_ref()) {
@ -70,3 +94,17 @@ pub fn decrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec
} }
} }
} }
pub fn trim_public_key(public: &String) -> String
{
public[27.. 125 + 27].to_string().replace('\n', ".")
}
pub fn fatten_public_key(public: &String) -> String
{
let mut fattened = public.clone();
fattened.insert_str(0, PUBLIC_KEY_OPENING);
fattened.push_str(PUBLIC_KEY_CLOSING);
fattened.replace('.', "\n")
}

View File

@ -33,3 +33,14 @@ fn arbitrary_string_back_and_forth() {
assert_eq!(data, result); assert_eq!(data, result);
} }
#[test]
fn public_key_trimming()
{
let (_, public) = get_random_asym_pair();
let trimmed = trim_public_key(&public);
let fattened = fatten_public_key(&trimmed.to_string());
assert_eq!(public, fattened);
}

View File

@ -1,3 +1,6 @@
//! # Common Functionality
//! The vast majority of functionality is in this library crate. The client and server executable crates are really just wiring up bits and pieces from this library.
pub mod message_parser; pub mod message_parser;
mod byte; mod byte;
@ -6,6 +9,6 @@ pub mod message;
pub mod net; pub mod net;
mod string; mod string;
pub mod config; pub mod config;
mod crypto; pub mod crypto;
pub use config::DomainConfig; pub use config::DomainConfig;

View File

@ -1,18 +1,24 @@
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, ResponseCode, QType, QClass, ResourceRecord, records_to_bytes, ARdata}; use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, ResponseCode, QType, QClass, ResourceRecord, records_to_bytes, ARdata};
/// A DNS message which can be used as either a request or response based on its direction and composition
#[derive(Debug)] #[derive(Debug)]
pub struct DNSMessage { pub struct DNSMessage {
/// Status/request codes, counts for other collections
pub header: DNSHeader, pub header: DNSHeader,
/// Hostname queries, should be the same in both requests and responses
pub questions: Vec<DNSQuestion>, pub questions: Vec<DNSQuestion>,
/// Responses for [`DNSMessage::questions`], has similar structure with varying data field based on query type
pub answer_records: Vec<ResourceRecord>, pub answer_records: Vec<ResourceRecord>,
pub authority_records: Vec<ResourceRecord>, pub authority_records: Vec<ResourceRecord>,
pub additional_records: Vec<ResourceRecord>, pub additional_records: Vec<ResourceRecord>,
/// IP and socket address of the client which sent this message/client to send message to
pub peer: SocketAddr pub peer: SocketAddr
} }
impl DNSMessage { impl DNSMessage {
/// Transform a message into a network transmissable byte sequence
pub fn to_bytes(& self) -> Vec<u8> pub fn to_bytes(& self) -> Vec<u8>
{ {
let mut header_bytes = self.header.to_bytes().to_vec(); let mut header_bytes = self.header.to_bytes().to_vec();
@ -29,7 +35,8 @@ impl DNSMessage {
return header_bytes return header_bytes
} }
pub fn from_hostname(peer: SocketAddr, id: u16, hostname: String) -> DNSMessage /// Helper function for getting a DNS request for the IPv4 of a single hostname
pub fn req_from_hostname(peer: SocketAddr, id: u16, hostname: String) -> DNSMessage
{ {
DNSMessage { DNSMessage {
header: DNSHeader::new_request(id, None), header: DNSHeader::new_request(id, None),
@ -47,7 +54,8 @@ impl DNSMessage {
} }
} }
pub fn from_hostnames(peer: SocketAddr, id: u16, hostnames: Vec<String>) -> DNSMessage /// Helper function to get a DNS request for the IPv4s of multiple hostnames
pub fn reqs_from_hostnames(peer: SocketAddr, id: u16, hostnames: Vec<String>) -> DNSMessage
{ {
DNSMessage { DNSMessage {
header: DNSHeader::new_request(id, Some(hostnames.len() as u16)), header: DNSHeader::new_request(id, Some(hostnames.len() as u16)),
@ -67,7 +75,7 @@ impl DNSMessage {
} }
} }
pub fn a_from_request(request: &DNSMessage, ip: impl Fn(&DNSQuestion) -> Ipv4Addr) -> DNSMessage pub fn a_resp_from_request(request: &DNSMessage, ip: impl Fn(&DNSQuestion) -> Ipv4Addr) -> DNSMessage
{ {
let mut response = DNSMessage{ let mut response = DNSMessage{
header: request.header.clone(), header: request.header.clone(),
@ -83,7 +91,7 @@ impl DNSMessage {
.map(|x| .map(|x|
ResourceRecord::from_query(x, ResourceRecord::from_query(x,
12, 12,
Box::from(ARdata::from(ip(x))), Box::new(ARdata::from(ip(x))),
None)) None))
.collect(); .collect();

View File

@ -1,5 +1,13 @@
use super::*; use super::*;
macro_rules! assert_questions_eq {
($left:expr, $right:expr) => {
assert_eq!($left.qname, $right.qname);
assert_eq!($left.qclass, $right.qclass);
assert_eq!($left.qtype, $right.qtype);
};
}
#[test] #[test]
fn one_question_back_and_forth() { fn one_question_back_and_forth() {
let q = DNSQuestion { let q = DNSQuestion {
@ -13,9 +21,7 @@ fn one_question_back_and_forth() {
let (q_reconstructed, q_remaining) = 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_questions_eq!(q, q_reconstructed[0]);
assert_eq!(q.qclass, q_reconstructed[0].qclass);
assert_eq!(q.qtype, q_reconstructed[0].qtype);
} }
#[test] #[test]
@ -39,13 +45,8 @@ fn two_questions_back_and_forth() {
let (q_reconstructed, q_remaining) = 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_questions_eq!(q, q_reconstructed[0]);
assert_eq!(q.qclass, q_reconstructed[0].qclass); assert_questions_eq!(q2, q_reconstructed[1]);
assert_eq!(q.qtype, q_reconstructed[0].qtype);
assert_eq!(q2.qname, q_reconstructed[1].qname);
assert_eq!(q2.qclass, q_reconstructed[1].qclass);
assert_eq!(q2.qtype, q_reconstructed[1].qtype);
} }
#[test] #[test]
@ -77,15 +78,7 @@ fn three_questions_back_and_forth() {
let (q_reconstructed, q_remaining) = 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_questions_eq!(q, q_reconstructed[0]);
assert_eq!(q.qclass, q_reconstructed[0].qclass); assert_questions_eq!(q2, q_reconstructed[1]);
assert_eq!(q.qtype, q_reconstructed[0].qtype); assert_questions_eq!(q3, q_reconstructed[2]);
assert_eq!(q2.qname, q_reconstructed[1].qname);
assert_eq!(q2.qclass, q_reconstructed[1].qclass);
assert_eq!(q2.qtype, q_reconstructed[1].qtype);
assert_eq!(q3.qname, q_reconstructed[2].qname);
assert_eq!(q3.qclass, q_reconstructed[2].qclass);
assert_eq!(q3.qtype, q_reconstructed[2].qtype);
} }

View File

@ -8,8 +8,8 @@ pub struct ARdata {
impl Debug for ARdata { impl Debug for ARdata {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IP") f.debug_struct("A")
.field("data", &self.rdata) .field("IP", &self.rdata)
.finish() .finish()
} }
} }

View File

@ -8,8 +8,8 @@ pub struct AAAARdata {
impl Debug for AAAARdata { impl Debug for AAAARdata {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IP") f.debug_struct("AAAA")
.field("data", &self.rdata) .field("IP", &self.rdata)
.finish() .finish()
} }
} }

View File

@ -128,7 +128,7 @@ pub fn records_from_bytes(bytes: Vec<u8>, total_answers: u16) -> Result<(Vec<Res
class: qclass_formed, class: qclass_formed,
ttl: four_byte_combine(ttl.0.unwrap(), ttl.1.unwrap(), ttl.2.unwrap(), ttl.3.unwrap()), ttl: four_byte_combine(ttl.0.unwrap(), ttl.1.unwrap(), ttl.2.unwrap(), ttl.3.unwrap()),
rd_length: data_length_combined.unwrap(), rd_length: data_length_combined.unwrap(),
r_data: Box::from(RawRData::from(data.clone())) r_data: Box::new(RawRData::from(data.clone()))
}); });
name_offset = (None, None); name_offset = (None, None);

View File

@ -2,6 +2,17 @@ use crate::message::question::{QClass, QType};
use super::*; use super::*;
macro_rules! assert_record_eq {
($left:expr, $right:expr) => {
assert_eq!($left.name_offset, $right.name_offset);
assert_eq!($left.answer_type, $right.answer_type);
assert_eq!($left.class, $right.class);
assert_eq!($left.ttl, $right.ttl);
assert_eq!($left.rd_length, $right.rd_length);
assert_eq!($left.r_data.to_bytes(), $right.r_data.to_bytes());
};
}
#[test] #[test]
fn one_answer_back_and_forth() { fn one_answer_back_and_forth() {
let q = ResourceRecord { let q = ResourceRecord {
@ -11,7 +22,7 @@ fn one_answer_back_and_forth() {
class: QClass::Internet, class: QClass::Internet,
ttl: 0, ttl: 0,
rd_length: 1, rd_length: 1,
r_data: Box::from(RawRData::from(vec![1])) r_data: Box::new(RawRData::from(vec![1]))
}; };
let mut q_bytes = q.to_bytes(); let mut q_bytes = q.to_bytes();
@ -19,12 +30,7 @@ fn one_answer_back_and_forth() {
let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 1).unwrap(); let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 1).unwrap();
assert_eq!(q.name_offset, q_reconstructed[0].name_offset); assert_record_eq!(q, q_reconstructed[0]);
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] #[test]
@ -36,7 +42,7 @@ fn two_answers_back_and_forth() {
class: QClass::Internet, class: QClass::Internet,
ttl: 0, ttl: 0,
rd_length: 1, rd_length: 1,
r_data: Box::from(RawRData::from(vec![1])) r_data: Box::new(RawRData::from(vec![1]))
}; };
let q_2 = ResourceRecord { let q_2 = ResourceRecord {
@ -46,7 +52,7 @@ fn two_answers_back_and_forth() {
class: QClass::Internet, class: QClass::Internet,
ttl: 0, ttl: 0,
rd_length: 3, rd_length: 3,
r_data: Box::from(RawRData::from(vec![1, 2, 3])) r_data: Box::new(RawRData::from(vec![1, 2, 3]))
}; };
let mut q_bytes = q.to_bytes(); let mut q_bytes = q.to_bytes();
@ -55,17 +61,49 @@ fn two_answers_back_and_forth() {
let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 2).unwrap(); let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 2).unwrap();
assert_eq!(q.name_offset, q_reconstructed[0].name_offset); assert_record_eq!(q, q_reconstructed[0]);
assert_eq!(q.answer_type, q_reconstructed[0].answer_type); assert_record_eq!(q_2, q_reconstructed[1]);
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());
} }
// #[test]
// fn two_answers_back_and_forth_zero_data() {
// let q = ResourceRecord {
// // name_offset: "google.com".to_string(),
// name_offset: 12,
// answer_type: QType::A,
// class: QClass::Internet,
// ttl: 0,
// rd_length: 0,
// r_data: Box::new(RawRData::from(vec![]))
// };
//
// 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::new(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());
// }

View File

@ -3,7 +3,7 @@
use crate::byte; use crate::byte;
use crate::message::{DNSMessage, Direction, DNSHeader, Opcode, ResponseCode, QuestionParseError, questions_from_bytes, records_from_bytes, RecordParseError}; use crate::message::{DNSMessage, Direction, DNSHeader, Opcode, ResponseCode, QuestionParseError, questions_from_bytes, records_from_bytes, RecordParseError};
use crate::net::NetworkMessage; use crate::net::NetworkMessage;
use crate::message_parser::RequestParseError::{HeaderParse, QuesionsParse}; use crate::message_parser::MessageParseError::{HeaderParse, QuesionsParse};
pub const ID_START: usize = 0; pub const ID_START: usize = 0;
pub const FLAGS_START: usize = 2; pub const FLAGS_START: usize = 2;
@ -77,14 +77,14 @@ pub fn parse_header(header: &[u8; 12]) -> Result<DNSHeader, HeaderParseError>
} }
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
pub enum RequestParseError { pub enum MessageParseError {
HeaderParse(HeaderParseError), HeaderParse(HeaderParseError),
QuesionsParse(QuestionParseError), QuesionsParse(QuestionParseError),
RecordParse(RecordParseError), RecordParse(RecordParseError),
RecordCount(u16, usize), RecordCount(u16, usize),
} }
pub fn parse_message(msg: NetworkMessage) -> Result<DNSMessage, RequestParseError> pub fn parse_message(msg: NetworkMessage) -> Result<DNSMessage, MessageParseError>
{ {
let header = parse_header(msg.buffer[0..12].try_into().unwrap()); let header = parse_header(msg.buffer[0..12].try_into().unwrap());
@ -106,7 +106,7 @@ pub fn parse_message(msg: NetworkMessage) -> Result<DNSMessage, RequestParseErro
Ok((mut answers, _)) => { Ok((mut answers, _)) => {
if answers.len() != total_records as usize { if answers.len() != total_records as usize {
return Err(RequestParseError::RecordCount(total_records, answers.len())); return Err(MessageParseError::RecordCount(total_records, answers.len()));
} }
else { else {
let answer_records = answers.drain(0 .. (header.answer_record_count as usize)).collect(); let answer_records = answers.drain(0 .. (header.answer_record_count as usize)).collect();
@ -122,7 +122,7 @@ pub fn parse_message(msg: NetworkMessage) -> Result<DNSMessage, RequestParseErro
}); });
} }
} }
Err(e) => return Err(RequestParseError::RecordParse(e)) Err(e) => return Err(MessageParseError::RecordParse(e))
} }
} }
else { else {

View File

@ -47,7 +47,7 @@ impl DNSSocket {
{ {
match UdpSocket::bind(&self.addresses[..]) { match UdpSocket::bind(&self.addresses[..]) {
Ok(s) => { Ok(s) => {
self.socket = Option::from(Box::from(s)); self.socket = Option::from(Box::new(s));
}, },
Err(_) => {} Err(_) => {}
}; };
@ -56,7 +56,7 @@ impl DNSSocket {
fn get_socket_clone(&mut self) -> Option<Box<UdpSocket>> fn get_socket_clone(&mut self) -> Option<Box<UdpSocket>>
{ {
match &self.socket { match &self.socket {
Some(s) => Option::from(Box::from(s.try_clone().unwrap())), Some(s) => Option::from(Box::new(s.try_clone().unwrap())),
None => None None => None
} }
} }

View File

@ -2,7 +2,55 @@
pub mod request; pub mod request;
pub mod response; pub mod response;
pub mod encryption;
use std::net::SocketAddr;
use log::error;
pub use request::RequestProcesor; pub use request::RequestProcesor;
pub use response::ResponseProcesor; pub use response::ResponseProcesor;
use crate::message::{QuestionParseError, RecordParseError};
use crate::message_parser::{HeaderParseError, MessageParseError};
pub fn print_error(e: MessageParseError, peer: &SocketAddr)
{
match e {
MessageParseError::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);
}
}
}
MessageParseError::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);
}
}
}
MessageParseError::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);
}
}
}
MessageParseError::RecordCount(expected, actual) => {
error!("[{}] failed to parse records of received message, record count mismatch: [Expected:{}] [Actual:{}]", peer, expected, actual);
}
}
}

View File

@ -1,120 +0,0 @@
use std::net::Ipv4Addr;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use log::{error, info};
use crate::config::DomainConfig;
use crate::message::{DNSMessage, QuestionParseError, RecordParseError};
use crate::net::{NetworkMessage, NetworkMessagePtr};
use crate::message_parser::{HeaderParseError, parse_message, RequestParseError};
pub struct RequestProcesor {
message_channel: Option<Sender<NetworkMessagePtr>>,
domain_config: DomainConfig
}
impl RequestProcesor {
pub fn new(domain_config: DomainConfig) -> RequestProcesor {
RequestProcesor {
message_channel: None,
domain_config
}
}
pub fn run(&mut self, sending_channel: Sender<NetworkMessagePtr>)
{
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = mpsc::channel();
self.message_channel = Some(tx);
let mut base_domain_equality = self.domain_config.base_domain.clone();
base_domain_equality.insert_str(0, ".");
let base_domain_len = base_domain_equality.len() + 1;
thread::spawn(move || {
for m in rx
{
let peer = m.peer.clone();
match parse_message(*m) {
Ok(r) => {
info!("received dns message: {:?}", r);
if r.questions.iter().any(|q| q.qname.ends_with(&base_domain_equality))
{
let mut response = DNSMessage::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 = DNSMessage::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
}
));
}
}
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")
});
}
pub fn get_message_channel(&mut self) -> Option<Sender<NetworkMessagePtr>>
{
self.message_channel.clone()
}
}

View File

@ -0,0 +1,16 @@
use std::net::Ipv4Addr;
use p256::ecdh::EphemeralSecret;
use crate::crypto::{get_random_asym_pair, trim_public_key};
use crate::message::DNSMessage;
pub fn get_key_response(request: DNSMessage) -> DNSMessage
{
DNSMessage::a_resp_from_request(&request, |_| Ipv4Addr::from([127, 0, 0, 1]))
}
pub fn get_key_request_with_base_domain(base_domain: String) -> (EphemeralSecret, String)
{
let (private, public) = get_random_asym_pair();
(private, vec![trim_public_key(&public), base_domain].join("."))
}

View File

@ -0,0 +1,103 @@
use std::net::Ipv4Addr;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use log::info;
use crate::config::DomainConfig;
use crate::message::DNSMessage;
use crate::net::{NetworkMessage, NetworkMessagePtr};
use crate::message_parser::parse_message;
use crate::processor::print_error;
pub mod encryption;
#[cfg(test)]
mod tests;
pub struct RequestProcesor {
message_channel: Option<Sender<NetworkMessagePtr>>,
domain_config: DomainConfig,
encryption_endpoint: String
}
impl RequestProcesor {
pub fn new(domain_config: DomainConfig) -> RequestProcesor {
let fq_key_endpoint = domain_config.get_fq_key_endpoint();
RequestProcesor {
message_channel: None,
domain_config,
encryption_endpoint: fq_key_endpoint
}
}
pub fn run(&mut self, sending_channel: Sender<NetworkMessagePtr>)
{
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = mpsc::channel();
self.message_channel = Some(tx);
let mut base_domain_equality = self.domain_config.base_domain.clone();
base_domain_equality.insert_str(0, ".");
let base_domain_len = base_domain_equality.len() + 1;
let fq_key_endpoint = self.encryption_endpoint.clone();
thread::spawn(move || {
// let fq_key_endpoint = fq_key_endpoint;
for m in rx
{
let peer = m.peer.clone();
match parse_message(*m) {
Ok(r) => {
info!("received dns message: {:?}", r);
if r.questions.iter().any(|q| q.qname.ends_with(&base_domain_equality))
{
if r.questions[0].qname.eq_ignore_ascii_case(&fq_key_endpoint)
{
info!("[{}] received encryption key request", peer);
}
else
{
let response = DNSMessage::a_resp_from_request(&r, |_| Ipv4Addr::from([127, 0, 0, 1]));
sending_channel.send(Box::new(
NetworkMessage {
buffer: Box::new(response.to_bytes()),
peer: response.peer
}
));
}
}
else {
let response = DNSMessage::a_resp_from_request(&r, |_| Ipv4Addr::from([127, 0, 0, 1]));
sending_channel.send(Box::new(
NetworkMessage {
buffer: Box::new(response.to_bytes()),
peer: response.peer
}
));
}
}
Err(e) => {
print_error(e, &peer);
}
}
}
info!("message processing thread finishing")
});
}
pub fn get_message_channel(&mut self) -> Option<Sender<NetworkMessagePtr>>
{
self.message_channel.clone()
}
}

View File

@ -0,0 +1,15 @@
use crate::crypto::{fatten_public_key, get_random_asym_pair, trim_public_key};
use crate::string::encode_domain_name;
use super::*;
use super::encryption::*;
#[test]
fn encryption()
{
let (private, public) = get_key_request_with_base_domain(String::from("sarsoo.xyz"));
let encoded = encode_domain_name(&public);
// let decoded = decode_domain_name();
assert_eq!(1, 1);
}

View File

@ -1,89 +0,0 @@
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use log::{error, info};
use crate::message::{QuestionParseError, RecordParseError};
use crate::net::raw_request::NetworkMessagePtr;
use crate::message_parser::{HeaderParseError, parse_message, RequestParseError};
pub struct ResponseProcesor {
message_channel: Option<Sender<NetworkMessagePtr>>
}
impl ResponseProcesor {
pub fn new() -> ResponseProcesor {
ResponseProcesor{
message_channel: None
}
}
pub fn run(&mut self)
{
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = mpsc::channel();
self.message_channel = Some(tx);
thread::spawn(move || {
for mut m in rx
{
let peer = m.peer.clone();
match parse_message(*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")
});
}
pub fn get_message_channel(&mut self) -> Option<Sender<NetworkMessagePtr>>
{
self.message_channel.clone()
}
}

View File

@ -0,0 +1,50 @@
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use log::{error, info};
use crate::message::{QuestionParseError, RecordParseError};
use crate::net::raw_request::NetworkMessagePtr;
use crate::message_parser::{HeaderParseError, parse_message, MessageParseError};
use crate::processor::print_error;
pub struct ResponseProcesor {
message_channel: Option<Sender<NetworkMessagePtr>>
}
impl ResponseProcesor {
pub fn new() -> ResponseProcesor {
ResponseProcesor{
message_channel: None
}
}
pub fn run(&mut self)
{
let (tx, rx): (Sender<NetworkMessagePtr>, Receiver<NetworkMessagePtr>) = mpsc::channel();
self.message_channel = Some(tx);
thread::spawn(move || {
for mut m in rx
{
let peer = m.peer.clone();
match parse_message(*m) {
Ok(r) => {
info!("received dns message: {:?}", r);
}
Err(e) => {
print_error(e, &peer);
}
}
}
info!("message processing thread finishing")
});
}
pub fn get_message_channel(&mut self) -> Option<Sender<NetworkMessagePtr>>
{
self.message_channel.clone()
}
}