1use adns_proto::{Class, Name, Question, Record, SoaData, Type, TypeData, TypeDataParseError};
2use indexmap::{map::Entry, IndexMap};
3use log::warn;
4use serde::{ser::SerializeSeq, Deserialize, Serialize};
5use serde_with::{serde_as, DeserializeAs, SerializeAs};
6
7mod updates;
8pub use updates::*;
9
10struct VecRecordConvert;
11
12impl SerializeAs<Vec<Record>> for VecRecordConvert {
13 fn serialize_as<S: serde::Serializer>(
14 source: &Vec<Record>,
15 serializer: S,
16 ) -> Result<S::Ok, S::Error> {
17 let mut seq = serializer.serialize_seq(Some(source.len()))?;
18 for item in source {
19 let item: ZoneRecord = item.clone().into();
20 seq.serialize_element(&item)?;
21 }
22 seq.end()
23 }
24}
25
26impl<'de> DeserializeAs<'de, Vec<Record>> for VecRecordConvert {
27 fn deserialize_as<D: serde::Deserializer<'de>>(
28 deserializer: D,
29 ) -> Result<Vec<Record>, D::Error> {
30 let from = Vec::<ZoneRecord>::deserialize(deserializer)?;
31 from.into_iter()
32 .map(|x| -> Result<Record, _> { x.try_into() })
33 .collect::<Result<Vec<Record>, TypeDataParseError>>()
34 .map_err(serde::de::Error::custom)
35 }
36}
37
38struct SubZoneConvert;
39
40impl SerializeAs<IndexMap<Name, Zone>> for SubZoneConvert {
41 fn serialize_as<S: serde::Serializer>(
42 source: &IndexMap<Name, Zone>,
43 serializer: S,
44 ) -> Result<S::Ok, S::Error> {
45 source
46 .clone()
47 .into_iter()
48 .map(|x| (x.0, x.1.into()))
49 .collect::<IndexMap<Name, SubZone>>()
50 .serialize(serializer)
51 }
52}
53
54impl<'de> DeserializeAs<'de, IndexMap<Name, Zone>> for SubZoneConvert {
55 fn deserialize_as<D: serde::Deserializer<'de>>(
56 deserializer: D,
57 ) -> Result<IndexMap<Name, Zone>, D::Error> {
58 let from = IndexMap::<Name, SubZone>::deserialize(deserializer)?;
59 Ok(from.into_iter().map(|x| (x.0, x.1.into())).collect())
60 }
61}
62
63fn serde_true() -> bool {
64 true
65}
66
67fn serde_is_true(value: &bool) -> bool {
68 *value
69}
70
71#[serde_as]
72#[derive(Default, Serialize, Deserialize, Debug, Clone)]
73pub struct Zone {
74 #[serde_as(as = "VecRecordConvert")]
76 #[serde(default)]
77 pub records: Vec<Record>,
78 #[serde(default)]
79 #[serde_as(as = "SubZoneConvert")]
80 pub zones: IndexMap<Name, Zone>,
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub soa: Option<SoaData>,
83 #[serde(default, skip_serializing_if = "Vec::is_empty")]
84 pub nameservers: Vec<Name>,
85 #[serde(default)]
86 pub tsig_keys: IndexMap<String, TsigKey>,
87 #[serde(default = "serde_true", skip_serializing_if = "serde_is_true")]
88 pub authoritative: bool,
89 #[serde(skip)]
90 pub class: Class,
91 #[serde(default)]
92 pub allow_md5_tsig: bool,
93}
94
95#[serde_as]
96#[derive(Default, Serialize, Deserialize, Debug, Clone)]
97pub struct SubZone {
98 #[serde_as(as = "VecRecordConvert")]
100 #[serde(default)]
101 pub records: Vec<Record>,
102 #[serde(default = "serde_true", skip_serializing_if = "serde_is_true")]
103 pub authoritative: bool,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub soa: Option<SoaData>,
106 #[serde(default, skip_serializing_if = "Vec::is_empty")]
107 pub nameservers: Vec<Name>,
108}
109
110impl From<SubZone> for Zone {
111 fn from(value: SubZone) -> Self {
112 Zone {
113 records: value.records,
114 zones: Default::default(),
115 tsig_keys: Default::default(),
116 authoritative: value.authoritative,
117 class: Default::default(),
118 allow_md5_tsig: Default::default(),
119 soa: value.soa,
120 nameservers: value.nameservers,
121 }
122 }
123}
124
125impl From<Zone> for SubZone {
126 fn from(value: Zone) -> Self {
127 SubZone {
128 records: value.records,
129 authoritative: value.authoritative,
130 soa: value.soa,
131 nameservers: value.nameservers,
132 }
133 }
134}
135
136#[serde_as]
137#[derive(Serialize, Deserialize, Clone, Debug)]
138pub struct TsigKey(#[serde_as(as = "serde_with::base64::Base64")] pub Vec<u8>);
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
141pub enum AnswerState {
142 None,
143 DomainSeen,
144}
145
146#[derive(Default, Serialize, Deserialize, Debug, Clone)]
147pub struct ZoneAnswer {
148 pub is_authoritative: bool,
149 pub answers: Vec<Record>,
150}
151
152impl Zone {
153 pub fn merge_from(&mut self, other: Zone) {
154 for record in other.records {
155 ZoneUpdateAction::AddRecord(record).apply_to(&Name::default(), self);
156 }
157 for (zone_name, new_zone) in other.zones {
158 match self.zones.entry(zone_name.clone()) {
159 Entry::Occupied(mut current_zone) => {
160 let current_zone = current_zone.get_mut();
161 for record in new_zone.records {
162 ZoneUpdateAction::AddRecord(record).apply_to(&zone_name, current_zone);
163 }
164 }
165 Entry::Vacant(v) => {
166 v.insert(new_zone);
167 }
168 }
169 }
170 }
171
172 pub fn answer(
173 &self,
174 parent_zone: Option<&Zone>,
175 zone_name: &Name,
176 question: &Question,
177 response: &mut ZoneAnswer,
178 ) -> AnswerState {
179 response.is_authoritative = self.authoritative;
180 if &question.name == zone_name {
181 match question.type_ {
182 Type::SOA => {
183 if let Some(soa) = self
184 .soa
185 .clone()
186 .or_else(|| parent_zone.and_then(|x| x.soa.clone()))
187 {
188 response
189 .answers
190 .push(Record::new(zone_name, 0, TypeData::SOA(soa)));
191 } else {
192 warn!("no SOA specified for zone {}", zone_name);
193 }
194 return AnswerState::DomainSeen;
195 }
196 Type::NS => {
197 #[allow(clippy::unnecessary_unwrap)]
198 let nameservers = if self.nameservers.is_empty() && parent_zone.is_some() {
199 &parent_zone.unwrap().nameservers
200 } else {
201 &self.nameservers
202 };
203 for nameserver in nameservers {
204 response.answers.push(Record::new(
205 zone_name,
206 3600,
207 TypeData::NS(nameserver.clone()),
208 ));
209 }
210 return AnswerState::DomainSeen;
211 }
212 _ => (),
213 }
214 }
215 let mut state = AnswerState::None;
216 for record in &self.records {
217 if !record.name.contains(&question.name) {
218 continue;
219 }
220 state = AnswerState::DomainSeen;
221 if !question.type_.wants_by_query(record.type_) {
222 continue;
223 }
224 response.answers.push(Record {
225 name: question.name.clone(),
226 type_: record.type_,
227 class: record.class,
228 ttl: record.ttl,
229 data: record.data.clone(),
230 });
231 }
232 for (name, zone) in &self.zones {
233 if !question.name.ends_with(name) {
234 continue;
235 }
236 let substate = zone.answer(Some(self), name, question, response);
237 if substate > state {
238 state = substate;
239 }
240 }
241 state
242 }
243}
244
245fn default_ttl() -> u32 {
246 300
247}
248
249fn is_default_ttl(value: &u32) -> bool {
250 *value == 300
251}
252
253fn is_default_class(class: &Class) -> bool {
254 *class == Class::IN
255}
256
257#[derive(Serialize, Deserialize, Debug, Clone)]
258struct ZoneRecord {
259 domain: Name,
260 #[serde(rename = "type")]
261 type_: Type,
262 #[serde(default, skip_serializing_if = "is_default_class")]
263 class: Class,
264 #[serde(default = "default_ttl", skip_serializing_if = "is_default_ttl")]
265 ttl: u32,
266 data: String,
267}
268
269impl TryInto<Record> for ZoneRecord {
270 type Error = TypeDataParseError;
271
272 fn try_into(self) -> Result<Record, Self::Error> {
273 Ok(Record {
274 name: self.domain,
275 type_: self.type_,
276 class: self.class,
277 ttl: self.ttl,
278 data: TypeData::parse_str(self.type_, &self.data)?,
279 })
280 }
281}
282
283impl From<Record> for ZoneRecord {
284 fn from(value: Record) -> Self {
285 ZoneRecord {
286 domain: value.name.clone(),
287 type_: value.type_,
288 class: value.class,
289 ttl: value.ttl,
290 data: value.data.to_string(),
291 }
292 }
293}