Repo of no-std crates for my personal embedded projects
1use alloc::vec::Vec;
2use sachy_fmt::{error, info};
3
4use crate::{
5 dns::{
6 flags::Flags,
7 query::{Answer, QClass, Query},
8 records::QType,
9 reqres::{Request, Response},
10 traits::DnsParse,
11 },
12 encoder::Encoder,
13 service::Service,
14};
15
16pub(crate) enum ResponseKind {
17 Announcement,
18 QueryResponse(Vec<(QType, QClass)>),
19}
20
21#[derive(Debug)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub(crate) struct Server {
24 service: Service,
25}
26
27impl Server {
28 pub(crate) fn new(service: Service) -> Self {
29 Self { service }
30 }
31
32 pub(crate) fn broadcast<'a, 'b>(
33 &self,
34 response_kind: ResponseKind,
35 flags: Flags,
36 id: u16,
37 queries: Vec<Query<'b>>,
38 outgoing: &'a mut [u8],
39 ) -> Option<&'a [u8]> {
40 let answers: Vec<_> = match response_kind {
41 ResponseKind::Announcement => self.service.as_answers(QClass::Multicast).collect(),
42 ResponseKind::QueryResponse(valid) => valid
43 .iter()
44 .flat_map(|&(qtype, qclass)| match qtype {
45 QType::A | QType::AAAA => self.service.ip_answer(qclass),
46 QType::PTR => self.service.ptr_answer(qclass),
47 QType::TXT => self.service.txt_answer(qclass),
48 QType::SRV => self.service.srv_answer(qclass),
49 QType::Any | QType::Unknown(_) => None,
50 })
51 .collect(),
52 };
53
54 let additional = match &answers[..] {
55 [] => return None,
56 [
57 Answer {
58 atype: QType::PTR,
59 aclass,
60 ..
61 },
62 ] => self.service.as_additional(*aclass).collect(),
63 _ => Vec::new(),
64 };
65
66 let res = Response {
67 flags,
68 id,
69 queries,
70 answers,
71 additional,
72 };
73
74 info!("MDNS RESPONSE: {}", res);
75
76 Encoder::new(outgoing)
77 .encode(res)
78 .inspect_err(|err| error!("Encoder errored: {}", err))
79 .ok()
80 }
81
82 pub(crate) fn respond<'a>(&self, incoming: &[u8], outgoing: &'a mut [u8]) -> Option<&'a [u8]> {
83 Request::parse(&mut &incoming[..], incoming)
84 .ok()
85 .and_then(|req| {
86 let valid_queries =
87 req.queries
88 .iter()
89 .filter_map(|q| match q.qtype {
90 QType::A | QType::AAAA | QType::TXT | QType::SRV => {
91 (q.name == self.service.hostname()).then_some((q.qtype, q.qclass))
92 }
93 QType::PTR => (q.name == self.service.service_type())
94 .then_some((q.qtype, q.qclass)),
95 QType::Any | QType::Unknown(_) => None,
96 })
97 .collect::<Vec<_>>();
98
99 if !valid_queries.is_empty() {
100 self.broadcast(
101 ResponseKind::QueryResponse(valid_queries),
102 req.flags,
103 req.id,
104 req.queries,
105 outgoing,
106 )
107 } else {
108 None
109 }
110 })
111 }
112}