1#![cfg_attr(not(feature = "std"), no_std)]
2#![warn(clippy::large_futures)]
3#![allow(clippy::uninlined_format_args)]
4#![allow(unknown_lints)]
5
6use core::fmt::Display;
7use core::time::Duration;
8
9use domain::base::wire::Composer;
10use domain::dep::octseq::{OctetsBuilder, Truncate};
11
12use domain::{
13 base::{
14 iana::{Class, Opcode, Rcode},
15 message::ShortMessage,
16 message_builder::PushError,
17 record::Ttl,
18 wire::ParseError,
19 Record, Rtype,
20 },
21 dep::octseq::ShortBuf,
22 rdata::A,
23};
24
25pub(crate) mod fmt;
27
28#[cfg(feature = "io")]
29pub mod io;
30
31#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
32pub enum DnsError {
33 ShortBuf,
34 InvalidMessage,
35}
36
37impl Display for DnsError {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 match self {
40 Self::ShortBuf => write!(f, "ShortBuf"),
41 Self::InvalidMessage => write!(f, "InvalidMessage"),
42 }
43 }
44}
45
46#[cfg(feature = "defmt")]
47impl defmt::Format for DnsError {
48 fn format(&self, f: defmt::Formatter<'_>) {
49 match self {
50 Self::ShortBuf => defmt::write!(f, "ShortBuf"),
51 Self::InvalidMessage => defmt::write!(f, "InvalidMessage"),
52 }
53 }
54}
55
56impl core::error::Error for DnsError {}
57
58impl From<ShortBuf> for DnsError {
59 fn from(_: ShortBuf) -> Self {
60 Self::ShortBuf
61 }
62}
63
64impl From<PushError> for DnsError {
65 fn from(_: PushError) -> Self {
66 Self::ShortBuf
67 }
68}
69
70impl From<ShortMessage> for DnsError {
71 fn from(_: ShortMessage) -> Self {
72 Self::InvalidMessage
73 }
74}
75
76impl From<ParseError> for DnsError {
77 fn from(_: ParseError) -> Self {
78 Self::InvalidMessage
79 }
80}
81
82pub fn reply(
83 request: &[u8],
84 ip: &[u8; 4],
85 ttl: Duration,
86 buf: &mut [u8],
87) -> Result<usize, DnsError> {
88 let buf = Buf(buf, 0);
89
90 let message = domain::base::Message::from_octets(request)?;
91 debug!(
92 "Processing message with header: {:?}",
93 debug2format!(message.header())
94 );
95
96 let mut responseb = domain::base::MessageBuilder::from_target(buf)?;
97
98 let buf = if matches!(message.header().opcode(), Opcode::QUERY) {
99 debug!("Message is of type Query, processing all questions");
100
101 let mut answerb = responseb.start_answer(&message, Rcode::NOERROR)?;
102
103 for question in message.question() {
104 let question = question?;
105
106 if matches!(question.qtype(), Rtype::A) && matches!(question.qclass(), Class::IN) {
107 let record = Record::new(
108 question.qname(),
109 Class::IN,
110 Ttl::from_duration_lossy(ttl),
111 A::from_octets(ip[0], ip[1], ip[2], ip[3]),
112 );
113 debug!(
114 "Answering {:?} with {:?}",
115 debug2format!(question),
116 debug2format!(record)
117 );
118 answerb.push(record)?;
119 } else {
120 debug!(
121 "Question {:?} is not of type A, not answering",
122 debug2format!(question)
123 );
124 }
125 }
126
127 answerb.finish()
128 } else {
129 debug!("Message is not of type Query, replying with NotImp");
130
131 let headerb = responseb.header_mut();
132
133 headerb.set_id(message.header().id());
134 headerb.set_opcode(message.header().opcode());
135 headerb.set_rd(message.header().rd());
136 headerb.set_rcode(domain::base::iana::Rcode::NOTIMP);
137
138 responseb.finish()
139 };
140
141 Ok(buf.1)
142}
143
144struct Buf<'a>(pub &'a mut [u8], pub usize);
145
146impl Composer for Buf<'_> {}
147
148impl OctetsBuilder for Buf<'_> {
149 type AppendError = ShortBuf;
150
151 fn append_slice(&mut self, slice: &[u8]) -> Result<(), Self::AppendError> {
152 if self.1 + slice.len() <= self.0.len() {
153 let end = self.1 + slice.len();
154 self.0[self.1..end].copy_from_slice(slice);
155 self.1 = end;
156
157 Ok(())
158 } else {
159 Err(ShortBuf)
160 }
161 }
162}
163
164impl Truncate for Buf<'_> {
165 fn truncate(&mut self, len: usize) {
166 self.1 = len;
167 }
168}
169
170impl AsMut<[u8]> for Buf<'_> {
171 fn as_mut(&mut self) -> &mut [u8] {
172 &mut self.0[..self.1]
173 }
174}
175
176impl AsRef<[u8]> for Buf<'_> {
177 fn as_ref(&self) -> &[u8] {
178 &self.0[..self.1]
179 }
180}