matc/
mdns.rs

1//! Very simple mdns client library
2
3use std::io::{Cursor, Read, Write};
4
5use anyhow::Result;
6
7use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
8use socket2::{Domain, Protocol, Type};
9
10pub const TYPE_A: u16 = 1;
11pub const TYPE_PTR: u16 = 12;
12pub const TYPE_TXT: u16 = 16;
13pub const TYPE_AAAA: u16 = 28;
14pub const TYPE_SRV: u16 = 33;
15pub const QTYPE_ANY: u16 = 0xff;
16
17fn encode_label(label: &str, out: &mut Vec<u8>) -> Result<()> {
18    for seg in label.split(".") {
19        let bytes = seg.as_bytes();
20        out.write_u8(bytes.len() as u8)?;
21        out.write_all(bytes)?;
22    }
23    out.write_u8(0)?;
24    Ok(())
25}
26
27fn create_query(label: &str, qtype: u16) -> Result<Vec<u8>> {
28    let mut out = Vec::with_capacity(512);
29    out.write_u16::<BigEndian>(0)?; // transaction id
30    out.write_u16::<BigEndian>(0)?; // flags
31    out.write_u16::<BigEndian>(1)?; // questions
32    out.write_u16::<BigEndian>(0)?; // answers
33    out.write_u16::<BigEndian>(0)?; // authority
34    out.write_u16::<BigEndian>(0)?; // additional
35
36    encode_label(label, &mut out)?;
37
38    out.write_u16::<BigEndian>(qtype)?;
39    out.write_u16::<BigEndian>(0x0001)?; // class
40    Ok(out)
41}
42
43fn read_label(data: &[u8], cursor: &mut Cursor<&[u8]>) -> Result<String> {
44    let mut out = Vec::new();
45    loop {
46        let n = cursor.read_u8()?;
47        if n == 0 {
48            break;
49        } else if n & 0xc0 == 0xc0 {
50            let off = {
51                let off = n & 0x3f;
52                ((off as usize) << 8) | (cursor.read_u8()? as u16) as usize
53            };
54            let frag = read_label(data, &mut Cursor::new(&data[off..]))?;
55            out.extend_from_slice(frag.as_bytes());
56            break;
57        } else {
58            let mut b = vec![0; n as usize];
59            cursor.read_exact(&mut b)?;
60            out.extend_from_slice(&b);
61            out.extend_from_slice(b".");
62        }
63    }
64    Ok(std::str::from_utf8(&out)?.to_owned())
65}
66
67#[derive(Debug, Eq, PartialEq, Hash)]
68pub struct RR {
69    pub name: String,
70    pub typ: u16,
71    pub class: u16,
72    pub ttl: u32,
73    pub rdata: Vec<u8>,
74}
75
76#[derive(Debug, Eq, PartialEq, Hash)]
77pub struct Query {
78    pub name: String,
79    pub typ: u16,
80    pub class: u16,
81}
82
83#[derive(Debug, Eq, PartialEq, Hash)]
84pub struct DnsMessage {
85    pub source: std::net::SocketAddr,
86    pub transaction: u16,
87    pub flags: u16,
88    pub queries: Vec<Query>,
89    pub answers: Vec<RR>,
90    pub authority: Vec<RR>,
91    pub additional: Vec<RR>,
92}
93
94impl RR {
95    pub fn dump(&self, indent: usize) {
96        println!(
97            "{} {} {}",
98            " ".to_owned().repeat(indent),
99            self.name,
100            self.typ
101        )
102    }
103}
104
105impl Query {
106    pub fn dump(&self, indent: usize) {
107        println!(
108            "{} {} {}",
109            " ".to_owned().repeat(indent),
110            self.name,
111            self.typ
112        )
113    }
114}
115
116impl DnsMessage {
117    pub fn dump(&self) {
118        println!("{:?} {} {:x}", self.source, self.transaction, self.flags);
119        println!("  queries:");
120        for queries in &self.queries {
121            queries.dump(4);
122        }
123        println!("  answers:");
124        for answer in &self.answers {
125            answer.dump(4);
126        }
127        println!("  authority:");
128        for authority in &self.authority {
129            authority.dump(4);
130        }
131        println!("  additional:");
132        for additional in &self.additional {
133            additional.dump(4);
134        }
135    }
136}
137
138fn parse_rr(data: &[u8], cursor: &mut Cursor<&[u8]>) -> Result<RR> {
139    let name = read_label(data, cursor)?;
140    let typ = cursor.read_u16::<BigEndian>()?;
141    let class = cursor.read_u16::<BigEndian>()?;
142    let ttl = cursor.read_u32::<BigEndian>()?;
143    let dlen = cursor.read_u16::<BigEndian>()?;
144    let mut rdata = vec![0; dlen as usize];
145    cursor.read_exact(&mut rdata)?;
146
147    Ok(RR {
148        name,
149        typ,
150        class,
151        ttl,
152        rdata,
153    })
154}
155
156fn parse_q(data: &[u8], cursor: &mut Cursor<&[u8]>) -> Result<Query> {
157    let name = read_label(data, cursor)?;
158    let typ = cursor.read_u16::<BigEndian>()?;
159    let class = cursor.read_u16::<BigEndian>()?;
160
161    Ok(Query { name, typ, class })
162}
163
164fn parse_dns(data: &[u8], source: std::net::SocketAddr) -> Result<DnsMessage> {
165    let mut cursor = Cursor::new(data);
166    let transaction = cursor.read_u16::<BigEndian>()?;
167    let flags = cursor.read_u16::<BigEndian>()?;
168    let nquestions = cursor.read_u16::<BigEndian>()?;
169    let nanswers = cursor.read_u16::<BigEndian>()?;
170    let nauthority = cursor.read_u16::<BigEndian>()?;
171    let nadditional = cursor.read_u16::<BigEndian>()?;
172
173    let mut queries = Vec::new();
174    let mut answers = Vec::new();
175    let mut additional = Vec::new();
176    let mut authority = Vec::new();
177
178    for _ in 0..nquestions {
179        queries.push(parse_q(data, &mut cursor)?);
180    }
181    for _ in 0..nanswers {
182        answers.push(parse_rr(data, &mut cursor)?);
183    }
184    for _ in 0..nauthority {
185        authority.push(parse_rr(data, &mut cursor)?);
186    }
187    for _ in 0..nadditional {
188        additional.push(parse_rr(data, &mut cursor)?);
189    }
190
191    Ok(DnsMessage {
192        source,
193        transaction,
194        flags,
195        queries,
196        answers,
197        authority,
198        additional,
199    })
200}
201
202async fn discoverv4(
203    label: &str,
204    qtype: u16,
205    sender: tokio::sync::mpsc::UnboundedSender<DnsMessage>,
206    cancel: tokio_util::sync::CancellationToken,
207) -> Result<()> {
208    let stdsocket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
209    stdsocket.set_reuse_address(true)?;
210    #[cfg(not(target_os = "windows"))]
211    stdsocket.set_reuse_port(true)?;
212    let addr: std::net::SocketAddrV4 = "0.0.0.0:5353".parse()?;
213    stdsocket.bind(&socket2::SockAddr::from(addr))?;
214    let maddr: std::net::Ipv4Addr = "224.0.0.251".parse()?;
215    stdsocket.join_multicast_v4(&maddr, &std::net::Ipv4Addr::UNSPECIFIED)?;
216    stdsocket.set_nonblocking(true)?;
217    let socket = tokio::net::UdpSocket::from_std(stdsocket.into())?;
218    let query = create_query(label, qtype)?;
219    socket.send_to(&query, "224.0.0.251:5353").await?;
220    loop {
221        let mut buf = vec![0; 1024];
222        //let (n, addr) = socket.recv_from(&mut buf).await?;
223        let (n, addr) = tokio::select! {
224            v = socket.recv_from(&mut buf) => v?,
225            _ = cancel.cancelled() => return Ok(())
226        };
227
228        buf.resize(n, 0);
229        let dns = parse_dns(&buf, addr)?;
230        if dns.flags == 0 {
231            // ignore requests
232            continue;
233        }
234        sender.send(dns)?;
235    }
236}
237
238async fn discoverv6(
239    label: &str,
240    qtype: u16,
241    interface: u32,
242    sender: tokio::sync::mpsc::UnboundedSender<DnsMessage>,
243    cancel: tokio_util::sync::CancellationToken,
244) -> Result<()> {
245    let stdsocket = socket2::Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
246    stdsocket.set_reuse_address(true)?;
247    #[cfg(not(target_os = "windows"))]
248    stdsocket.set_reuse_port(true)?;
249    let addr: std::net::SocketAddrV6 = "[::]:5353".parse()?;
250    stdsocket.bind(&socket2::SockAddr::from(addr))?;
251    let maddr: std::net::Ipv6Addr = "ff02::fb".parse()?;
252    stdsocket.join_multicast_v6(&maddr, interface)?;
253    stdsocket.set_multicast_if_v6(interface)?;
254    stdsocket.set_nonblocking(true)?;
255    let socket = tokio::net::UdpSocket::from_std(stdsocket.into())?;
256    let query = create_query(label, qtype)?;
257    socket.send_to(&query, "[ff02::fb]:5353").await?;
258    loop {
259        let mut buf = vec![0; 1024];
260        //let (n, addr) = socket.recv_from(&mut buf).await?;
261        let (n, addr) = tokio::select! {
262            v = socket.recv_from(&mut buf) => v?,
263            _ = cancel.cancelled() => return Ok(())
264        };
265        buf.resize(n, 0);
266        let dns = parse_dns(&buf, addr)?;
267        if dns.flags == 0 {
268            // ignore requests
269            continue;
270        }
271        sender.send(dns)?;
272    }
273}
274
275pub async fn discover(
276    label: &str,
277    qtype: u16,
278    sender: tokio::sync::mpsc::UnboundedSender<DnsMessage>,
279    stop: tokio_util::sync::CancellationToken,
280) -> Result<()> {
281    let ifaces = if_addrs::get_if_addrs();
282    if let Ok(ifaces) = ifaces {
283        for iface in ifaces {
284            let stop_child = stop.child_token();
285            if !iface.ip().is_ipv6() {
286                continue;
287            }
288            if let Some(index) = iface.index {
289                let sender2 = sender.clone();
290                let label = label.to_owned();
291                tokio::spawn(async move {
292                    _ = discoverv6(&label, qtype, index, sender2, stop_child).await;
293                });
294            }
295        }
296    };
297
298    let stop_child = stop.child_token();
299    let label = label.to_owned();
300    tokio::spawn(async move {
301        _ = discoverv4(&label, qtype, sender, stop_child).await;
302    });
303
304    Ok(())
305}