|
@@ -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)
|