WIP: Writing a mdns parser
This commit is contained in:
parent
c778de9bf9
commit
55ee873280
237
Cargo.lock
generated
237
Cargo.lock
generated
|
@ -2,17 +2,6 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.2"
|
||||
|
@ -42,9 +31,9 @@ checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
|||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "c_linked_list"
|
||||
|
@ -64,73 +53,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset 0.9.0",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dns-parser"
|
||||
version = "0.8.0"
|
||||
|
@ -141,19 +63,6 @@ dependencies = [
|
|||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endorphin"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2dc87c62efee2c20e9287b6421800c02b56551b3cc5ee9af1ac9336e8173ebd"
|
||||
dependencies = [
|
||||
"crossbeam",
|
||||
"hashbrown 0.11.2",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"slotmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
|
@ -222,26 +131,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
|
@ -267,16 +156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -302,16 +182,6 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
|
@ -333,28 +203,20 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multicaster"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dns-parser",
|
||||
"endorphin",
|
||||
"env_logger",
|
||||
"get_if_addrs",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
|
@ -367,7 +229,7 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.7.1",
|
||||
"memoffset",
|
||||
"pin-utils",
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -378,31 +240,6 @@ version = "1.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
|
@ -433,15 +270,6 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.1"
|
||||
|
@ -484,12 +312,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.177"
|
||||
|
@ -519,21 +341,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.3"
|
||||
|
@ -570,6 +377,26 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.6"
|
||||
|
@ -610,18 +437,6 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
|
|
@ -5,12 +5,13 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
dns-parser = "0.8.0"
|
||||
endorphin = "0.1.9"
|
||||
env_logger = "0.10.0"
|
||||
get_if_addrs = "0.5.3"
|
||||
libc = "0.2.147"
|
||||
log = "0.4.18"
|
||||
nix = { version = "0.26.2", features = ["net"] }
|
||||
once_cell = "1.18.0"
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
socket2 = { version = "0.5.3", features = ["all"] }
|
||||
thiserror = "1.0.50"
|
||||
toml = "0.7.4"
|
||||
|
|
|
@ -9,7 +9,9 @@ pub mod config;
|
|||
pub use config::*;
|
||||
pub mod mdns;
|
||||
pub use mdns::*;
|
||||
mod parser;
|
||||
mod socket;
|
||||
pub use parser::*;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::Config;
|
|||
use dns_parser::Packet;
|
||||
use log::{info, trace, warn};
|
||||
use nix::errno::Errno;
|
||||
use std::net::{Ipv4Addr, SocketAddrV6};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{ffi::CString, net::SocketAddrV4};
|
||||
|
||||
pub struct Mdns {
|
||||
|
@ -20,7 +20,7 @@ impl Mdns {
|
|||
MulticastOptions::default(),
|
||||
MulticastSocket::all_interfaces().unwrap(),
|
||||
MulticastGroup {
|
||||
ipv4: Some(SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 251), 5353)),
|
||||
ipv4: SocketAddrV4::new(Ipv4Addr::new(224, 0, 0, 251), 5353),
|
||||
port: 5353,
|
||||
},
|
||||
)
|
13
src/parser/error.rs
Normal file
13
src/parser/error.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ParserError {
|
||||
#[error("Failed to parse DNS Header. Error: {0}")]
|
||||
HeaderError(&'static str),
|
||||
|
||||
#[error("Unexpected end of packet")]
|
||||
UnexpectedEOP,
|
||||
|
||||
#[error("Label is not UTF-8")]
|
||||
LabelIsNotUTF8,
|
||||
}
|
66
src/parser/header.rs
Normal file
66
src/parser/header.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use crate::ParserError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Header {
|
||||
pub id: u16,
|
||||
pub fields: u16,
|
||||
pub qd_count: u16,
|
||||
pub an_count: u16,
|
||||
pub ns_count: u16,
|
||||
pub ar_count: u16,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
|
||||
if data.len() < 12 {
|
||||
return Err(ParserError::HeaderError(
|
||||
"input length is less than 20 bytes",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Header {
|
||||
id: u16::from_be_bytes([data[0], data[1]]),
|
||||
fields: u16::from_be_bytes([data[2], data[3]]),
|
||||
qd_count: u16::from_be_bytes([data[4], data[5]]),
|
||||
an_count: u16::from_be_bytes([data[6], data[7]]),
|
||||
ns_count: u16::from_be_bytes([data[8], data[9]]),
|
||||
ar_count: u16::from_be_bytes([data[10], data[11]]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn qr(&self) -> bool {
|
||||
((self.fields >> 15) & 1) == 1
|
||||
}
|
||||
|
||||
pub fn opcode(&self) -> u8 {
|
||||
((self.fields >> 11) & 0b01111) as u8
|
||||
}
|
||||
|
||||
pub fn aa(&self) -> bool {
|
||||
((self.fields >> 10) & 1) == 1
|
||||
}
|
||||
|
||||
pub fn tc(&self) -> bool {
|
||||
((self.fields >> 9) & 1) == 1
|
||||
}
|
||||
|
||||
pub fn rd(&self) -> bool {
|
||||
((self.fields >> 8) & 1) == 1
|
||||
}
|
||||
|
||||
pub fn ra(&self) -> bool {
|
||||
((self.fields >> 7) & 1) == 1
|
||||
}
|
||||
|
||||
pub fn z(&self) -> u8 {
|
||||
((self.fields >> 4) & 0b111) as u8
|
||||
}
|
||||
|
||||
pub fn rcode(&self) -> u8 {
|
||||
(self.fields & 0b111) as u8
|
||||
}
|
||||
|
||||
pub fn size() -> usize {
|
||||
12
|
||||
}
|
||||
}
|
146
src/parser/mod.rs
Normal file
146
src/parser/mod.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
mod header;
|
||||
pub use header::*;
|
||||
mod error;
|
||||
pub use error::*;
|
||||
mod question;
|
||||
pub use question::*;
|
||||
mod resource_record;
|
||||
pub use resource_record::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DnsPacket {
|
||||
pub header: Header,
|
||||
pub questions: Vec<Question>,
|
||||
pub answers: Vec<ResourceRecord>,
|
||||
// pub authority: Authority,
|
||||
// pub additional: Additional,
|
||||
}
|
||||
|
||||
impl DnsPacket {
|
||||
pub fn parse(data: &[u8]) -> Result<Self, ParserError> {
|
||||
let header = Header::parse(data)?;
|
||||
let (questions, read) = DnsPacket::parse_questions(&header, data)?;
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
questions,
|
||||
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_questions(
|
||||
header: &Header,
|
||||
data: &[u8],
|
||||
) -> Result<(Vec<Question>, usize), ParserError> {
|
||||
let mut out = Vec::with_capacity(header.qd_count as usize);
|
||||
let mut offset = Header::size();
|
||||
|
||||
for _ in 0..header.qd_count {
|
||||
let (q, read) = Question::parse(&data[offset..], data)?;
|
||||
|
||||
offset += read;
|
||||
out.push(q);
|
||||
}
|
||||
|
||||
Ok((out, offset))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_header() {
|
||||
let header = Header::parse(&[
|
||||
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03,
|
||||
])
|
||||
.expect("error in parsing input");
|
||||
|
||||
assert_eq!(header.id, 0);
|
||||
assert!(header.qr());
|
||||
assert_eq!(header.opcode(), 0);
|
||||
assert!(header.aa());
|
||||
assert!(!header.tc());
|
||||
assert!(!header.rd());
|
||||
assert!(!header.ra());
|
||||
assert_eq!(header.z(), 0);
|
||||
assert_eq!(header.rcode(), 0);
|
||||
|
||||
assert_eq!(header.qd_count, 0);
|
||||
assert_eq!(header.an_count, 5);
|
||||
assert_eq!(header.ns_count, 0);
|
||||
assert_eq!(header.ar_count, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_questions() {
|
||||
let packet = DnsPacket::parse(&[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x5f,
|
||||
0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x69, 0x6e, 0x6b,
|
||||
0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c,
|
||||
0x00, 0x01, 0x07, 0x5f, 0x72, 0x64, 0x6c, 0x69, 0x6e, 0x6b, 0xc0, 0x1c, 0x00, 0x0c,
|
||||
0x00, 0x01, 0x0c, 0x5f, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x04, 0x5f, 0x75, 0x64, 0x70, 0xc0, 0x21, 0x00, 0x0c, 0x00, 0x01, 0xc0, 0x0c,
|
||||
0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x11, 0x93, 0x00, 0x11, 0x0e, 0x49, 0x73, 0x68,
|
||||
0x61, 0x6e, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x69, 0x50, 0x61, 0x64, 0xc0, 0x0c,
|
||||
])
|
||||
.expect("error in parsing input");
|
||||
|
||||
let header = packet.header;
|
||||
|
||||
assert_eq!(header.id, 0);
|
||||
assert!(!header.qr());
|
||||
assert_eq!(header.opcode(), 0);
|
||||
assert!(!header.aa());
|
||||
assert!(!header.tc());
|
||||
assert!(!header.rd());
|
||||
assert!(!header.ra());
|
||||
assert_eq!(header.z(), 0);
|
||||
assert_eq!(header.rcode(), 0);
|
||||
|
||||
assert_eq!(header.qd_count, 3);
|
||||
assert_eq!(header.an_count, 1);
|
||||
assert_eq!(header.ns_count, 0);
|
||||
assert_eq!(header.ar_count, 0);
|
||||
|
||||
assert_eq!(packet.questions.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_samples() {
|
||||
let inputs = [
|
||||
vec![
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0e, 0x49,
|
||||
0x73, 0x68, 0x61, 0x6e, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x69, 0x50, 0x61, 0x64, 0x0f,
|
||||
0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x69, 0x6e,
|
||||
0x6b, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00,
|
||||
0xff, 0x80, 0x01, 0x0b, 0x49, 0x73, 0x68, 0x61, 0x6e, 0x73, 0x2d, 0x69, 0x50, 0x61,
|
||||
0x64, 0xc0, 0x30, 0x00, 0xff, 0x80, 0x01, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x78, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xda, 0x42, 0xc0, 0x3b, 0xc0,
|
||||
0x3b, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x10, 0xfe, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x67, 0xf6, 0x62, 0x5e, 0xde, 0x9b, 0x3d, 0xc0,
|
||||
0x3b, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x10, 0x2a, 0x0a, 0x60,
|
||||
0x40, 0x40, 0x04, 0x00, 0x10, 0x04, 0x83, 0x64, 0x0f, 0x03, 0x51, 0x2c, 0x9c, 0xc0,
|
||||
0x3b, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0x0a, 0x00, 0x0a,
|
||||
0x32,
|
||||
],
|
||||
vec![
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x5f,
|
||||
0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x69, 0x6e, 0x6b,
|
||||
0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c,
|
||||
0x00, 0x01, 0x07, 0x5f, 0x72, 0x64, 0x6c, 0x69, 0x6e, 0x6b, 0xc0, 0x1c, 0x00, 0x0c,
|
||||
0x00, 0x01, 0x0c, 0x5f, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x04, 0x5f, 0x75, 0x64, 0x70, 0xc0, 0x21, 0x00, 0x0c, 0x00, 0x01, 0xc0, 0x0c,
|
||||
0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x11, 0x93, 0x00, 0x11, 0x0e, 0x49, 0x73, 0x68,
|
||||
0x61, 0x6e, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x69, 0x50, 0x61, 0x64, 0xc0, 0x0c,
|
||||
],
|
||||
];
|
||||
|
||||
for input in inputs.iter() {
|
||||
assert!(DnsPacket::parse(input).is_ok());
|
||||
}
|
||||
}
|
||||
}
|
86
src/parser/question.rs
Normal file
86
src/parser/question.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use crate::ParserError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Question {
|
||||
pub qname: String,
|
||||
pub qtype: u16,
|
||||
pub unicast_preferred: bool,
|
||||
pub qclass: u16,
|
||||
}
|
||||
|
||||
impl Question {
|
||||
pub fn parse(data: &[u8], original: &[u8]) -> Result<(Self, usize), ParserError> {
|
||||
let (qname, mut read) = Self::read_label(data, original)?;
|
||||
|
||||
let qtype = u16::from_be_bytes([data[read], data[read + 1]]);
|
||||
read += 2;
|
||||
let mut qclass = u16::from_be_bytes([data[read], data[read + 1]]);
|
||||
read += 2;
|
||||
|
||||
let unicast_preferred = (qclass & (1 << 15)) == 1 << 15;
|
||||
qclass &= !(1 << 15);
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
qname,
|
||||
qtype,
|
||||
unicast_preferred,
|
||||
qclass,
|
||||
},
|
||||
read,
|
||||
))
|
||||
}
|
||||
|
||||
fn read_label(data: &[u8], original: &[u8]) -> Result<(String, usize), ParserError> {
|
||||
let mut parse_data = data;
|
||||
let mut out = Vec::new();
|
||||
let mut offset = 0;
|
||||
let mut byte = parse_data[offset];
|
||||
let mut bytes_read = 1;
|
||||
|
||||
let mut return_pos: Option<usize> = None;
|
||||
|
||||
loop {
|
||||
match byte {
|
||||
0 => {
|
||||
out.pop();
|
||||
|
||||
return Ok((
|
||||
String::from_utf8(out).map_err(|_| ParserError::LabelIsNotUTF8)?,
|
||||
if let Some(p) = return_pos {
|
||||
p + 2
|
||||
} else {
|
||||
bytes_read
|
||||
},
|
||||
));
|
||||
}
|
||||
0xc0 => {
|
||||
let nof = parse_data[offset + 1] as usize;
|
||||
if nof >= original.len() {
|
||||
return Err(ParserError::UnexpectedEOP);
|
||||
}
|
||||
|
||||
return_pos = match return_pos {
|
||||
Some(x) => Some(std::cmp::max(x, offset)),
|
||||
None => Some(offset),
|
||||
};
|
||||
|
||||
offset = 0;
|
||||
parse_data = &original[nof..];
|
||||
byte = parse_data[0];
|
||||
}
|
||||
|
||||
_ => {
|
||||
out.extend(&parse_data[offset + 1..offset + 1 + byte as usize]);
|
||||
|
||||
out.push(b'.');
|
||||
|
||||
offset += byte as usize + 1;
|
||||
bytes_read += byte as usize + 1;
|
||||
|
||||
byte = parse_data[offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/parser/resource_record.rs
Normal file
9
src/parser/resource_record.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#[derive(Debug, Default)]
|
||||
pub struct ResourceRecord {
|
||||
name: String,
|
||||
rtype: u16,
|
||||
class: u16,
|
||||
ttl: u16,
|
||||
rdlength: u16,
|
||||
rdata: (),
|
||||
}
|
|
@ -49,15 +49,7 @@ impl MulticastSocket {
|
|||
interfaces: HashMap<String, Vec<IpAddr>>,
|
||||
multicast_group: MulticastGroup,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
// We want to accept traffic on IPv4 and IPv6.
|
||||
// and I don't want to deal with 2 sockets. 1 for ipv4 and 1 for ipv6
|
||||
// so we do this
|
||||
// Create a single Ipv6 socket and disable IPV6_ONLY option
|
||||
// This is already disabled on new OSes but just to be safe we do it any way
|
||||
// With this, We can accept IPv6 traffic _and_ we can accept IPv4 traffic
|
||||
// except the address for IPv4 will be presented within IPv6
|
||||
|
||||
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
|
||||
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
|
||||
socket.set_read_timeout(Some(options.read_timeout))?;
|
||||
socket.set_multicast_loop_v4(false)?;
|
||||
socket.set_reuse_address(true)?;
|
||||
|
@ -68,52 +60,35 @@ impl MulticastSocket {
|
|||
// setting this option allows for determining on which interface a packet was received.
|
||||
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv4PacketInfo, &true)
|
||||
.map_err(nix_to_io_error)?;
|
||||
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv6RecvPacketInfo, &true)
|
||||
.map_err(nix_to_io_error)?;
|
||||
|
||||
// Receive IPv4 traffic on IPv6 socket
|
||||
sock::setsockopt(socket.as_raw_fd(), sock::sockopt::Ipv6V6Only, &false)
|
||||
.map_err(nix_to_io_error);
|
||||
|
||||
for (if_name, addresses) in interfaces.iter() {
|
||||
trace!(
|
||||
"joining groups if_name = {} addresses = {:?}",
|
||||
if_name,
|
||||
addresses
|
||||
"joining groups = {:?} interface = {:?}",
|
||||
multicast_group.ipv4.ip(),
|
||||
Ipv4Addr::UNSPECIFIED
|
||||
);
|
||||
|
||||
if addresses.iter().any(|addr| addr.is_ipv6()) {
|
||||
trace!(
|
||||
"joined ipv6 multicast group {} if_name {}",
|
||||
multicast_group.ipv6.ip(),
|
||||
if_name
|
||||
);
|
||||
socket.join_multicast_v4(multicast_group.ipv4.ip(), &Ipv4Addr::UNSPECIFIED);
|
||||
|
||||
socket.join_multicast_v6(
|
||||
multicast_group.ipv6.ip(),
|
||||
ifname_to_ifidx(if_name.to_string()),
|
||||
);
|
||||
}
|
||||
for address in addresses {
|
||||
if let IpAddr::V4(v4_addr) = address {
|
||||
if v4_addr.is_loopback() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(ipv4_mdns_group) = multicast_group.ipv4 {
|
||||
trace!(
|
||||
"joined ipv4 multicast group {} {}",
|
||||
ipv4_mdns_group.ip(),
|
||||
v4_addr
|
||||
);
|
||||
socket.join_multicast_v4(ipv4_mdns_group.ip(), v4_addr)?;
|
||||
}
|
||||
socket.join_multicast_v4(multicast_group.ipv4.ip(), v4_addr)?;
|
||||
|
||||
trace!(
|
||||
"joined ipv4 multicast group {} {}",
|
||||
multicast_group.ipv4.ip(),
|
||||
v4_addr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
socket.bind(
|
||||
&SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), multicast_group.port).into(),
|
||||
&SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), multicast_group.port).into(),
|
||||
)?;
|
||||
|
||||
Ok(MulticastSocket {
|
||||
|
|
Loading…
Reference in New Issue
Block a user