Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further updates to rustls 0.20 #68

Merged
merged 9 commits into from
Sep 28, 2021

Conversation

djc
Copy link
Contributor

@djc djc commented Jul 26, 2021

Note: this targets the branch for #64.

@djc djc force-pushed the eliza/rustls-0.20 branch 2 times, most recently from 90732ba to 6945939 Compare July 26, 2021 13:06
if prev == buf.remaining() && would_block {
Poll::Ready(Err(err))
} else {
break;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make sure to yield UnexpectedEof on future reads in this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get UnexpectedEof from the reader().read() call, that means the reader currently has:

  • No received plaintext left to read, and
  • !peer_cleanly_closed, which expands to !(has_received_close_notify && !self.message_deframer.has_pending()), and
  • has_seen_eof on the input stream

has_seen_eof should be permanent, but if we have not yet received a CloseNotify it is possible that there are still frames pending in the deframer that contain a CloseNotify. So we should actually not eagerly return UnexpectedEof unless the reader gives it to us.

Do you agree?

@djc djc changed the title Adapt to RootCertStore API changes Further updates to rustls 0.20 Jul 26, 2021
@djc
Copy link
Contributor Author

djc commented Jul 30, 2021

In testing #69, we found that the AsyncRead impl is not quite right yet. In particular, the would_block guard on the Ok(0) case for the Connection::reader().read() call seems to cause issues (potentially because !self.eof prevents would_block from being set in case of EOF). Here's what I was toying with:

diff --git a/tokio-rustls/src/common/mod.rs b/tokio-rustls/src/common/mod.rs
index 77d371d..05789b1 100644
--- a/tokio-rustls/src/common/mod.rs
+++ b/tokio-rustls/src/common/mod.rs
@@ -229,63 +229,61 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Connection> AsyncRead for Stream
         let prev = buf.remaining();
 
         while buf.remaining() != 0 {
-            let mut would_block = false;
+            let mut io_pending = false;
 
             // read a packet
             while !self.eof && self.session.wants_read() {
-                match self.read_io(cx) {
+                match dbg!(self.read_io(cx)) {
                     Poll::Ready(Ok(0)) => {
                         self.eof = true;
                         break;
                     }
                     Poll::Ready(Ok(_)) => (),
                     Poll::Pending => {
-                        would_block = true;
+                        io_pending = true;
                         break;
                     }
                     Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
                 }
             }
 
-            return match self.session.reader().read(buf.initialize_unfilled()) {
-                Ok(0) if prev == buf.remaining() && would_block => Poll::Pending,
+            return match dbg!(self.session.reader().read(buf.initialize_unfilled())) {
+                // The TLS session has been closed cleanly, we can return since there will
+                // be no more data to read from the session.
+                Ok(0) => break,
+
+                // Read some bytes
                 Ok(n) => {
                     buf.advance(n);
-
-                    if self.eof || would_block {
+                    if self.eof || io_pending {
                         break;
                     } else {
                         continue;
                     }
                 }
 
-                // Rustls may return `WouldBlock` instead of `Ok(0)` if the
-                // stream has reached EOF without receiving a `CloseNotify` from
-                // the peer. Therefore, we must determine whether a `WouldBlock`
-                // here means EOF, or if there is more data to read.
+                // Rustls returns `WouldBlock` if it needs more data before returning.
                 Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
-                    if prev == buf.remaining() && would_block {
+                    if prev == buf.remaining() && io_pending {
                         Poll::Pending
-                    } else if self.eof || would_block {
+                    } else if self.eof || io_pending {
                         break;
                     } else {
                         continue;
                     }
                 }
+
+                // `UnexpectedEof` means the peer closed the connection without sending
+                // `CloseNotify`. Declare EOF and propagate the error.
                 Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
-                    self.eof = true;
-                    if prev == buf.remaining() && would_block {
+                    if prev == buf.remaining() {
                         Poll::Ready(Err(err))
                     } else {
                         break;
                     }
                 }
-                Err(ref err)
-                    if err.kind() == io::ErrorKind::ConnectionAborted
-                        && prev != buf.remaining() =>
-                {
-                    break
-                }
+
+                // This should be unreachable.
                 Err(err) => Poll::Ready(Err(err)),
             };
         }
diff --git a/tokio-rustls/src/common/test_stream.rs b/tokio-rustls/src/common/test_stream.rs
index 14766c0..a1730d1 100644
--- a/tokio-rustls/src/common/test_stream.rs
+++ b/tokio-rustls/src/common/test_stream.rs
@@ -217,8 +217,8 @@ async fn stream_eof() -> io::Result<()> {
     let mut stream = Stream::new(&mut good, &mut client).set_eof(true);
 
     let mut buf = Vec::new();
-    stream.read_to_end(&mut buf).await?;
-    assert_eq!(buf.len(), 0);
+    let result = stream.read_to_end(&mut buf).await;
+    assert_eq!(result.err().map(|e| e.kind()), Some(io::ErrorKind::UnexpectedEof));
 
     Ok(()) as io::Result<()>
 }

This still fails stream_good for reasons that I can't figure out right now. (I renamed would_block because it gets a bit confusing now that rustls can also return WouldBlock directly.)

@djc djc force-pushed the eliza/rustls-0.20 branch 13 times, most recently from 1bbc747 to 698b728 Compare August 3, 2021 12:30
@djc
Copy link
Contributor Author

djc commented Aug 10, 2021

@hawkw would you have a chance to look at this? Would be nice to figure out if we have the reader semantics right.

@hawkw
Copy link
Member

hawkw commented Aug 12, 2021

@hawkw would you have a chance to look at this? Would be nice to figure out if we have the reader semantics right.

Checking it out right now, sorry it took me a few days to get to this!

@djc
Copy link
Contributor Author

djc commented Aug 30, 2021

@hawkw gentle ping? 🙂

@djc djc mentioned this pull request Sep 8, 2021
3 tasks
@quininer
Copy link
Member

This looks great to me, are you ready to merge?

@djc
Copy link
Contributor Author

djc commented Sep 11, 2021

I think this is ready to be merged into the target branch, yes. Probably also fine to have on master.

Copy link
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry it took me so long to look at this, seems good overall!

tokio-rustls/tests/badssl.rs Outdated Show resolved Hide resolved
tokio-rustls/tests/badssl.rs Outdated Show resolved Hide resolved
if prev == buf.remaining() && would_block {
Poll::Ready(Err(err))
} else {
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think so?

@djc
Copy link
Contributor Author

djc commented Sep 15, 2021

Reverted the assert changes and updated to current rustls main. On further investigation, I convinced myself the change relating to UnexpectedEof is not necessary after all.

@djc
Copy link
Contributor Author

djc commented Sep 26, 2021

Updated to final rustls 0.20 release.

@quininer
Copy link
Member

If there are no more comments, I plan to merge this PR in next two days.

@paolobarbolini
Copy link
Contributor

If there are no more comments, I plan to merge this PR in next two days.

It's be cool to expose the tls12 rustls feature

@djc
Copy link
Contributor Author

djc commented Sep 27, 2021

@paolobarbolini good suggestion. Added a commit that forwards the logging and tls12 features, enabling them by default.

@quininer quininer merged commit 6bb5f6c into tokio-rs:eliza/rustls-0.20 Sep 28, 2021
quininer pushed a commit that referenced this pull request Sep 28, 2021
* update to rustls 0.20

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* track simple renamings in rustls

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* use reader/writer methods

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* fix find and replace

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* use rustls-pemfile crate for pem file parsing

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update misc api breakage

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update client example with api changes

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update server example with new APIs

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update test_stream test

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update tests to use new APIs

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* rm unused imports

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* handle rustls `WouldBlock` on eof

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* expect rustls to return wouldblock in tests

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* i think this is *actually* the right EOF behavior

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* bump version

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* okay that seems to fix it

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* update to track builder API changes

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* actually shutdown read side on close notify

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* Further updates to rustls 0.20 (#68)

* Adapt to RootCertStore API changes

* Handle UnexpectedEof errors

* Rename would_block to io_pending

* Try to make badssl test failures more verbose

* Rebuild AsyncRead impl

* Upgrade to current rustls

* Revert to using assert!()

* Update to rustls 0.20

* Forward rustls features

Co-authored-by: Dirkjan Ochtman <dirkjan@ochtman.nl>
@quininer quininer added this to the rustls 0.20 milestone Oct 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants