1use core::cmp::min;
2#[cfg(feature = "async")]
3use core::task::Waker;
4
5use heapless::Vec;
6use managed::ManagedSlice;
7
8use crate::config::{DNS_MAX_NAME_SIZE, DNS_MAX_RESULT_COUNT, DNS_MAX_SERVER_COUNT};
9use crate::socket::{Context, PollAt};
10use crate::time::{Duration, Instant};
11use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type};
12use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr};
13
14#[cfg(feature = "async")]
15use super::WakerRegistration;
16
17const DNS_PORT: u16 = 53;
18const MDNS_DNS_PORT: u16 = 5353;
19const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000);
20const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000);
21const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); #[cfg(feature = "proto-ipv6")]
24#[allow(unused)]
25const MDNS_IPV6_ADDR: IpAddress = IpAddress::Ipv6(crate::wire::Ipv6Address::new(
26 0xff02, 0, 0, 0, 0, 0, 0, 0xfb,
27));
28
29#[cfg(feature = "proto-ipv4")]
30#[allow(unused)]
31const MDNS_IPV4_ADDR: IpAddress = IpAddress::Ipv4(crate::wire::Ipv4Address::new(224, 0, 0, 251));
32
33#[derive(Debug, PartialEq, Eq, Clone, Copy)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum StartQueryError {
37 NoFreeSlot,
38 InvalidName,
39 NameTooLong,
40}
41
42impl core::fmt::Display for StartQueryError {
43 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44 match self {
45 StartQueryError::NoFreeSlot => write!(f, "No free slot"),
46 StartQueryError::InvalidName => write!(f, "Invalid name"),
47 StartQueryError::NameTooLong => write!(f, "Name too long"),
48 }
49 }
50}
51
52impl core::error::Error for StartQueryError {}
53
54#[derive(Debug, PartialEq, Eq, Clone, Copy)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57pub enum GetQueryResultError {
58 Pending,
60 Failed,
62}
63
64impl core::fmt::Display for GetQueryResultError {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 match self {
67 GetQueryResultError::Pending => write!(f, "Query is not done yet"),
68 GetQueryResultError::Failed => write!(f, "Query failed"),
69 }
70 }
71}
72
73impl core::error::Error for GetQueryResultError {}
74
75#[derive(Debug)]
80pub struct DnsQuery {
81 state: State,
82
83 #[cfg(feature = "async")]
84 waker: WakerRegistration,
85}
86
87impl DnsQuery {
88 fn set_state(&mut self, state: State) {
89 self.state = state;
90 #[cfg(feature = "async")]
91 self.waker.wake();
92 }
93}
94
95#[derive(Debug)]
96#[allow(clippy::large_enum_variant)]
97enum State {
98 Pending(PendingQuery),
99 Completed(CompletedQuery),
100 Failure,
101}
102
103#[derive(Debug)]
104struct PendingQuery {
105 name: Vec<u8, DNS_MAX_NAME_SIZE>,
106 type_: Type,
107
108 port: u16, txid: u16, timeout_at: Option<Instant>,
112 retransmit_at: Instant,
113 delay: Duration,
114
115 server_idx: usize,
116 mdns: MulticastDns,
117}
118
119#[derive(Debug)]
120pub enum MulticastDns {
121 Disabled,
122 #[cfg(feature = "socket-mdns")]
123 Enabled,
124}
125
126#[derive(Debug)]
127struct CompletedQuery {
128 addresses: Vec<IpAddress, DNS_MAX_RESULT_COUNT>,
129}
130
131#[derive(Clone, Copy)]
133pub struct QueryHandle(usize);
134
135#[derive(Debug)]
140pub struct Socket<'a> {
141 servers: Vec<IpAddress, DNS_MAX_SERVER_COUNT>,
142 queries: ManagedSlice<'a, Option<DnsQuery>>,
143
144 hop_limit: Option<u8>,
146}
147
148impl<'a> Socket<'a> {
149 pub fn new<Q>(servers: &[IpAddress], queries: Q) -> Socket<'a>
153 where
154 Q: Into<ManagedSlice<'a, Option<DnsQuery>>>,
155 {
156 let truncated_servers = &servers[..min(servers.len(), DNS_MAX_SERVER_COUNT)];
157
158 Socket {
159 servers: Vec::from_slice(truncated_servers).unwrap(),
160 queries: queries.into(),
161 hop_limit: None,
162 }
163 }
164
165 pub fn update_servers(&mut self, servers: &[IpAddress]) {
169 if servers.len() > DNS_MAX_SERVER_COUNT {
170 net_trace!("Max DNS Servers exceeded. Increase MAX_SERVER_COUNT");
171 self.servers = Vec::from_slice(&servers[..DNS_MAX_SERVER_COUNT]).unwrap();
172 } else {
173 self.servers = Vec::from_slice(servers).unwrap();
174 }
175 }
176
177 pub fn hop_limit(&self) -> Option<u8> {
181 self.hop_limit
182 }
183
184 pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {
196 if let Some(0) = hop_limit {
198 panic!("the time-to-live value of a packet must not be zero")
199 }
200
201 self.hop_limit = hop_limit
202 }
203
204 fn find_free_query(&mut self) -> Option<QueryHandle> {
205 for (i, q) in self.queries.iter().enumerate() {
206 if q.is_none() {
207 return Some(QueryHandle(i));
208 }
209 }
210
211 match &mut self.queries {
212 ManagedSlice::Borrowed(_) => None,
213 #[cfg(feature = "alloc")]
214 ManagedSlice::Owned(queries) => {
215 queries.push(None);
216 let index = queries.len() - 1;
217 Some(QueryHandle(index))
218 }
219 }
220 }
221
222 pub fn start_query(
228 &mut self,
229 cx: &mut Context,
230 name: &str,
231 query_type: Type,
232 ) -> Result<QueryHandle, StartQueryError> {
233 let mut name = name.as_bytes();
234
235 if name.is_empty() {
236 net_trace!("invalid name: zero length");
237 return Err(StartQueryError::InvalidName);
238 }
239
240 if name[name.len() - 1] == b'.' {
242 name = &name[..name.len() - 1];
243 }
244
245 let mut raw_name: Vec<u8, DNS_MAX_NAME_SIZE> = Vec::new();
246
247 let mut mdns = MulticastDns::Disabled;
248 #[cfg(feature = "socket-mdns")]
249 if name.split(|&c| c == b'.').next_back().unwrap() == b"local" {
250 net_trace!("Starting a mDNS query");
251 mdns = MulticastDns::Enabled;
252 }
253
254 for s in name.split(|&c| c == b'.') {
255 if s.len() > 63 {
256 net_trace!("invalid name: too long label");
257 return Err(StartQueryError::InvalidName);
258 }
259 if s.is_empty() {
260 net_trace!("invalid name: zero length label");
261 return Err(StartQueryError::InvalidName);
262 }
263
264 raw_name
266 .push(s.len() as u8)
267 .map_err(|_| StartQueryError::NameTooLong)?;
268 raw_name
269 .extend_from_slice(s)
270 .map_err(|_| StartQueryError::NameTooLong)?;
271 }
272
273 raw_name
275 .push(0x00)
276 .map_err(|_| StartQueryError::NameTooLong)?;
277
278 self.start_query_raw(cx, &raw_name, query_type, mdns)
279 }
280
281 pub fn start_query_raw(
286 &mut self,
287 cx: &mut Context,
288 raw_name: &[u8],
289 query_type: Type,
290 mdns: MulticastDns,
291 ) -> Result<QueryHandle, StartQueryError> {
292 let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?;
293
294 self.queries[handle.0] = Some(DnsQuery {
295 state: State::Pending(PendingQuery {
296 name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?,
297 type_: query_type,
298 txid: cx.rand().rand_u16(),
299 port: cx.rand().rand_source_port(),
300 delay: RETRANSMIT_DELAY,
301 timeout_at: None,
302 retransmit_at: Instant::ZERO,
303 server_idx: 0,
304 mdns,
305 }),
306 #[cfg(feature = "async")]
307 waker: WakerRegistration::new(),
308 });
309 Ok(handle)
310 }
311
312 pub fn get_query_result(
319 &mut self,
320 handle: QueryHandle,
321 ) -> Result<Vec<IpAddress, DNS_MAX_RESULT_COUNT>, GetQueryResultError> {
322 let slot = &mut self.queries[handle.0];
323 let q = slot.as_mut().unwrap();
324 match &mut q.state {
325 State::Pending(_) => Err(GetQueryResultError::Pending),
327 State::Completed(q) => {
329 let res = q.addresses.clone();
330 *slot = None; Ok(res)
332 }
333 State::Failure => {
334 *slot = None; Err(GetQueryResultError::Failed)
336 }
337 }
338 }
339
340 pub fn cancel_query(&mut self, handle: QueryHandle) {
346 let slot = &mut self.queries[handle.0];
347 if slot.is_none() {
348 panic!("Canceling query in a free slot.")
349 }
350 *slot = None; }
352
353 #[cfg(feature = "async")]
361 pub fn register_query_waker(&mut self, handle: QueryHandle, waker: &Waker) {
362 self.queries[handle.0]
363 .as_mut()
364 .unwrap()
365 .waker
366 .register(waker);
367 }
368
369 pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool {
370 (udp_repr.src_port == DNS_PORT
371 && self
372 .servers
373 .iter()
374 .any(|server| *server == ip_repr.src_addr()))
375 || (udp_repr.src_port == MDNS_DNS_PORT)
376 }
377
378 pub(crate) fn process(
379 &mut self,
380 _cx: &mut Context,
381 ip_repr: &IpRepr,
382 udp_repr: &UdpRepr,
383 payload: &[u8],
384 ) {
385 debug_assert!(self.accepts(ip_repr, udp_repr));
386
387 let size = payload.len();
388
389 net_trace!(
390 "receiving {} octets from {:?}:{}",
391 size,
392 ip_repr.src_addr(),
393 udp_repr.dst_port
394 );
395
396 let p = match Packet::new_checked(payload) {
397 Ok(x) => x,
398 Err(_) => {
399 net_trace!("dns packet malformed");
400 return;
401 }
402 };
403 if p.opcode() != Opcode::Query {
404 net_trace!("unwanted opcode {:?}", p.opcode());
405 return;
406 }
407
408 if !p.flags().contains(Flags::RESPONSE) {
409 net_trace!("packet doesn't have response bit set");
410 return;
411 }
412
413 if p.question_count() != 1 {
414 net_trace!("bad question count {:?}", p.question_count());
415 return;
416 }
417
418 for q in self.queries.iter_mut().flatten() {
420 if let State::Pending(pq) = &mut q.state {
421 if udp_repr.dst_port != pq.port || p.transaction_id() != pq.txid {
422 continue;
423 }
424
425 if p.rcode() == Rcode::NXDomain {
426 net_trace!("rcode NXDomain");
427 q.set_state(State::Failure);
428 continue;
429 }
430
431 let payload = p.payload();
432 let (mut payload, question) = match Question::parse(payload) {
433 Ok(x) => x,
434 Err(_) => {
435 net_trace!("question malformed");
436 return;
437 }
438 };
439
440 if question.type_ != pq.type_ {
441 net_trace!("question type mismatch");
442 return;
443 }
444
445 match eq_names(p.parse_name(question.name), p.parse_name(&pq.name)) {
446 Ok(true) => {}
447 Ok(false) => {
448 net_trace!("question name mismatch");
449 return;
450 }
451 Err(_) => {
452 net_trace!("dns question name malformed");
453 return;
454 }
455 }
456
457 let mut addresses = Vec::new();
458
459 for _ in 0..p.answer_record_count() {
460 let (payload2, r) = match Record::parse(payload) {
461 Ok(x) => x,
462 Err(_) => {
463 net_trace!("dns answer record malformed");
464 return;
465 }
466 };
467 payload = payload2;
468
469 match eq_names(p.parse_name(r.name), p.parse_name(&pq.name)) {
470 Ok(true) => {}
471 Ok(false) => {
472 net_trace!("answer name mismatch: {:?}", r);
473 continue;
474 }
475 Err(_) => {
476 net_trace!("dns answer record name malformed");
477 return;
478 }
479 }
480
481 match r.data {
482 #[cfg(feature = "proto-ipv4")]
483 RecordData::A(addr) => {
484 net_trace!("A: {:?}", addr);
485 if addresses.push(addr.into()).is_err() {
486 net_trace!("too many addresses in response, ignoring {:?}", addr);
487 }
488 }
489 #[cfg(feature = "proto-ipv6")]
490 RecordData::Aaaa(addr) => {
491 net_trace!("AAAA: {:?}", addr);
492 if addresses.push(addr.into()).is_err() {
493 net_trace!("too many addresses in response, ignoring {:?}", addr);
494 }
495 }
496 RecordData::Cname(name) => {
497 net_trace!("CNAME: {:?}", name);
498
499 if copy_name(&mut pq.name, p.parse_name(name)).is_err() {
507 net_trace!("dns answer cname malformed");
508 return;
509 }
510 }
511 RecordData::Other(type_, data) => {
512 net_trace!("unknown: {:?} {:?}", type_, data)
513 }
514 }
515 }
516
517 q.set_state(if addresses.is_empty() {
518 State::Failure
519 } else {
520 State::Completed(CompletedQuery { addresses })
521 });
522
523 return;
525 }
526 }
527
528 net_trace!("no query matched");
530 }
531
532 pub(crate) fn dispatch<F, E>(&mut self, cx: &mut Context, emit: F) -> Result<(), E>
533 where
534 F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>,
535 {
536 let hop_limit = self.hop_limit.unwrap_or(64);
537
538 for q in self.queries.iter_mut().flatten() {
539 if let State::Pending(pq) = &mut q.state {
540 let servers = match pq.mdns {
544 #[cfg(feature = "socket-mdns")]
545 MulticastDns::Enabled => &[
546 #[cfg(feature = "proto-ipv6")]
547 MDNS_IPV6_ADDR,
548 #[cfg(feature = "proto-ipv4")]
549 MDNS_IPV4_ADDR,
550 ],
551 MulticastDns::Disabled => self.servers.as_slice(),
552 };
553
554 let timeout = if let Some(timeout) = pq.timeout_at {
555 timeout
556 } else {
557 let v = cx.now() + RETRANSMIT_TIMEOUT;
558 pq.timeout_at = Some(v);
559 v
560 };
561
562 if timeout < cx.now() {
564 pq.timeout_at = Some(cx.now() + RETRANSMIT_TIMEOUT);
566 pq.retransmit_at = Instant::ZERO;
567 pq.delay = RETRANSMIT_DELAY;
568
569 pq.server_idx += 1;
571 }
572 if pq.server_idx >= servers.len() {
574 net_trace!("already tried all servers.");
575 q.set_state(State::Failure);
576 continue;
577 }
578
579 if servers[pq.server_idx].is_unspecified() {
581 net_trace!("invalid unspecified DNS server addr.");
582 q.set_state(State::Failure);
583 continue;
584 }
585
586 if pq.retransmit_at > cx.now() {
587 continue;
589 }
590
591 let repr = Repr {
592 transaction_id: pq.txid,
593 flags: Flags::RECURSION_DESIRED,
594 opcode: Opcode::Query,
595 question: Question {
596 name: &pq.name,
597 type_: pq.type_,
598 },
599 };
600
601 let mut payload = [0u8; 512];
602 let payload = &mut payload[..repr.buffer_len()];
603 repr.emit(&mut Packet::new_unchecked(payload));
604
605 let dst_port = match pq.mdns {
606 #[cfg(feature = "socket-mdns")]
607 MulticastDns::Enabled => MDNS_DNS_PORT,
608 MulticastDns::Disabled => DNS_PORT,
609 };
610
611 let udp_repr = UdpRepr {
612 src_port: pq.port,
613 dst_port,
614 };
615
616 let dst_addr = servers[pq.server_idx];
617 let src_addr = match cx.get_source_address(&dst_addr) {
618 Some(src_addr) => src_addr,
619 None => {
620 net_trace!("no source address for destination {}", dst_addr);
621 q.set_state(State::Failure);
622 continue;
623 }
624 };
625
626 let ip_repr = IpRepr::new(
627 src_addr,
628 dst_addr,
629 IpProtocol::Udp,
630 udp_repr.header_len() + payload.len(),
631 hop_limit,
632 );
633
634 net_trace!(
635 "sending {} octets to {} from port {}",
636 payload.len(),
637 ip_repr.dst_addr(),
638 udp_repr.src_port
639 );
640
641 emit(cx, (ip_repr, udp_repr, payload))?;
642
643 pq.retransmit_at = cx.now() + pq.delay;
644 pq.delay = MAX_RETRANSMIT_DELAY.min(pq.delay * 2);
645
646 return Ok(());
647 }
648 }
649
650 Ok(())
652 }
653
654 pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
655 self.queries
656 .iter()
657 .flatten()
658 .filter_map(|q| match &q.state {
659 State::Pending(pq) => Some(PollAt::Time(pq.retransmit_at)),
660 State::Completed(_) => None,
661 State::Failure => None,
662 })
663 .min()
664 .unwrap_or(PollAt::Ingress)
665 }
666}
667
668fn eq_names<'a>(
669 mut a: impl Iterator<Item = wire::Result<&'a [u8]>>,
670 mut b: impl Iterator<Item = wire::Result<&'a [u8]>>,
671) -> wire::Result<bool> {
672 loop {
673 match (a.next(), b.next()) {
674 (Some(Err(e)), _) => return Err(e),
676 (_, Some(Err(e))) => return Err(e),
677
678 (None, None) => return Ok(true),
680
681 (None, _) => return Ok(false),
683 (_, None) => return Ok(false),
684
685 (Some(Ok(la)), Some(Ok(lb))) => {
687 if la != lb {
688 return Ok(false);
689 }
690 }
691 }
692 }
693}
694
695fn copy_name<'a, const N: usize>(
696 dest: &mut Vec<u8, N>,
697 name: impl Iterator<Item = wire::Result<&'a [u8]>>,
698) -> Result<(), wire::Error> {
699 dest.truncate(0);
700
701 for label in name {
702 let label = label?;
703 dest.push(label.len() as u8).map_err(|_| wire::Error)?;
704 dest.extend_from_slice(label).map_err(|_| wire::Error)?;
705 }
706
707 dest.push(0).map_err(|_| wire::Error)?;
709
710 Ok(())
711}