diff options
author | Mike Yu <yumike@google.com> | 2022-02-10 16:43:47 +0800 |
---|---|---|
committer | Mike Yu <yumike@google.com> | 2022-02-23 15:47:02 +0800 |
commit | dfbcdde55ae5d92e389bf2f7fceb7d29f30c3cdc (patch) | |
tree | 5dbd5574ffdca7dd751e504178f5c8d2a03d2fc1 /doh | |
parent | f4fa285df0f02115b83e82247aaab39c7462f4e3 (diff) | |
download | DnsResolver-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.rs | 36 |
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() } |