aboutsummaryrefslogtreecommitdiff
path: root/doh
diff options
context:
space:
mode:
authorMike Yu <yumike@google.com>2022-02-10 16:43:47 +0800
committerMike Yu <yumike@google.com>2022-02-23 15:47:02 +0800
commitdfbcdde55ae5d92e389bf2f7fceb7d29f30c3cdc (patch)
tree5dbd5574ffdca7dd751e504178f5c8d2a03d2fc1 /doh
parentf4fa285df0f02115b83e82247aaab39c7462f4e3 (diff)
downloadDnsResolver-dfbcdde55ae5d92e389bf2f7fceb7d29f30c3cdc.tar.gz
DoH: Handle connection draining state
When a CONNECTION_CLOSE frame is received, the connection enters draining state (RFC 9000 section 10.2.2). When in draining state, no more packets can be sent. So the DoH client needs to initiate a new connection if necessary; otherwise, subsequent DNS requests might fail. As a connection enters draining state upon a CONNECTION_CLOSE frame is received, DNS packets sent prior to the frame received might fail. For example, in Quiche (version 0.9.0) implementation, the DoH client can send some DNS packets out on the same connection after predefined max_idle_timeout seconds (55 seconds) because of large Probe Timeout (RFC 9002 section 6.2). It might lead to the DoH server responding with a CONNECTION_CLOSE frame, and result in those DNS queries failed. Bug: 215818810 Test: cd packages/modules/DnsResolver && atest Test: manual test (see bug 215818810 comment#2) verified that the DoH client can create a new connection for subsequent DNS requests after detecting a connection being draining state. Change-Id: Ibd1667aeb9e1528776392e6f91ddb13b66637f33
Diffstat (limited to 'doh')
-rw-r--r--doh/connection/driver.rs36
1 files changed, 35 insertions, 1 deletions
diff --git a/doh/connection/driver.rs b/doh/connection/driver.rs
index 500dc923..ef22b72c 100644
--- a/doh/connection/driver.rs
+++ b/doh/connection/driver.rs
@@ -154,7 +154,7 @@ impl Driver {
if self.quiche_conn.is_closed() {
// TODO: Also log local_error() once Quiche 0.10.0 is available.
debug!(
- "Connection closed on network {}, peer_error={:?}",
+ "Connection closed on network {}, peer_error={:x?}",
self.net_id,
self.quiche_conn.peer_error()
);
@@ -166,6 +166,28 @@ impl Driver {
}
}
+ fn handle_draining(&mut self) {
+ if self.quiche_conn.is_draining() {
+ // TODO: avoid running the code below more than once.
+ // TODO: Also log local_error() once Quiche 0.10.0 is available.
+ debug!(
+ "Connection is draining on network {}, peer_error={:x?}",
+ self.net_id,
+ self.quiche_conn.peer_error()
+ );
+ // We don't care if the receiver has hung up
+ let _ = self.status_tx.send(Status::Dead { session: self.quiche_conn.session() });
+
+ self.request_rx.close();
+ // Drain the pending DNS requests from the queue to make their corresponding future
+ // tasks return some error quickly rather than timeout. However, the DNS requests
+ // that has been sent will still time out.
+ // TODO: re-issue the outstanding DNS requests, such as passing H3Driver.requests
+ // along with Status::Dead to the `Network` that can re-issue the DNS requests.
+ while self.request_rx.try_recv().is_ok() {}
+ }
+ }
+
async fn drive_once(mut self) -> Result<Self> {
let timer = optional_timeout(self.quiche_conn.timeout(), self.net_id);
select! {
@@ -191,6 +213,12 @@ impl Driver {
let _ = self.status_tx.send(Status::QUIC);
}
+ // If the connection has entered draining state (the server is closing the connection),
+ // tell the status watcher not to use the connection. Besides, per Quiche document,
+ // the connection should not be dropped until is_closed() returns true.
+ // This tokio task will become unowned and get dropped when is_closed() returns true.
+ self.handle_draining();
+
// If the connection has closed, tear down
self.handle_closed()?;
@@ -276,6 +304,12 @@ impl H3Driver {
// Process any incoming HTTP/3 events
self.flush_h3().await?;
+ // If the connection has entered draining state (the server is closing the connection),
+ // tell the status watcher not to use the connection. Besides, per Quiche document,
+ // the connection should not be dropped until is_closed() returns true.
+ // This tokio task will become unowned and get dropped when is_closed() returns true.
+ self.driver.handle_draining();
+
// If the connection has closed, tear down
self.driver.handle_closed()
}