manycastr/worker/inbound/
dns.rs1use crate::custom_module::manycastr::reply::ReplyData;
2use crate::custom_module::manycastr::{
3 Address, DiscoveryReply, MeasurementReply, Reply, TraceReply,
4};
5use crate::dns_identifier;
6use crate::net::{DNSAnswer, DNSRecord, TXTRecord};
7use crate::worker::inbound::ReplyMeta;
8
9pub struct DnsContext {
11 pub is_chaos: bool,
12 pub sport: u16,
13 pub m_id: u32,
14 pub is_traceroute: bool,
15}
16
17pub fn parse_dns(packet_bytes: &[u8], meta: ReplyMeta, ctx: &DnsContext) -> Option<Reply> {
28 let ReplyMeta { src, ttl, rx_time } = meta;
29 let DnsContext {
30 is_chaos,
31 sport,
32 m_id,
33 is_traceroute: traceroute,
34 } = *ctx;
35
36 let udp_bytes = if src.is_v6() {
38 packet_bytes
39 } else {
40 packet_bytes.get(20..)?
42 };
43 if udp_bytes.len() < 8 {
44 return None;
45 }
46 let reply_dport = u16::from_be_bytes([udp_bytes[2], udp_bytes[3]]);
47 let dns_msg = &udp_bytes[8..];
48
49 if reply_dport != sport {
51 return None;
52 }
53
54 if dns_msg.is_empty() || (dns_msg[0] >> 2) != dns_identifier(m_id) {
56 return None;
57 }
58
59 if (!is_chaos & (dns_msg.len() < 66)) | (is_chaos & (dns_msg.len() < 10)) {
61 return None;
62 }
63
64 let (tx_time, tx_id, chaos, is_discovery, hop_ttl, session_id) = if !is_chaos {
65 let dns_result = parse_dns_a_record(dns_msg, src.is_v6(), m_id)?;
66
67 if (dns_result.probe_sport != reply_dport) | (dns_result.probe_dst != src) {
68 return None; }
70
71 (
72 dns_result.tx_time,
73 dns_result.tx_id,
74 None,
75 dns_result.is_discovery,
76 dns_result.hop_ttl,
77 dns_result.session_id,
78 )
79 } else {
80 let (tx_time, tx_worker_id, chaos) = parse_chaos(dns_msg)?;
82 (tx_time, tx_worker_id, Some(chaos), false, None, 0)
83 };
84
85 if is_discovery {
86 Some(Reply {
88 reply_data: Some(ReplyData::Discovery(DiscoveryReply {
89 src: Some(src),
90 session_id,
91 })),
92 })
93 } else if traceroute {
94 Some(Reply {
96 reply_data: Some(ReplyData::Trace(TraceReply {
97 hop_addr: Some(src),
98 ttl,
99 rtt: super::rtt_ms(rx_time, tx_time, super::TxEncoding::Micros),
100 tx_id,
101 trace_dst: Some(src),
102 hop_count: hop_ttl.unwrap_or(0) as u32,
103 })),
104 })
105 } else {
106 let rtt = if is_chaos {
108 0.0
109 } else {
110 super::rtt_ms(rx_time, tx_time, super::TxEncoding::Micros)
111 };
112 Some(Reply {
113 reply_data: Some(ReplyData::Measurement(MeasurementReply {
114 src: Some(src),
115 ttl,
116 rtt,
117 tx_id,
118 chaos,
119 session_id,
120 })),
121 })
122 }
123}
124
125struct DnsResult {
126 tx_time: u64,
127 tx_id: u32,
128 probe_sport: u16,
129 probe_dst: Address,
130 is_discovery: bool,
131 hop_ttl: Option<u8>,
133 session_id: u32,
135}
136
137fn parse_dns_a_record(packet_bytes: &[u8], is_ipv6: bool, m_id: u32) -> Option<DnsResult> {
151 let record = DNSRecord::from(packet_bytes);
152 let domain = record.domain; let parts: Vec<&str> = domain.split('.').collect();
154 if parts.len() < 6 {
156 return None;
157 }
158
159 let tx_time = parts[0].parse::<u64>().ok()?;
160 let probe_dst = if is_ipv6 {
161 Address::from(parts[2].parse::<u128>().ok()?)
162 } else {
163 Address::from(parts[2].parse::<u32>().ok()?)
164 };
165 let mut tx_id = parts[3].parse::<u32>().ok()?;
166 let probe_sport = parts[4].parse::<u16>().ok()?;
167 let pkt_probe_id = parts[5].parse::<u32>().ok()?;
168
169 if crate::m_id_of(pkt_probe_id) != m_id {
171 return None;
172 }
173 let session_id = crate::session_id_of(pkt_probe_id);
174
175 let is_discovery = if tx_id > u16::MAX as u32 {
176 tx_id -= u16::MAX as u32;
177 true
178 } else {
179 false
180 };
181
182 let hop_ttl = parts.get(6).and_then(|s| s.parse::<u8>().ok());
184
185 Some(DnsResult {
186 tx_time,
187 tx_id,
188 hop_ttl,
189 probe_sport,
190 probe_dst,
191 is_discovery,
192 session_id,
193 })
194}
195
196fn parse_chaos(packet_bytes: &[u8]) -> Option<(u64, u32, String)> {
207 let record = DNSRecord::from(packet_bytes);
208
209 let tx_worker_id = (record.transaction_id & 0x03FF) as u32;
211
212 if record.answer == 0 {
213 return Some((0u64, tx_worker_id, "*".to_string()));
214 }
215
216 let chaos_data = TXTRecord::from(DNSAnswer::from(record.body.as_slice()).data.as_slice()).txt;
217
218 Some((0u64, tx_worker_id, chaos_data))
219}