parsing questions back and forth
This commit is contained in:
parent
985adbae68
commit
7cf99b610a
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -130,16 +130,7 @@ name = "dnstplib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -148,16 +139,6 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
@ -185,12 +166,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
@ -303,27 +278,6 @@ dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
@ -331,24 +285,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
|
@ -7,4 +7,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.20"
|
||||
url = "2.5.0"
|
||||
urlencoding = "2.1.3"
|
@ -1,4 +1,5 @@
|
||||
use url::form_urlencoded;
|
||||
use std::ops::Sub;
|
||||
use urlencoding::{encode, decode};
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
enum QType {
|
||||
@ -17,6 +18,29 @@ enum QType {
|
||||
SRV = 33
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for QType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
x if x == QType::A as u8 => Ok(QType::A),
|
||||
x if x == QType::NS as u8 => Ok(QType::NS),
|
||||
x if x == QType::CNAME as u8 => Ok(QType::CNAME),
|
||||
x if x == QType::SOA as u8 => Ok(QType::SOA),
|
||||
x if x == QType::WKS as u8 => Ok(QType::WKS),
|
||||
x if x == QType::PTR as u8 => Ok(QType::PTR),
|
||||
x if x == QType::HINFO as u8 => Ok(QType::HINFO),
|
||||
x if x == QType::MINFO as u8 => Ok(QType::MINFO),
|
||||
x if x == QType::MX as u8 => Ok(QType::MX),
|
||||
x if x == QType::TXT as u8 => Ok(QType::TXT),
|
||||
x if x == QType::RP as u8 => Ok(QType::RP),
|
||||
x if x == QType::AAAA as u8 => Ok(QType::AAAA),
|
||||
x if x == QType::SRV as u8 => Ok(QType::SRV),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Copy, Clone)]
|
||||
enum QClass {
|
||||
Internet = 1,
|
||||
@ -24,6 +48,20 @@ enum QClass {
|
||||
Hesiod = 4,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for QClass {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(v: u8) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
x if x == QClass::Internet as u8 => Ok(QClass::Internet),
|
||||
x if x == QClass::Chaos as u8 => Ok(QClass::Chaos),
|
||||
x if x == QClass::Hesiod as u8 => Ok(QClass::Hesiod),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
|
||||
struct DNSQuestion {
|
||||
qname: String,
|
||||
qtype: QType,
|
||||
@ -42,16 +80,15 @@ impl DNSQuestion {
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8>
|
||||
{
|
||||
let mut ret: Vec<u8> = vec!();
|
||||
let mut ret: Vec<u8> = Vec::with_capacity(self.qname.len() + 2 + 3);
|
||||
|
||||
for part in self.qname.split(".")
|
||||
{
|
||||
let encoded_string: String = form_urlencoded::byte_serialize(part.as_bytes()).collect();
|
||||
let encoded_string = encode(part);
|
||||
let count = encoded_string.len();
|
||||
|
||||
ret.push(count as u8);
|
||||
ret.reserve(count);
|
||||
for x in encoded_string.into_bytes() {
|
||||
for x in encoded_string.bytes() {
|
||||
ret.push(x);
|
||||
};
|
||||
}
|
||||
@ -64,3 +101,199 @@ impl DNSQuestion {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
pub fn questions_from_bytes(bytes: Vec<u8>, total_questions: u8) -> Result<Vec<DNSQuestion>, ()>
|
||||
{
|
||||
if (bytes.len() < 4)
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut questions: Vec<DNSQuestion> = Vec::with_capacity(total_questions as usize);
|
||||
let mut current_query: Option<Vec<u8>> = None;
|
||||
|
||||
let mut current_length: Option<u8> = None;
|
||||
let mut remaining_length: Box<u8> = Box::from(0);
|
||||
let mut current_qtype: Option<u8> = None;
|
||||
let mut current_qclass: Option<u8> = None;
|
||||
let mut trailers_reached = false;
|
||||
|
||||
for byte in bytes {
|
||||
match current_length {
|
||||
None => { // next question, init lengths
|
||||
current_length = Some(byte);
|
||||
remaining_length = Box::from(byte);
|
||||
current_query = Some(Vec::with_capacity(10));
|
||||
}
|
||||
Some(_) => {
|
||||
if byte == 0 {
|
||||
trailers_reached = true;
|
||||
continue
|
||||
}
|
||||
|
||||
if *remaining_length == 0 && !trailers_reached {
|
||||
current_query.as_mut().unwrap().push('.' as u8);
|
||||
current_length = Some(byte);
|
||||
remaining_length = Box::from(byte);
|
||||
}
|
||||
else if trailers_reached { // trailer fields
|
||||
match current_qtype {
|
||||
None => {
|
||||
current_qtype = Some(byte);
|
||||
}
|
||||
Some(qtype_b) => {
|
||||
match current_qclass {
|
||||
None => {
|
||||
current_qclass = Some(byte);
|
||||
}
|
||||
Some(qclass_b) => {
|
||||
|
||||
match (qtype_b.try_into(), qclass_b.try_into()) {
|
||||
(Ok(qtype), Ok(qclass)) => {
|
||||
questions.push(DNSQuestion {
|
||||
qname: String::from_utf8(current_query.unwrap()).unwrap(),
|
||||
qtype,
|
||||
qclass
|
||||
});
|
||||
|
||||
current_length = Some(byte);
|
||||
remaining_length = Box::from(byte);
|
||||
current_query = Some(Vec::with_capacity(10));
|
||||
current_qtype = None;
|
||||
current_qclass = None;
|
||||
trailers_reached = false;
|
||||
}
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current_query.as_mut().unwrap().push(byte);
|
||||
*remaining_length = remaining_length.sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (current_qtype, current_qclass) {
|
||||
(Some(qtype), Some(qclass)) => {
|
||||
match (qtype.try_into(), qclass.try_into()) {
|
||||
(Ok(qtype), Ok(qclass)) => {
|
||||
questions.push(DNSQuestion {
|
||||
qname: String::from_utf8(current_query.unwrap()).unwrap(),
|
||||
qtype,
|
||||
qclass
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(questions)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn one_question_back_and_forth() {
|
||||
let q = DNSQuestion {
|
||||
qname: "google.com".to_string(),
|
||||
qclass: QClass::Internet,
|
||||
qtype: QType::A
|
||||
};
|
||||
|
||||
let q_bytes = q.to_bytes();
|
||||
|
||||
let q_reconstructed = 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_questions_back_and_forth() {
|
||||
let q = DNSQuestion {
|
||||
qname: "google.com".to_string(),
|
||||
qclass: QClass::Internet,
|
||||
qtype: QType::A
|
||||
};
|
||||
|
||||
let q2 = DNSQuestion {
|
||||
qname: "duck.com".to_string(),
|
||||
qclass: QClass::Internet,
|
||||
qtype: QType::AAAA
|
||||
};
|
||||
|
||||
let mut q_bytes = q.to_bytes();
|
||||
let mut q2_bytes = q2.to_bytes();
|
||||
|
||||
q_bytes.append(&mut q2_bytes);
|
||||
|
||||
let q_reconstructed = 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_questions_back_and_forth() {
|
||||
let q = DNSQuestion {
|
||||
qname: "google.com".to_string(),
|
||||
qclass: QClass::Internet,
|
||||
qtype: QType::A
|
||||
};
|
||||
|
||||
let q2 = DNSQuestion {
|
||||
qname: "duck.com".to_string(),
|
||||
qclass: QClass::Internet,
|
||||
qtype: QType::AAAA
|
||||
};
|
||||
|
||||
let q3 = DNSQuestion {
|
||||
qname: "facebook.com".to_string(),
|
||||
qclass: QClass::Hesiod,
|
||||
qtype: QType::CNAME
|
||||
};
|
||||
|
||||
let mut q_bytes = q.to_bytes();
|
||||
let mut q2_bytes = q2.to_bytes();
|
||||
let mut q3_bytes = q3.to_bytes();
|
||||
|
||||
q_bytes.append(&mut q2_bytes);
|
||||
q_bytes.append(&mut q3_bytes);
|
||||
|
||||
let q_reconstructed = 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_eq!(q3.qname, q_reconstructed[2].qname);
|
||||
assert_eq!(q3.qclass, q_reconstructed[2].qclass);
|
||||
assert_eq!(q3.qtype, q_reconstructed[2].qtype);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user