1use core::net::{Ipv4Addr, Ipv6Addr};
2
3use crate::domain::base::{iana::Class, Record, Ttl};
4use crate::domain::rdata::{Aaaa, AllRecordData, Ptr, Srv, A};
5
6use crate::{HostAnswer, HostAnswers, MdnsError, NameSlice, RecordDataChain, Txt, DNS_SD_OWNER};
7
8#[derive(Debug, Clone)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct Host<'a> {
15 pub hostname: &'a str,
17 pub ipv4: Ipv4Addr,
20 pub ipv6: Ipv6Addr,
23 #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
25 pub ttl: Ttl,
26}
27
28impl Host<'_> {
29 fn visit_answers<F, E>(&self, mut f: F) -> Result<(), E>
30 where
31 F: FnMut(HostAnswer) -> Result<(), E>,
32 E: From<MdnsError>,
33 {
34 let owner = &[self.hostname, "local"];
35
36 if !self.ipv4.is_unspecified() {
37 f(Record::new(
38 NameSlice::new(owner),
39 Class::IN,
40 self.ttl,
41 RecordDataChain::Next(AllRecordData::A(A::new(domain::base::net::Ipv4Addr::from(
42 self.ipv4.octets(),
43 )))),
44 ))?;
45 }
46
47 if !self.ipv6.is_unspecified() {
48 f(Record::new(
49 NameSlice::new(owner),
50 Class::IN,
51 self.ttl,
52 RecordDataChain::Next(AllRecordData::Aaaa(Aaaa::new(
53 domain::base::net::Ipv6Addr::from(self.ipv6.octets()),
54 ))),
55 ))?;
56 }
57
58 Ok(())
59 }
60}
61
62impl HostAnswers for Host<'_> {
63 fn visit<F, E>(&self, mut f: F) -> Result<(), E>
64 where
65 F: FnMut(HostAnswer) -> Result<(), E>,
66 E: From<MdnsError>,
67 {
68 self.visit_answers(&mut f)
69 }
70}
71
72#[derive(Debug, Clone)]
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79pub struct Service<'a> {
80 pub name: &'a str,
82 pub priority: u16,
84 pub weight: u16,
86 pub service: &'a str,
88 pub protocol: &'a str,
90 pub port: u16,
92 pub service_subtypes: &'a [&'a str],
94 pub txt_kvs: &'a [(&'a str, &'a str)],
96}
97
98impl Service<'_> {
99 fn visit_answers<F, E>(&self, host: &Host, mut f: F) -> Result<(), E>
100 where
101 F: FnMut(HostAnswer) -> Result<(), E>,
102 E: From<MdnsError>,
103 {
104 host.visit_answers(&mut f)?;
105
106 let owner = &[self.name, self.service, self.protocol, "local"];
107 let stype = &[self.service, self.protocol, "local"];
108 let target = &[host.hostname, "local"];
109
110 f(Record::new(
111 NameSlice::new(owner),
112 Class::IN,
113 host.ttl,
114 RecordDataChain::Next(AllRecordData::Srv(Srv::new(
115 self.priority,
116 self.weight,
117 self.port,
118 NameSlice::new(target),
119 ))),
120 ))?;
121
122 f(Record::new(
123 NameSlice::new(owner),
124 Class::IN,
125 host.ttl,
126 RecordDataChain::This(Txt::new(self.txt_kvs)),
127 ))?;
128
129 f(Record::new(
130 DNS_SD_OWNER,
131 Class::IN,
132 host.ttl,
133 RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(stype)))),
134 ))?;
135
136 f(Record::new(
137 NameSlice::new(stype),
138 Class::IN,
139 host.ttl,
140 RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(owner)))),
141 ))?;
142
143 for subtype in self.service_subtypes {
144 let subtype_owner = &[subtype, self.name, self.service, self.protocol, "local"];
145 let subtype = &[subtype, "_sub", self.service, self.protocol, "local"];
146
147 f(Record::new(
148 NameSlice::new(subtype_owner),
149 Class::IN,
150 host.ttl,
151 RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(owner)))),
152 ))?;
153
154 f(Record::new(
155 NameSlice::new(subtype),
156 Class::IN,
157 host.ttl,
158 RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(subtype_owner)))),
159 ))?;
160
161 f(Record::new(
162 DNS_SD_OWNER,
163 Class::IN,
164 host.ttl,
165 RecordDataChain::Next(AllRecordData::Ptr(Ptr::new(NameSlice::new(subtype)))),
166 ))?;
167 }
168
169 Ok(())
170 }
171}
172
173pub struct ServiceAnswers<'a> {
176 host: &'a Host<'a>,
177 service: &'a Service<'a>,
178}
179
180impl<'a> ServiceAnswers<'a> {
181 pub const fn new(host: &'a Host<'a>, service: &'a Service<'a>) -> Self {
183 Self { host, service }
184 }
185}
186
187impl HostAnswers for ServiceAnswers<'_> {
188 fn visit<F, E>(&self, mut f: F) -> Result<(), E>
189 where
190 F: FnMut(HostAnswer) -> Result<(), E>,
191 E: From<MdnsError>,
192 {
193 self.service.visit_answers(self.host, &mut f)
194 }
195}