Commit 8e01f8d2 authored by Alex Crichton's avatar Alex Crichton
Browse files

Handle zero-length reads/writes

This commit adds some short-circuits for zero-length reads/writes to
`SslStream`. Because OpenSSL returns 0 on error, then we could mistakenly
confuse a 0-length success as an actual error, so we avoid writing or reading 0
bytes by returning quickly with a success.
parent 3cfcf138
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1506,6 +1506,15 @@ impl<S: Read + Write> SslStream<S> {
    /// This is particularly useful with a nonblocking socket, where the error
    /// value will identify if OpenSSL is waiting on read or write readiness.
    pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        // The intepretation of the return code here is a little odd with a
        // zero-length write. OpenSSL will likely correctly report back to us
        // that it read zero bytes, but zero is also the sentinel for "error".
        // To avoid that confusion short-circuit that logic and return quickly
        // if `buf` has a length of zero.
        if buf.len() == 0 {
            return Ok(0)
        }

        let ret = self.ssl.read(buf);
        if ret > 0 {
            Ok(ret as usize)
@@ -1523,6 +1532,11 @@ impl<S: Read + Write> SslStream<S> {
    /// This is particularly useful with a nonblocking socket, where the error
    /// value will identify if OpenSSL is waiting on read or write readiness.
    pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        // See above for why we short-circuit on zero-length buffers
        if buf.len() == 0 {
            return Ok(0)
        }

        let ret = self.ssl.write(buf);
        if ret > 0 {
            Ok(ret as usize)
+10 −0
Original line number Diff line number Diff line
@@ -421,6 +421,16 @@ fn test_write() {
    stream.flush().unwrap();
}

#[test]
fn zero_length_buffers() {
    let (_s, stream) = Server::new();
    let ctx = SslContext::builder(SslMethod::tls()).unwrap();
    let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();

    assert_eq!(stream.write(b"").unwrap(), 0);
    assert_eq!(stream.read(&mut []).unwrap(), 0);
}

run_test!(get_peer_certificate, |method, stream| {
    let ctx = SslContext::builder(method).unwrap();
    let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();