Browse Source

dns: Added timeout and trying to use different servers

Emil Fresk 3 years ago
parent
commit
dd18f3c450
2 changed files with 46 additions and 5 deletions
  1. 1 1
      Cargo.toml
  2. 45 4
      src/socket/dns.rs

+ 1 - 1
Cargo.toml

@@ -48,7 +48,7 @@ verbose = []
 "proto-ipv6" = []
 "proto-sixlowpan" = ["proto-ipv6"]
 "proto-dns" = []
- 
+
 "socket" = []
 "socket-raw" = ["socket"]
 "socket-udp" = ["socket"]

+ 45 - 4
src/socket/dns.rs

@@ -11,8 +11,9 @@ use crate::{rand, Error, Result};
 const DNS_PORT: u16 = 53;
 const MAX_NAME_LEN: usize = 255;
 const MAX_ADDRESS_COUNT: usize = 4;
-const RETRANSMIT_DELAY: Duration = Duration::from_millis(1000);
-const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10000);
+const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000);
+const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000);
+const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs
 
 /// State for an in-progress DNS query.
 ///
@@ -28,6 +29,7 @@ pub struct DnsQuery {
 enum State {
     Pending(PendingQuery),
     Completed(CompletedQuery),
+    Failure,
 }
 
 #[derive(Debug)]
@@ -38,8 +40,11 @@ struct PendingQuery {
     port: u16, // UDP port (src for request, dst for response)
     txid: u16, // transaction ID
 
+    timeout_at: Option<Instant>,
     retransmit_at: Instant,
     delay: Duration,
+
+    server_idx: usize,
 }
 
 #[derive(Debug)]
@@ -88,7 +93,7 @@ impl<'a> DnsSocket<'a> {
 
         // Fill the rest with no address
         for s in local_servers {
-            *s = IpAddress::Unspecified;
+            *s = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); // TODO
         }
     }
 
@@ -147,7 +152,9 @@ impl<'a> DnsSocket<'a> {
                 txid: cx.rand().rand_u16(),
                 port: cx.rand().rand_source_port(),
                 delay: RETRANSMIT_DELAY,
+                timeout_at: None,
                 retransmit_at: Instant::ZERO,
+                server_idx: 0,
             }),
         });
         Ok(handle)
@@ -168,6 +175,10 @@ impl<'a> DnsSocket<'a> {
                 *slot = None; // Free up the slot for recycling.
                 Ok(res)
             }
+            State::Failure => {
+                *slot = None; // Free up the slot for recycling.
+                Err(Error::Unaddressable)
+            }
         }
     }
 
@@ -303,6 +314,35 @@ impl<'a> DnsSocket<'a> {
 
         for q in self.queries.iter_mut().flatten() {
             if let State::Pending(pq) = &mut q.state {
+                let timeout = if let Some(timeout) = pq.timeout_at {
+                    timeout
+                } else {
+                    let v = cx.now() + RETRANSMIT_TIMEOUT;
+                    pq.timeout_at = Some(v);
+                    v
+                };
+
+                // Check timeout
+                if timeout < cx.now() {
+                    pq.server_idx += 1;
+                    if pq.server_idx < self.servers.len() {
+                        // DNS timeout
+                        pq.timeout_at = Some(cx.now() + RETRANSMIT_TIMEOUT);
+                        pq.retransmit_at = Instant::ZERO;
+                        pq.delay = RETRANSMIT_DELAY;
+                    } else {
+                        // Query failure
+                        q.state = State::Failure;
+                        continue;
+                    }
+                }
+
+                // Check so the IP address is valid
+                if self.servers[pq.server_idx].is_unspecified() {
+                    q.state = State::Failure;
+                    continue;
+                }
+
                 if pq.retransmit_at > cx.now() {
                     // query is waiting for retransmit
                     continue;
@@ -327,7 +367,7 @@ impl<'a> DnsSocket<'a> {
                     dst_port: 53,
                 };
 
-                let dst_addr = self.servers[0];
+                let dst_addr = self.servers[pq.server_idx];
                 let src_addr = cx.get_source_address(dst_addr).unwrap(); // TODO remove unwrap
                 let ip_repr = IpRepr::new(
                     src_addr,
@@ -367,6 +407,7 @@ impl<'a> DnsSocket<'a> {
             .filter_map(|q| match &q.state {
                 State::Pending(pq) => Some(PollAt::Time(pq.retransmit_at)),
                 State::Completed(_) => None,
+                State::Failure => None,
             })
             .min()
             .unwrap_or(PollAt::Ingress)