1use std::error;
30use std::fmt;
31use std::io;
32use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket};
33use std::time::Duration;
34
35pub const DEFAULT_DNS_SERVERS: &[&str] = &[
37 "1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4", ];
42
43pub const DNS_TYPE_A: u16 = 1; pub const DNS_TYPE_MX: u16 = 15; pub const DNS_TYPE_AAAA: u16 = 28; #[derive(Debug)]
50pub enum Error {
51 Io(io::Error),
53 Timeout,
55 ServerError(u16),
57 NoRecordsFound,
59 MalformedPacket,
61}
62
63impl fmt::Display for Error {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Error::Io(err) => write!(f, "IO error: {}", err),
67 Error::Timeout => write!(f, "DNS query timed out"),
68 Error::ServerError(code) => write!(f, "DNS server returned error code: {}", code),
69 Error::NoRecordsFound => write!(f, "No DNS records found"),
70 Error::MalformedPacket => write!(f, "Malformed DNS packet"),
71 }
72 }
73}
74
75impl error::Error for Error {
76 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
77 match self {
78 Error::Io(err) => Some(err),
79 _ => None,
80 }
81 }
82}
83
84impl From<io::Error> for Error {
85 fn from(err: io::Error) -> Self {
86 Error::Io(err)
87 }
88}
89
90#[derive(Debug)]
92pub struct DnsHeader {
93 pub id: u16,
95 pub flags: u16,
97 pub questions: u16,
99 pub answers: u16,
101 pub authorities: u16,
103 pub additionals: u16,
105}
106
107pub fn parse_dns_header(buffer: &[u8]) -> Result<DnsHeader, Error> {
109 if buffer.len() < 12 {
110 return Err(Error::MalformedPacket);
111 }
112
113 let header = DnsHeader {
114 id: u16::from_be_bytes([buffer[0], buffer[1]]),
115 flags: u16::from_be_bytes([buffer[2], buffer[3]]),
116 questions: u16::from_be_bytes([buffer[4], buffer[5]]),
117 answers: u16::from_be_bytes([buffer[6], buffer[7]]),
118 authorities: u16::from_be_bytes([buffer[8], buffer[9]]),
119 additionals: u16::from_be_bytes([buffer[10], buffer[11]]),
120 };
121
122 let rcode = header.flags & 0x0F;
124 if rcode != 0 {
125 return Err(Error::ServerError(rcode));
126 }
127
128 Ok(header)
129}
130
131pub fn skip_question(buffer: &[u8], mut pos: usize) -> Result<usize, Error> {
133 if pos >= buffer.len() {
134 return Err(Error::MalformedPacket);
135 }
136
137 while pos < buffer.len() {
139 let len = buffer[pos] as usize;
140 if len == 0 {
141 pos += 1;
142 break;
143 }
144
145 if len & 0xC0 == 0xC0 {
147 pos += 2;
148 break;
149 }
150
151 pos += len + 1;
152
153 if pos >= buffer.len() {
154 return Err(Error::MalformedPacket);
155 }
156 }
157
158 if pos + 4 > buffer.len() {
160 return Err(Error::MalformedPacket);
161 }
162
163 Ok(pos + 4)
164}
165
166#[derive(Debug, Clone)]
168pub enum RecordData {
169 MX {
171 priority: u16,
173 server: String,
175 },
176 A(Ipv4Addr),
178 AAAA(Ipv6Addr),
180 Unknown,
182}
183
184#[derive(Debug)]
186pub struct DnsRecord {
187 pub record_type: u16,
189 pub data: RecordData,
191}
192
193pub fn parse_answer(buffer: &[u8], mut pos: usize) -> Result<(DnsRecord, usize), Error> {
195 if pos >= buffer.len() {
196 return Err(Error::MalformedPacket);
197 }
198
199 while pos < buffer.len() {
201 let len = buffer[pos] as usize;
202 if len == 0 {
203 pos += 1;
204 break;
205 }
206
207 if len & 0xC0 == 0xC0 {
209 pos += 2;
210 break;
211 }
212
213 pos += len + 1;
214
215 if pos >= buffer.len() {
216 return Err(Error::MalformedPacket);
217 }
218 }
219
220 if pos + 10 > buffer.len() {
222 return Err(Error::MalformedPacket);
223 }
224
225 let record_type = u16::from_be_bytes([buffer[pos], buffer[pos + 1]]);
227 pos += 4; pos += 4;
231
232 let data_len = u16::from_be_bytes([buffer[pos], buffer[pos + 1]]) as usize;
234 pos += 2;
235
236 if pos + data_len > buffer.len() {
238 return Err(Error::MalformedPacket);
239 }
240
241 let data = match record_type {
242 DNS_TYPE_MX => {
243 if data_len < 2 {
244 return Err(Error::MalformedPacket);
245 }
246
247 let priority = u16::from_be_bytes([buffer[pos], buffer[pos + 1]]);
248 pos += 2;
249
250 let server = parse_dns_name(buffer, pos)?;
252 pos += data_len - 2; RecordData::MX { priority, server }
255 }
256 DNS_TYPE_A => {
257 if data_len != 4 {
258 return Err(Error::MalformedPacket);
259 }
260
261 let ipv4 = Ipv4Addr::new(
262 buffer[pos],
263 buffer[pos + 1],
264 buffer[pos + 2],
265 buffer[pos + 3],
266 );
267 pos += 4;
268 RecordData::A(ipv4)
269 }
270 DNS_TYPE_AAAA => {
271 if data_len != 16 {
272 return Err(Error::MalformedPacket);
273 }
274
275 let mut ipv6_bytes = [0u8; 16];
276 ipv6_bytes.copy_from_slice(&buffer[pos..pos + 16]);
277 let ipv6 = Ipv6Addr::from(ipv6_bytes);
278 pos += 16;
279 RecordData::AAAA(ipv6)
280 }
281 _ => {
282 pos += data_len;
284 RecordData::Unknown
285 }
286 };
287
288 Ok((DnsRecord { record_type, data }, pos))
289}
290
291pub fn parse_dns_name(buffer: &[u8], mut pos: usize) -> Result<String, Error> {
293 if pos >= buffer.len() {
294 return Err(Error::MalformedPacket);
295 }
296
297 let mut name = String::new();
298 let mut first = true;
299
300 let mut jumps = 0;
302 const MAX_JUMPS: usize = 10; loop {
305 if pos >= buffer.len() {
306 return Err(Error::MalformedPacket);
307 }
308
309 let len = buffer[pos] as usize;
310 pos += 1;
311
312 if len == 0 {
314 break;
315 }
316
317 if len & 0xC0 == 0xC0 {
319 if pos >= buffer.len() {
320 return Err(Error::MalformedPacket);
321 }
322
323 jumps += 1;
324 if jumps > MAX_JUMPS {
325 return Err(Error::MalformedPacket);
326 }
327
328 let offset = ((len & 0x3F) << 8) | buffer[pos] as usize;
329
330 if !first {
332 name.push('.');
333 }
334
335 let remainder = parse_dns_name(buffer, offset)?;
337 name.push_str(&remainder);
338 break;
339 }
340
341 if pos + len > buffer.len() {
343 return Err(Error::MalformedPacket);
344 }
345
346 if !first {
348 name.push('.');
349 }
350 first = false;
351
352 name.push_str(&String::from_utf8_lossy(&buffer[pos..pos + len]));
354 pos += len;
355 }
356
357 Ok(name)
358}
359
360#[derive(Debug, Clone, PartialEq)]
362pub struct MxRecord {
363 pub priority: u16,
365 pub server: String,
367}
368
369#[derive(Debug, Clone, PartialEq)]
371pub struct ServerIpRecord {
372 pub server: String,
374 pub ip_addresses: Vec<IpAddr>,
376}
377
378#[derive(Debug, Clone)]
380pub struct DnsConfig {
381 pub servers: Vec<String>,
383 pub timeout: u64,
385}
386
387impl Default for DnsConfig {
388 fn default() -> Self {
389 Self {
390 servers: DEFAULT_DNS_SERVERS.iter().map(|s| s.to_string()).collect(),
391 timeout: 5,
392 }
393 }
394}
395
396pub fn build_dns_query(domain: &str, record_type: u16) -> Result<Vec<u8>, Error> {
398 let mut packet = Vec::new();
399
400 let id = 1_i16;
402 packet.extend_from_slice(&id.to_be_bytes());
403 packet.extend_from_slice(&[0x01, 0x00]); packet.extend_from_slice(&[0x00, 0x01]); packet.extend_from_slice(&[0x00, 0x00]); packet.extend_from_slice(&[0x00, 0x00]); packet.extend_from_slice(&[0x00, 0x00]); for part in domain.split('.') {
411 if part.is_empty() {
412 continue;
413 }
414
415 if part.len() > 63 {
416 return Err(Error::MalformedPacket);
417 }
418
419 packet.push(part.len() as u8);
420 packet.extend_from_slice(part.as_bytes());
421 }
422 packet.push(0); packet.extend_from_slice(&record_type.to_be_bytes());
426 packet.extend_from_slice(&[0x00, 0x01]); Ok(packet)
429}
430
431pub fn parse_mx_records(buffer: &[u8]) -> Result<Vec<MxRecord>, Error> {
433 let mut records = Vec::new();
434 let header = parse_dns_header(buffer)?;
435
436 if header.answers == 0 {
437 return Err(Error::NoRecordsFound);
438 }
439
440 let mut pos = 12; for _ in 0..header.questions {
443 pos = skip_question(buffer, pos)?;
444 }
445
446 for _ in 0..header.answers {
448 let (record, new_pos) = parse_answer(buffer, pos)?;
449 pos = new_pos;
450
451 if record.record_type == DNS_TYPE_MX {
452 if let RecordData::MX { priority, server } = record.data {
453 records.push(MxRecord { priority, server });
454 }
455 }
456 }
457
458 if records.is_empty() {
459 return Err(Error::NoRecordsFound);
460 }
461
462 records.sort_by_key(|r| r.priority);
464 Ok(records)
465}
466
467pub fn parse_ip_records(buffer: &[u8]) -> Result<Vec<IpAddr>, Error> {
469 let mut ips = Vec::new();
470 let header = parse_dns_header(buffer)?;
471
472 let mut pos = 12; for _ in 0..header.questions {
475 pos = skip_question(buffer, pos)?;
476 }
477
478 for _ in 0..header.answers {
480 let (record, new_pos) = parse_answer(buffer, pos)?;
481 pos = new_pos;
482
483 match record.data {
484 RecordData::A(ipv4) => ips.push(IpAddr::V4(ipv4)),
485 RecordData::AAAA(ipv6) => ips.push(IpAddr::V6(ipv6)),
486 _ => {}
487 }
488 }
489
490 if ips.is_empty() {
491 return Err(Error::NoRecordsFound);
492 }
493
494 Ok(ips)
495}
496
497pub fn lookup_dns_records(
499 domain: &str,
500 record_type: u16,
501 config: Option<DnsConfig>,
502) -> Result<Vec<u8>, Error> {
503 let config = config.unwrap_or_default();
504
505 let query = build_dns_query(domain, record_type)?;
507
508 for server in &config.servers {
510 let server_addr = format!("{}:53", server);
511
512 match try_dns_query(&server_addr, &query, config.timeout) {
513 Ok(response) => return Ok(response),
514 Err(Error::Timeout) | Err(Error::Io(_)) => continue, Err(e) => return Err(e),
516 }
517 }
518
519 Err(Error::Timeout)
520}
521
522fn try_dns_query(server: &str, query: &[u8], timeout: u64) -> Result<Vec<u8>, Error> {
524 let socket = UdpSocket::bind("0.0.0.0:0")?;
526 socket.set_read_timeout(Some(Duration::from_secs(timeout)))?;
527
528 socket.connect(server)?;
530
531 socket.send(query)?;
533
534 let mut buffer = [0; 512];
536 let size = socket.recv(&mut buffer)?;
537
538 Ok(buffer[..size].to_vec())
539}
540
541pub fn lookup_mx_records(domain: &str) -> Result<Vec<MxRecord>, Error> {
543 lookup_mx_records_with_config(domain, None)
544}
545
546pub fn lookup_mx_records_with_config(
548 domain: &str,
549 config: Option<DnsConfig>,
550) -> Result<Vec<MxRecord>, Error> {
551 let response = lookup_dns_records(domain, DNS_TYPE_MX, config)?;
552 parse_mx_records(&response)
553}
554
555pub fn lookup_ip_addresses(hostname: &str) -> Result<Vec<IpAddr>, Error> {
557 lookup_ip_addresses_with_config(hostname, None)
558}
559
560pub fn lookup_ip_addresses_with_config(
562 hostname: &str,
563 config: Option<DnsConfig>,
564) -> Result<Vec<IpAddr>, Error> {
565 let mut ips = Vec::new();
566
567 match lookup_dns_records(hostname, DNS_TYPE_A, config.clone()) {
569 Ok(response) => match parse_ip_records(&response) {
570 Ok(v4_ips) => ips.extend(v4_ips),
571 Err(Error::NoRecordsFound) => {} Err(e) => return Err(e),
573 },
574 Err(Error::NoRecordsFound) => {} Err(e) => return Err(e),
576 }
577
578 match lookup_dns_records(hostname, DNS_TYPE_AAAA, config) {
580 Ok(response) => match parse_ip_records(&response) {
581 Ok(v6_ips) => ips.extend(v6_ips),
582 Err(Error::NoRecordsFound) => {} Err(e) => return Err(e),
584 },
585 Err(Error::NoRecordsFound) => {} Err(e) => return Err(e),
587 }
588
589 if ips.is_empty() {
590 return Err(Error::NoRecordsFound);
591 }
592
593 Ok(ips)
594}
595
596pub fn resolve_mx_server_ips(domain: &str) -> Result<Vec<ServerIpRecord>, Error> {
598 resolve_mx_server_ips_with_config(domain, None)
599}
600
601pub fn resolve_mx_server_ips_with_config(
603 domain: &str,
604 config: Option<DnsConfig>,
605) -> Result<Vec<ServerIpRecord>, Error> {
606 let mx_records = lookup_mx_records_with_config(domain, config.clone())?;
607
608 let mut server_ips = Vec::new();
609 for mx in mx_records {
610 match lookup_ip_addresses_with_config(&mx.server, config.clone()) {
611 Ok(ips) => {
612 server_ips.push(ServerIpRecord {
613 server: mx.server,
614 ip_addresses: ips,
615 });
616 }
617 Err(Error::NoRecordsFound) => {} Err(e) => return Err(e),
619 }
620 }
621
622 if server_ips.is_empty() {
623 return Err(Error::NoRecordsFound);
624 }
625
626 Ok(server_ips)
627}