WIP: Writing a mdns parser

This commit is contained in:
Ishan Jain 2023-11-21 03:23:00 +05:30
parent c778de9bf9
commit 55ee873280
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
10 changed files with 365 additions and 252 deletions

237
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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();

View File

@ -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
View 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
View 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
View 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
View 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];
}
}
}
}
}

View File

@ -0,0 +1,9 @@
#[derive(Debug, Default)]
pub struct ResourceRecord {
name: String,
rtype: u16,
class: u16,
ttl: u16,
rdlength: u16,
rdata: (),
}

View File

@ -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 {