serialising public key to string, adding docs, shifting stuff about
This commit is contained in:
parent
dcb257a784
commit
328c011109
@ -8,4 +8,6 @@ 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.
|
||||
|
||||
[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.
|
@ -1,4 +1,7 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
//! # Client Side
|
||||
//!
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@ -6,6 +9,7 @@ use clap::Parser;
|
||||
use log::{info, LevelFilter};
|
||||
use rand::RngCore;
|
||||
use simplelog::*;
|
||||
use dnstplib::DomainConfig;
|
||||
|
||||
use dnstplib::message::DNSMessage;
|
||||
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"));
|
||||
|
||||
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();
|
||||
loop {
|
||||
|
||||
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();
|
||||
|
||||
tx_channel.send(Box::from(NetworkMessage {
|
||||
buffer: Box::from(bytes),
|
||||
tx_channel.send(Box::new(NetworkMessage {
|
||||
buffer: Box::new(bytes),
|
||||
peer: args.address.parse().unwrap()
|
||||
}));
|
||||
|
||||
|
@ -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 std::{thread};
|
||||
|
||||
use log::info;
|
||||
use simplelog::*;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::fs::OpenOptions;
|
||||
use std::net::SocketAddr;
|
||||
use dnstplib::DomainConfig;
|
||||
|
||||
|
@ -4,4 +4,11 @@
|
||||
pub struct DomainConfig {
|
||||
pub base_domain: 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(".")
|
||||
}
|
||||
}
|
@ -1,25 +1,45 @@
|
||||
//! 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)]
|
||||
mod tests;
|
||||
|
||||
use std::str::FromStr;
|
||||
use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret, NistP256};
|
||||
use p256::elliptic_curve::ecdh::SharedSecret;
|
||||
use aes_gcm_siv::{aead::{Aead, KeyInit}, AeadCore, Aes256GcmSiv, Nonce};
|
||||
|
||||
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 public_point = EncodedPoint::from(secret.public_key());
|
||||
let public_point = secret.public_key().to_string();
|
||||
|
||||
(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(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)
|
||||
// }
|
||||
|
||||
/// Generate a safe nonce to use in symmetric encryption
|
||||
pub fn generate_aes_nonce() -> Nonce
|
||||
{
|
||||
Aes256GcmSiv::generate_nonce(OsRng)
|
||||
}
|
||||
|
||||
/// Turn the asymmetric shared secret into a symmetric encryption key
|
||||
pub fn asym_to_sym_key(secret: &SharedSecret<NistP256>) -> Aes256GcmSiv
|
||||
{
|
||||
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>, ()>
|
||||
{
|
||||
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>, ()>
|
||||
{
|
||||
match key.decrypt(nonce, bytes.as_ref()) {
|
||||
@ -69,4 +93,18 @@ pub fn decrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
@ -32,4 +32,15 @@ fn arbitrary_string_back_and_forth() {
|
||||
let result = String::from_utf8(plain_text).unwrap();
|
||||
|
||||
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);
|
||||
}
|
@ -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;
|
||||
|
||||
mod byte;
|
||||
@ -6,6 +9,6 @@ pub mod message;
|
||||
pub mod net;
|
||||
mod string;
|
||||
pub mod config;
|
||||
mod crypto;
|
||||
pub mod crypto;
|
||||
|
||||
pub use config::DomainConfig;
|
@ -1,18 +1,24 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
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)]
|
||||
pub struct DNSMessage {
|
||||
/// Status/request codes, counts for other collections
|
||||
pub header: DNSHeader,
|
||||
/// Hostname queries, should be the same in both requests and responses
|
||||
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 authority_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
|
||||
}
|
||||
|
||||
impl DNSMessage {
|
||||
|
||||
/// Transform a message into a network transmissable byte sequence
|
||||
pub fn to_bytes(& self) -> Vec<u8>
|
||||
{
|
||||
let mut header_bytes = self.header.to_bytes().to_vec();
|
||||
@ -29,7 +35,8 @@ impl DNSMessage {
|
||||
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 {
|
||||
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 {
|
||||
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{
|
||||
header: request.header.clone(),
|
||||
@ -83,7 +91,7 @@ impl DNSMessage {
|
||||
.map(|x|
|
||||
ResourceRecord::from_query(x,
|
||||
12,
|
||||
Box::from(ARdata::from(ip(x))),
|
||||
Box::new(ARdata::from(ip(x))),
|
||||
None))
|
||||
.collect();
|
||||
|
||||
|
@ -1,5 +1,13 @@
|
||||
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]
|
||||
fn one_question_back_and_forth() {
|
||||
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();
|
||||
|
||||
assert_eq!(q.qname, q_reconstructed[0].qname);
|
||||
assert_eq!(q.qclass, q_reconstructed[0].qclass);
|
||||
assert_eq!(q.qtype, q_reconstructed[0].qtype);
|
||||
assert_questions_eq!(q, q_reconstructed[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -39,13 +45,8 @@ fn two_questions_back_and_forth() {
|
||||
|
||||
let (q_reconstructed, q_remaining) = questions_from_bytes(q_bytes, 2).unwrap();
|
||||
|
||||
assert_eq!(q.qname, q_reconstructed[0].qname);
|
||||
assert_eq!(q.qclass, q_reconstructed[0].qclass);
|
||||
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);
|
||||
assert_questions_eq!(q, q_reconstructed[0]);
|
||||
assert_questions_eq!(q2, q_reconstructed[1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -77,15 +78,7 @@ fn three_questions_back_and_forth() {
|
||||
|
||||
let (q_reconstructed, q_remaining) = questions_from_bytes(q_bytes, 3).unwrap();
|
||||
|
||||
assert_eq!(q.qname, q_reconstructed[0].qname);
|
||||
assert_eq!(q.qclass, q_reconstructed[0].qclass);
|
||||
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);
|
||||
|
||||
assert_eq!(q3.qname, q_reconstructed[2].qname);
|
||||
assert_eq!(q3.qclass, q_reconstructed[2].qclass);
|
||||
assert_eq!(q3.qtype, q_reconstructed[2].qtype);
|
||||
assert_questions_eq!(q, q_reconstructed[0]);
|
||||
assert_questions_eq!(q2, q_reconstructed[1]);
|
||||
assert_questions_eq!(q3, q_reconstructed[2]);
|
||||
}
|
@ -8,8 +8,8 @@ pub struct ARdata {
|
||||
|
||||
impl Debug for ARdata {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("IP")
|
||||
.field("data", &self.rdata)
|
||||
f.debug_struct("A")
|
||||
.field("IP", &self.rdata)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ pub struct AAAARdata {
|
||||
|
||||
impl Debug for AAAARdata {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("IP")
|
||||
.field("data", &self.rdata)
|
||||
f.debug_struct("AAAA")
|
||||
.field("IP", &self.rdata)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ pub fn records_from_bytes(bytes: Vec<u8>, total_answers: u16) -> Result<(Vec<Res
|
||||
class: qclass_formed,
|
||||
ttl: four_byte_combine(ttl.0.unwrap(), ttl.1.unwrap(), ttl.2.unwrap(), ttl.3.unwrap()),
|
||||
rd_length: data_length_combined.unwrap(),
|
||||
r_data: Box::from(RawRData::from(data.clone()))
|
||||
r_data: Box::new(RawRData::from(data.clone()))
|
||||
});
|
||||
|
||||
name_offset = (None, None);
|
||||
|
@ -2,6 +2,17 @@ use crate::message::question::{QClass, QType};
|
||||
|
||||
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]
|
||||
fn one_answer_back_and_forth() {
|
||||
let q = ResourceRecord {
|
||||
@ -11,7 +22,7 @@ fn one_answer_back_and_forth() {
|
||||
class: QClass::Internet,
|
||||
ttl: 0,
|
||||
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();
|
||||
@ -19,12 +30,7 @@ fn one_answer_back_and_forth() {
|
||||
|
||||
let (q_reconstructed, q_remaining) = records_from_bytes(q_bytes, 1).unwrap();
|
||||
|
||||
assert_eq!(q.name_offset, q_reconstructed[0].name_offset);
|
||||
assert_eq!(q.answer_type, q_reconstructed[0].answer_type);
|
||||
assert_eq!(q.class, q_reconstructed[0].class);
|
||||
assert_eq!(q.ttl, q_reconstructed[0].ttl);
|
||||
assert_eq!(q.rd_length, q_reconstructed[0].rd_length);
|
||||
assert_eq!(q.r_data.to_bytes(), q_reconstructed[0].r_data.to_bytes());
|
||||
assert_record_eq!(q, q_reconstructed[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -36,7 +42,7 @@ fn two_answers_back_and_forth() {
|
||||
class: QClass::Internet,
|
||||
ttl: 0,
|
||||
rd_length: 1,
|
||||
r_data: Box::from(RawRData::from(vec![1]))
|
||||
r_data: Box::new(RawRData::from(vec![1]))
|
||||
};
|
||||
|
||||
let q_2 = ResourceRecord {
|
||||
@ -46,7 +52,7 @@ fn two_answers_back_and_forth() {
|
||||
class: QClass::Internet,
|
||||
ttl: 0,
|
||||
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();
|
||||
@ -55,17 +61,49 @@ fn two_answers_back_and_forth() {
|
||||
|
||||
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_record_eq!(q, q_reconstructed[0]);
|
||||
assert_record_eq!(q_2, q_reconstructed[1]);
|
||||
}
|
||||
|
||||
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());
|
||||
// }
|
@ -3,7 +3,7 @@
|
||||
use crate::byte;
|
||||
use crate::message::{DNSMessage, Direction, DNSHeader, Opcode, ResponseCode, QuestionParseError, questions_from_bytes, records_from_bytes, RecordParseError};
|
||||
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 FLAGS_START: usize = 2;
|
||||
@ -77,14 +77,14 @@ pub fn parse_header(header: &[u8; 12]) -> Result<DNSHeader, HeaderParseError>
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
|
||||
pub enum RequestParseError {
|
||||
pub enum MessageParseError {
|
||||
HeaderParse(HeaderParseError),
|
||||
QuesionsParse(QuestionParseError),
|
||||
RecordParse(RecordParseError),
|
||||
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());
|
||||
|
||||
@ -106,7 +106,7 @@ pub fn parse_message(msg: NetworkMessage) -> Result<DNSMessage, RequestParseErro
|
||||
Ok((mut answers, _)) => {
|
||||
|
||||
if answers.len() != total_records as usize {
|
||||
return Err(RequestParseError::RecordCount(total_records, answers.len()));
|
||||
return Err(MessageParseError::RecordCount(total_records, answers.len()));
|
||||
}
|
||||
else {
|
||||
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 {
|
||||
|
@ -47,7 +47,7 @@ impl DNSSocket {
|
||||
{
|
||||
match UdpSocket::bind(&self.addresses[..]) {
|
||||
Ok(s) => {
|
||||
self.socket = Option::from(Box::from(s));
|
||||
self.socket = Option::from(Box::new(s));
|
||||
},
|
||||
Err(_) => {}
|
||||
};
|
||||
@ -56,7 +56,7 @@ impl DNSSocket {
|
||||
fn get_socket_clone(&mut self) -> Option<Box<UdpSocket>>
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,55 @@
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod encryption;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use log::error;
|
||||
pub use request::RequestProcesor;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
16
dnstp/src/processor/request/encryption.rs
Normal file
16
dnstp/src/processor/request/encryption.rs
Normal 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("."))
|
||||
}
|
103
dnstp/src/processor/request/mod.rs
Normal file
103
dnstp/src/processor/request/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
15
dnstp/src/processor/request/tests.rs
Normal file
15
dnstp/src/processor/request/tests.rs
Normal 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);
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
50
dnstp/src/processor/response/mod.rs
Normal file
50
dnstp/src/processor/response/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user