1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2use anyhow::{Result, bail};
3use core::{
4 mem::{self, MaybeUninit},
5 pin::Pin,
6 ptr,
7};
8
9use crate::{
10 dns::query::Query,
11 entropy::Entropy,
12 net::{IpAddr, Ipv4Addr, Ipv6Addr, Socket, SocketAddr},
13 runtime::Runtime,
14};
15
16mod private {
17 pub trait Sealed {}
18}
19
20macro_rules! impl_upstream {
21
22 ($($n:expr),*) => {
23 $(
24 impl private::Sealed for [SocketAddr; $n] {}
25 impl UpstreamSized for [SocketAddr; $n] {}
26 )*
27 };
28}
29
30impl_upstream!(1, 2, 3, 4, 5, 6, 7, 8);
31
32pub struct Upstream(usize, [MaybeUninit<SocketAddr>; 8]);
33
34impl Upstream {
35 fn get_addr(&self, alt: usize) -> Option<SocketAddr> {
36 if alt < self.0 {
37 Some(unsafe { self.1[alt].assume_init() })
38 } else {
39 None
40 }
41 }
42}
43
44pub trait UpstreamSized: private::Sealed {}
45
46impl Upstream {
47 const fn create<const N: usize>(addrs: [SocketAddr; N]) -> Upstream
48 where
49 [SocketAddr; N]: UpstreamSized,
50 {
51 let mut data = [MaybeUninit::uninit(); 8];
52 unsafe {
53 ptr::write(
54 (&mut data as *mut _ as *mut _),
55 (&addrs as *const _ as *const [MaybeUninit<SocketAddr>; N]).read(),
56 )
57 };
58 Self(N, data)
59 }
60}
61
62pub const GOOGLE: Upstream = Upstream::create([
63 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 53),
64 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 4, 4)), 53),
65 SocketAddr::new(
66 IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
67 53,
68 ),
69 SocketAddr::new(
70 IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844)),
71 53,
72 ),
73]);
74
75pub const CLOUDFLARE: Upstream = Upstream::create([
76 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 53),
77 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 0, 0, 1)), 53),
78 SocketAddr::new(
79 IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)),
80 53,
81 ),
82 SocketAddr::new(
83 IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)),
84 53,
85 ),
86]);
87
88#[cfg(not(any(target_os = "linux")))]
89compile_error!("unsupported platform, check back soon or make an issue/PR");
90
91pub fn platform_resolver<R: Runtime>() -> Result<Arc<dyn Resolver<R>>> {
92 let mut upstreams = Vec::new();
93 upstreams.push(GOOGLE);
94 upstreams.push(CLOUDFLARE);
95 let config = Config { upstreams };
96 #[cfg(target_os = "linux")]
97 Ok(Arc::new(linux::Resolver::<R>::create(config)?))
98}
99
100pub struct Config {
101 upstreams: Vec<Upstream>,
102}
103
104pub struct Request(Vec<u8>);
105
106impl Request {
107 pub fn build(entropy: &mut impl Entropy, query: Query) -> Request {
108 let mut packet = Vec::new();
109 packet.extend(entropy.entropy().unwrap().to_be_bytes()); packet.extend(&0x0100u16.to_be_bytes()); packet.extend(&1u16.to_be_bytes()); packet.extend(&0u16.to_be_bytes()); packet.extend(&0u16.to_be_bytes()); packet.extend(&0u16.to_be_bytes()); for label in query.domain.split('.') {
119 packet.push(label.len() as u8);
120 packet.extend(label.as_bytes());
121 }
122 packet.push(0); packet.extend(&(query.ty as u16).to_be_bytes()); packet.extend(&(Class::IN as u16).to_be_bytes()); Request(packet)
128 }
129}
130
131#[cfg(target_os = "linux")]
132mod linux {
133 use core::{marker::PhantomData, pin::Pin};
134
135 use crate::{
136 dns::{Config, Parse, Query, Request, Response},
137 entropy::Time,
138 net::Socket,
139 runtime::Runtime,
140 };
141 use alloc::{
142 boxed::Box,
143 string::{String, ToString},
144 sync::Arc,
145 vec,
146 };
147 use anyhow::{Result, bail};
148 use fast::sync::{Mutex, Waiter};
149 use futures::{AsyncReadExt, AsyncWriteExt, FutureExt};
150
151 pub struct Resolver<R: Runtime>(Arc<Mutex<Inner>>, PhantomData<R>);
152
153 pub struct Inner {
154 socket: Pin<Box<dyn Socket + Send>>,
155 }
156
157 pub struct Lookup {}
158
159 impl Future for Lookup {
160 type Output = Result<Response>;
161
162 fn poll(
163 self: core::pin::Pin<&mut Self>,
164 cx: &mut core::task::Context<'_>,
165 ) -> core::task::Poll<Self::Output> {
166 todo!()
167 }
168 }
169
170 impl<R: Runtime> super::Resolver<R> for Resolver<R> {
171 fn create(config: Config) -> Result<Self>
172 where
173 Self: Sized,
174 {
175 let mut range = (0..).into_iter();
176 let mut alt = 0;
177 try {
178 loop {
179 let Some(i) = range.next() else {
180 unreachable!();
181 };
182 match R::Socket::connect(
183 config.upstreams[i % config.upstreams.len()]
184 .get_addr(alt)
185 .ok_or(anyhow::Error::msg("all options exhausted"))?,
186 ) {
187 Ok(socket) => {
188 break Self(Arc::new(Mutex::new(Inner { socket })), PhantomData);
189 }
190 _ => (),
191 }
192 if i == config.upstreams.len() - 1 {
193 alt += 1;
194 }
195 }
196 }
197 }
198
199 fn resolve(
200 &self,
201 query: Query,
202 ) -> core::pin::Pin<alloc::boxed::Box<dyn Future<Output = Result<Response>>>> {
203 let Self(mutex, _) = self;
204 let mutex = mutex.clone();
205 Box::pin(R::spawn(async move || {
206 let packet = Request::build(&mut Time, query);
207 let mut guard = mutex.lock(Box::pin(Waiter::default())).await;
208 guard.socket.write_all(&packet.0).await;
209 let mut buf = vec![0u8; 512];
210 let Ok(count) = guard.socket.read(&mut buf).await else {
211 bail!("failed to read authority response");
212 };
213 buf.truncate(count);
214 Ok(Response::parse(&buf))
215 }))
216 }
217 }
218}
219
220pub use query::*;
221
222mod query {
223 use core::borrow::Borrow;
224
225 use alloc::{
226 borrow::ToOwned,
227 string::{String, ToString},
228 sync::Arc,
229 };
230
231 pub struct Query {
232 pub(crate) domain: String,
233 pub(crate) ty: Type,
234 }
235
236 #[repr(u16)]
237 pub enum Type {
238 A = 1, NS = 2, CNAME = 5, SOA = 6, PTR = 12, MX = 15, TXT = 16, AAAA = 28, SRV = 33, ANY = 255, }
249
250 #[repr(u16)]
251 pub enum Class {
252 IN = 1,
253 }
254
255 pub fn a(domain: &str) -> Query {
256 Query {
257 domain: domain.into(),
258 ty: Type::A,
259 }
260 }
261
262 pub fn aaaa(domain: &str) -> Query {
263 Query {
264 domain: domain.into(),
265 ty: Type::AAAA,
266 }
267 }
268
269 pub fn cname(domain: &str) -> Query {
270 Query {
271 domain: domain.into(),
272 ty: Type::CNAME,
273 }
274 }
275
276 pub fn mx(domain: &str) -> Query {
277 Query {
278 domain: domain.into(),
279 ty: Type::MX,
280 }
281 }
282
283 pub fn ns(domain: &str) -> Query {
284 Query {
285 domain: domain.into(),
286 ty: Type::NS,
287 }
288 }
289
290 pub fn ptr(domain: &str) -> Query {
291 Query {
292 domain: domain.into(),
293 ty: Type::PTR,
294 }
295 }
296
297 pub fn soa(domain: &str) -> Query {
298 Query {
299 domain: domain.into(),
300 ty: Type::SOA,
301 }
302 }
303
304 pub fn srv(domain: &str) -> Query {
305 Query {
306 domain: domain.into(),
307 ty: Type::SRV,
308 }
309 }
310
311 pub fn txt(domain: &str) -> Query {
312 Query {
313 domain: domain.into(),
314 ty: Type::TXT,
315 }
316 }
317}
318
319pub trait Resolver<R: Runtime> {
320 fn create(config: Config) -> Result<Self>
321 where
322 Self: Sized;
323 fn resolve(&self, query: Query) -> Pin<Box<dyn Future<Output = Result<Response>>>>;
324}
325
326pub trait Authority {
327 fn create<T: Socket>(config: Config, socket: impl Socket) -> Self
328 where
329 Self: Sized;
330 fn answer(&self, query: Query) -> Pin<Box<dyn Future<Output = Result<Response>>>>;
331}
332
333pub struct Response {
334 pub answers: Vec<Answer>,
335 pub authorities: Vec<Answer>,
336 pub additionals: Vec<Answer>,
337}
338
339impl Parse for Response {
340 fn parse(response: &[u8]) -> Result<Self>
341 where
342 Self: Sized,
343 {
344 if response.len() < 12 {
345 bail!("invalid response");
346 }
347
348 let flags = u16::from_be_bytes([response[2], response[3]]);
350 let rcode = flags & 0x000F;
351
352 if rcode == 3 {
353 bail!("name not found");
354 } else if rcode != 0 {
355 bail!("server failure");
356 }
357
358 let question_count = u16::from_be_bytes([response[4], response[5]]);
359 let answer_count = u16::from_be_bytes([response[6], response[7]]);
360 let authority_count = u16::from_be_bytes([response[8], response[9]]);
361 let additional_count = u16::from_be_bytes([response[10], response[11]]);
362
363 let mut offset = 12;
364
365 let mut query_type = None;
367 for _ in 0..question_count {
368 let (name_end, _name) = util::parse_name(response, offset)?;
369 offset = name_end;
370
371 if offset + 4 > response.len() {
372 bail!("invalid response");
373 }
374
375 let qtype = u16::from_be_bytes([response[offset], response[offset + 1]]);
376 let _qclass = u16::from_be_bytes([response[offset + 2], response[offset + 3]]);
377 offset += 4;
378
379 if query_type.is_none() {
381 query_type = Some(qtype);
382 }
383 }
384
385 let mut answers = Vec::new();
387 for _ in 0..answer_count {
388 if let Ok(answer) = parse_match_record(response, &mut offset) {
389 answers.push(answer);
390 }
391 }
392
393 let mut authorities = Vec::new();
395 for _ in 0..authority_count {
396 if let Ok(answer) = parse_match_record(response, &mut offset) {
397 authorities.push(answer);
398 }
399 }
400
401 let mut additionals = Vec::new();
403 for _ in 0..additional_count {
404 if let Ok(answer) = parse_match_record(response, &mut offset) {
405 additionals.push(answer);
406 }
407 }
408
409 Ok(Response {
410 answers,
411 authorities,
412 additionals,
413 })
414 }
415}
416
417fn parse_match_record(response: &[u8], offset: &mut usize) -> Result<Answer> {
418 let rtype = u16::from_be_bytes([response[*offset], response[*offset + 1]]);
419 *offset += 10;
420 Ok(match unsafe { mem::transmute::<_, Type>(rtype) } {
421 Type::A => Answer::A(Record::<Ipv4Addr>::parse(response)?),
422 Type::NS => Answer::AAAA(Record::<Ipv6Addr>::parse(response)?),
423 Type::CNAME => todo!(),
424 Type::SOA => todo!(),
425 Type::PTR => todo!(),
426 Type::MX => todo!(),
427 Type::TXT => todo!(),
428 Type::AAAA => todo!(),
429 Type::SRV => todo!(),
430 Type::ANY => todo!(),
431 })
432}
433
434pub enum Answer {
435 A(Record<Ipv4Addr>),
436 AAAA(Record<Ipv6Addr>),
437 CNAME(Record<String>),
438 MX(Record<(u16, String)>),
439 TXT(Record<Vec<String>>),
440 PTR(Record<String>),
441 NS(Record<String>),
442 SOA(Record<StartOfAuthority>),
443}
444
445pub struct StartOfAuthority {
446 master: String,
447 responsible: String,
448 serial: u32,
449 refresh: u32,
450 retry: u32,
451 expire: u32,
452 minimum: u32,
453}
454
455pub struct Record<T> {
456 name: String,
457 ttl: u32,
458 data: T,
459}
460
461mod util {
462 use alloc::string::String;
463 use anyhow::Result;
464 use anyhow::bail;
465
466 pub(crate) fn skip_name(data: &[u8], mut offset: usize) -> Result<usize> {
467 loop {
468 if offset >= data.len() {
469 bail!("invalid response");
470 }
471
472 let len = data[offset];
473
474 if len == 0 {
475 return Ok(offset + 1);
476 }
477
478 if (len & 0xC0) == 0xC0 {
479 return Ok(offset + 2);
480 }
481
482 offset += 1 + len as usize;
483 }
484 }
485
486 pub fn parse_name(data: &[u8], mut offset: usize) -> Result<(usize, String)> {
487 let mut name = String::new();
488 let mut jumps = 0;
489 let max_jumps = 5;
490 let original_offset = offset;
491 let mut jumped = false;
492
493 loop {
494 if jumps > max_jumps {
495 bail!("invalid response");
496 }
497
498 if offset >= data.len() {
499 bail!("invalid response");
500 }
501
502 let len = data[offset];
503
504 if len == 0 {
505 offset += 1;
506 break;
507 }
508
509 if (len & 0xC0) == 0xC0 {
510 if offset + 1 >= data.len() {
511 bail!("invalid response");
512 }
513
514 if !jumped {
515 jumped = true;
516 }
517
518 let ptr = (((len & 0x3F) as u16) << 8) | (data[offset + 1] as u16);
519 offset = ptr as usize;
520 jumps += 1;
521 continue;
522 }
523
524 offset += 1;
525
526 if offset + len as usize > data.len() {
527 bail!("invalid response");
528 }
529
530 if !name.is_empty() {
531 name.push('.');
532 }
533
534 let Ok(x) = core::str::from_utf8(&data[offset..offset + len as usize]) else {
535 bail!("failed to parse")
536 };
537
538 name.push_str(x);
539
540 offset += len as usize;
541 }
542
543 let final_offset = if jumped { original_offset + 2 } else { offset };
544
545 Ok((final_offset, name))
546 }
547}
548
549pub trait Parse {
550 fn parse(response: &[u8]) -> Result<Self>
551 where
552 Self: Sized;
553}
554
555impl Parse for Record<Ipv4Addr> {
556 fn parse(response: &[u8]) -> Result<Self> {
557 if response.len() < 12 {
558 bail!("invalid response");
559 }
560
561 let flags = u16::from_be_bytes([response[2], response[3]]);
563 let rcode = flags & 0x000F;
564
565 if rcode == 3 {
566 bail!("name not found");
567 } else if rcode != 0 {
568 bail!("server failure");
569 }
570
571 let question_count = u16::from_be_bytes([response[4], response[5]]);
572 let answer_count = u16::from_be_bytes([response[6], response[7]]);
573
574 if answer_count == 0 {
575 bail!("no answers");
576 }
577
578 let mut offset = 12;
580 for _ in 0..question_count {
581 offset = util::skip_name(response, offset)?;
582 offset += 4; }
584
585 let mut found_name = String::new();
587 let mut found_ttl = 0u32;
588 let mut found_addr = None;
589
590 for _ in 0..answer_count {
591 let (name_end, name) = util::parse_name(response, offset)?;
592 offset = name_end;
593
594 if offset + 10 > response.len() {
595 bail!("invalid response");
596 }
597
598 let rtype = u16::from_be_bytes([response[offset], response[offset + 1]]);
599 let rclass = u16::from_be_bytes([response[offset + 2], response[offset + 3]]);
600 let ttl = u32::from_be_bytes([
601 response[offset + 4],
602 response[offset + 5],
603 response[offset + 6],
604 response[offset + 7],
605 ]);
606 let rdlength = u16::from_be_bytes([response[offset + 8], response[offset + 9]]);
607 offset += 10;
608
609 if offset + rdlength as usize > response.len() {
610 bail!("invalid response");
611 }
612
613 if rtype == Type::A as u16 && rclass == Class::IN as u16 && rdlength == 4 {
615 if found_addr.is_none() {
616 found_name = name;
617 found_ttl = ttl;
618 found_addr = Some(Ipv4Addr::new(
619 response[offset],
620 response[offset + 1],
621 response[offset + 2],
622 response[offset + 3],
623 ));
624 }
625 }
626
627 offset += rdlength as usize;
628 }
629
630 match found_addr {
631 Some(addr) => Ok(Record {
632 name: found_name,
633 ttl: found_ttl,
634 data: addr,
635 }),
636 None => bail!("no A records found"),
637 }
638 }
639}
640
641impl Parse for Record<Ipv6Addr> {
642 fn parse(response: &[u8]) -> Result<Self> {
643 if response.len() < 12 {
644 bail!("invalid response");
645 }
646
647 let flags = u16::from_be_bytes([response[2], response[3]]);
649 let rcode = flags & 0x000F;
650
651 if rcode == 3 {
652 bail!("name not found");
653 } else if rcode != 0 {
654 bail!("server failure");
655 }
656
657 let question_count = u16::from_be_bytes([response[4], response[5]]);
658 let answer_count = u16::from_be_bytes([response[6], response[7]]);
659
660 if answer_count == 0 {
661 bail!("no answers");
662 }
663
664 let mut offset = 12;
666 for _ in 0..question_count {
667 offset = util::skip_name(response, offset)?;
668 offset += 4; }
670
671 let mut found_name = String::new();
673 let mut found_ttl = 0u32;
674 let mut found_addr = None;
675
676 for _ in 0..answer_count {
677 let (name_end, name) = util::parse_name(response, offset)?;
678 offset = name_end;
679
680 if offset + 10 > response.len() {
681 bail!("invalid response");
682 }
683
684 let rtype = u16::from_be_bytes([response[offset], response[offset + 1]]);
685 let rclass = u16::from_be_bytes([response[offset + 2], response[offset + 3]]);
686 let ttl = u32::from_be_bytes([
687 response[offset + 4],
688 response[offset + 5],
689 response[offset + 6],
690 response[offset + 7],
691 ]);
692 let rdlength = u16::from_be_bytes([response[offset + 8], response[offset + 9]]);
693 offset += 10;
694
695 if offset + rdlength as usize > response.len() {
696 bail!("invalid response");
697 }
698
699 if rtype == Type::AAAA as u16 && rclass == Class::IN as u16 && rdlength == 16 {
701 if found_addr.is_none() {
702 found_name = name;
703 found_ttl = ttl;
704
705 let mut octets = [0u8; 16];
707 octets.copy_from_slice(&response[offset..offset + 16]);
708 found_addr = Some(Ipv6Addr(unsafe { mem::transmute(octets) }));
709 }
710 }
711
712 offset += rdlength as usize;
713 }
714
715 match found_addr {
716 Some(addr) => Ok(Record {
717 name: found_name,
718 ttl: found_ttl,
719 data: addr,
720 }),
721 None => bail!("no AAAA records found"),
722 }
723 }
724}