1use crate::custom_module::manycastr::Address;
2use crate::net::packet::DnsProbeId;
3use crate::net::{PacketPayload, PseudoHeader, build_ip_packet, calculate_checksum};
4use crate::{dns_identifier, m_id_of};
5use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
6use prost::bytes::Buf;
7use std::io::{Cursor, Read, Write};
8
9#[derive(Debug)]
11pub struct UDPPacket {
12 pub sport: u16,
13 pub dport: u16,
14 pub length: u16,
15 pub checksum: u16,
16 pub body: Vec<u8>,
17}
18
19impl From<&[u8]> for UDPPacket {
21 fn from(data: &[u8]) -> Self {
22 let mut data = Cursor::new(data);
23 UDPPacket {
24 sport: data.read_u16::<NetworkEndian>().unwrap(),
25 dport: data.read_u16::<NetworkEndian>().unwrap(),
26 length: data.read_u16::<NetworkEndian>().unwrap(),
27 checksum: data.read_u16::<NetworkEndian>().unwrap(),
28 body: data.into_inner()[8..].to_vec(),
29 }
30 }
31}
32
33impl From<&UDPPacket> for Vec<u8> {
35 fn from(packet: &UDPPacket) -> Self {
36 let mut wtr = vec![];
37 wtr.write_u16::<NetworkEndian>(packet.sport)
38 .expect("Unable to write to byte buffer for UDP packet");
39 wtr.write_u16::<NetworkEndian>(packet.dport)
40 .expect("Unable to write to byte buffer for UDP packet");
41 wtr.write_u16::<NetworkEndian>(packet.length)
42 .expect("Unable to write to byte buffer for UDP packet");
43 wtr.write_u16::<NetworkEndian>(packet.checksum)
44 .expect("Unable to write to byte buffer for UDP packet");
45 wtr.write_all(&packet.body)
46 .expect("Unable to write to byte buffer for UDP packet");
47
48 wtr
49 }
50}
51
52#[allow(dead_code)]
54#[derive(Debug)]
55pub struct DNSRecord {
56 pub transaction_id: u16,
57 pub flags: u16,
58 pub questions: u16,
59 pub answer: u16,
60 pub authority: u16,
61 pub additional: u16,
62 pub domain: String,
63 pub record_type: u16,
64 pub class: u16,
65 pub body: Vec<u8>, }
67
68#[allow(dead_code)]
70#[derive(Debug)]
71pub struct DNSAnswer {
72 pub domain: String,
73 pub record_type: u16,
74 pub class: u16,
75 pub ttl: u32,
76 pub data_length: u16,
77 pub data: Vec<u8>,
78}
79
80#[allow(dead_code)]
82#[derive(Debug)]
83pub struct TXTRecord {
84 pub txt_length: u8,
85 pub txt: String,
86}
87
88#[cfg_attr(test, allow(dead_code))]
91fn read_dns_name(data: &mut Cursor<&[u8]>) -> String {
92 let mut result = String::new();
93 loop {
94 if !data.has_remaining() {
95 break;
96 }
97 let label_len = data.read_u8().unwrap();
98 if label_len == 0 {
100 break;
101 }
102 if label_len & 0xC0 == 0xC0 {
104 let offset = ((label_len as u16 & 0x3F) << 8) | data.read_u8().unwrap() as u16;
106 data.set_position(offset as u64);
107 result.push_str(&read_dns_name(data));
108 break;
109 }
110 let mut label_bytes = vec![0; label_len as usize];
112
113 match data.read_exact(&mut label_bytes) {
114 Ok(()) => {}
115 Err(_) => {
116 return "Invalid domain name".to_string();
117 }
118 }
119
120 let label = String::from_utf8_lossy(&label_bytes).to_string();
121 result.push_str(&label);
122 result.push('.');
123 }
124 if result.ends_with('.') {
126 result.pop();
127 }
128 result
129}
130
131impl From<&[u8]> for DNSRecord {
133 fn from(data: &[u8]) -> Self {
134 let mut data = Cursor::new(data);
135
136 let transaction_id = data.read_u16::<NetworkEndian>().unwrap();
137 let flags = data.read_u16::<NetworkEndian>().unwrap();
138 let questions = data.read_u16::<NetworkEndian>().unwrap();
139 let answer = data.read_u16::<NetworkEndian>().unwrap();
140 let authority = data.read_u16::<NetworkEndian>().unwrap();
141 let additional = data.read_u16::<NetworkEndian>().unwrap();
142 let domain = read_dns_name(&mut data);
143
144 let (record_type, class, body) = if data.remaining() >= 4 {
145 let record_type = data.read_u16::<NetworkEndian>().unwrap();
146 let class = data.read_u16::<NetworkEndian>().unwrap();
147 let body = data.clone().into_inner()[data.position() as usize..].to_vec();
148 (record_type, class, body)
149 } else {
150 let record_type = 0;
151 let class = 0;
152 let body = vec![];
153 (record_type, class, body)
154 };
155
156 DNSRecord {
157 transaction_id,
158 flags,
159 questions,
160 answer,
161 authority,
162 additional,
163 domain,
164 record_type,
165 class,
166 body,
167 }
168 }
169}
170
171impl From<&[u8]> for DNSAnswer {
173 fn from(data: &[u8]) -> Self {
174 let mut data = Cursor::new(data);
175
176 if data.remaining() < 10 {
178 return DNSAnswer {
179 domain: "Invalid DNS record".to_string(),
180 record_type: 0,
181 class: 0,
182 ttl: 0,
183 data_length: 0,
184 data: vec![],
185 };
186 }
187
188 DNSAnswer {
189 domain: data.read_u16::<NetworkEndian>().unwrap().to_string(), record_type: data.read_u16::<NetworkEndian>().unwrap(),
191 class: data.read_u16::<NetworkEndian>().unwrap(),
192 ttl: data.read_u32::<NetworkEndian>().unwrap(),
193 data_length: data.read_u16::<NetworkEndian>().unwrap(),
194 data: data.clone().into_inner()[data.position() as usize..].to_vec(),
195 }
196 }
197}
198
199impl From<&[u8]> for TXTRecord {
201 fn from(data: &[u8]) -> Self {
202 let mut data = Cursor::new(data);
203 if data.remaining() < 1 {
205 return TXTRecord {
206 txt_length: 0,
207 txt: "Invalid TXT record".to_string(),
208 };
209 }
210
211 let txt_length = data.read_u8().unwrap();
212
213 if txt_length as usize > data.remaining() {
215 return TXTRecord {
216 txt_length,
217 txt: "Invalid TXT record".to_string(),
218 };
219 }
220
221 TXTRecord {
222 txt_length,
223 txt: String::from_utf8_lossy(&data.into_inner()[1..(1 + txt_length as u64) as usize])
225 .to_string(),
226 }
227 }
228}
229
230impl UDPPacket {
231 pub fn dns_request(
234 src: &Address,
235 dst: &Address,
236 sport: u16,
237 domain_name: &str,
238 tx_time: u64,
239 id: &DnsProbeId,
240 ) -> Vec<u8> {
241 let ttl: u8 = 255;
242 let dns_packet = Self::create_a_record_request(domain_name, tx_time, src, dst, id, sport);
243
244 let udp_length = (8 + dns_packet.len()) as u16;
245
246 let mut udp_packet = Self {
247 sport,
248 dport: 53u16, length: udp_length,
250 checksum: 0,
251 body: dns_packet,
252 };
253
254 let udp_bytes: Vec<u8> = (&udp_packet).into();
255
256 let pseudo_header = PseudoHeader::new(src, dst, 17, udp_length as u32);
257 udp_packet.checksum = calculate_checksum(&udp_bytes, &pseudo_header);
258
259 build_ip_packet(
260 src,
261 dst,
262 ttl,
263 15037,
264 PacketPayload::Udp { value: udp_packet },
265 )
266 }
267
268 fn create_a_record_request(
270 domain_name: &str,
271 tx_time: u64,
272 src: &Address,
273 dst: &Address,
274 id: &DnsProbeId,
275 sport: u16,
276 ) -> Vec<u8> {
277 let src_num = src.as_numeric();
278 let dst_num = dst.as_numeric();
279 let tx_id = id.worker_id;
280 let probe_id = id.probe_id;
281
282 let subdomain =
283 format!("{tx_time}.{src_num}.{dst_num}.{tx_id}.{sport}.{probe_id}.{domain_name}");
284 let mut dns_body: Vec<u8> = Vec::new();
285
286 let tx_id_raw: u16 = tx_id as u16;
288 let encoded_tx_id =
289 ((dns_identifier(m_id_of(probe_id)) as u16) << 10) | (tx_id_raw & 0x03FF);
290
291 dns_body
293 .write_u16::<byteorder::BigEndian>(encoded_tx_id)
294 .unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0100).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); for label in subdomain.split('.') {
303 dns_body.push(label.len() as u8);
304 dns_body.write_all(label.as_bytes()).unwrap();
305 }
306 dns_body.push(0); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body
311 }
312
313 pub fn chaos_request(
315 src: &Address,
316 dst: &Address,
317 sport: u16,
318 id: &DnsProbeId,
319 chaos: &str,
320 ) -> Vec<u8> {
321 let dns_body = Self::create_chaos_request(id, chaos);
322
323 let udp_length = 8 + dns_body.len() as u32;
324
325 let mut udp_packet = Self {
326 sport,
327 dport: 53u16,
328 length: udp_length as u16,
329 checksum: 0,
330 body: dns_body,
331 };
332
333 let udp_bytes: Vec<u8> = (&udp_packet).into();
334
335 let pseudo_header = PseudoHeader::new(src, dst, 17, udp_length);
336
337 udp_packet.checksum = calculate_checksum(&udp_bytes, &pseudo_header);
338
339 build_ip_packet(
340 src,
341 dst,
342 255,
343 15037,
344 PacketPayload::Udp { value: udp_packet },
345 )
346 }
347
348 fn create_chaos_request(id: &DnsProbeId, chaos: &str) -> Vec<u8> {
350 let mut dns_body: Vec<u8> = Vec::new();
351
352 let tx_id_raw: u16 = id.worker_id as u16;
354 let encoded_tx_id =
355 ((dns_identifier(m_id_of(id.probe_id)) as u16) << 10) | (tx_id_raw & 0x03FF);
356
357 dns_body
359 .write_u16::<byteorder::BigEndian>(encoded_tx_id)
360 .unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0100).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); for label in chaos.split('.') {
369 dns_body.push(label.len() as u8);
370 dns_body.write_all(label.as_bytes()).unwrap();
371 }
372 dns_body.push(0); dns_body.write_u16::<byteorder::BigEndian>(0x0010).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0003).unwrap(); dns_body
377 }
378}
379
380pub(crate) fn dns_a_trace_body(
390 src: &Address,
391 dst: &Address,
392 sport: u16,
393 id: &crate::net::packet::TraceDnsId,
394) -> Vec<u8> {
395 let &crate::net::packet::TraceDnsId {
396 tx_id,
397 probe_id,
398 tx_micros,
399 ttl,
400 qname,
401 } = id;
402
403 let src_num = src.as_numeric();
404 let dst_num = dst.as_numeric();
405 let subdomain =
406 format!("{tx_micros}.{src_num}.{dst_num}.{tx_id}.{sport}.{probe_id}.{ttl}.{qname}");
407
408 let mut dns_body: Vec<u8> = Vec::new();
409
410 let encoded_tx_id =
412 ((dns_identifier(m_id_of(probe_id)) as u16) << 10) | ((tx_id as u16) & 0x03FF);
413 dns_body
414 .write_u16::<byteorder::BigEndian>(encoded_tx_id)
415 .unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0100).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0000).unwrap(); for label in subdomain.split('.') {
423 dns_body.push(label.len() as u8);
424 dns_body.write_all(label.as_bytes()).unwrap();
425 }
426 dns_body.push(0); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body.write_u16::<byteorder::BigEndian>(0x0001).unwrap(); dns_body
431}