From 228b41f0d034c2c5fef7e8c87fce50e548810647 Mon Sep 17 00:00:00 2001 From: Sam O'Connor Date: Mon, 16 Apr 2018 20:52:28 +1000 Subject: [PATCH 1/3] Check for close_notify message in Base.isopen(ctx::SSLContext) --- src/ssl.jl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/ssl.jl b/src/ssl.jl index b1ba038..24d9b13 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -270,7 +270,33 @@ function Base.close(ctx::SSLContext) nothing end -Base.isopen(ctx::SSLContext) = ctx.isopen && isopen(ctx.bio) +function Base.isopen(ctx::SSLContext) + + if !ctx.isopen || !isopen(ctx.bio) + return false + end + + if bytesavailable(ctx.bio) == 0 + # Ensure that libuv is processing data from this socket in case the peer + # has sent a close_notify message on an otherwise idle connection. + # https://tools.ietf.org/html/rfc5246#section-7.2.1 + Base.start_reading(ctx.bio) + yield() + end + + # Zero-byte read causes MbedTLS to process buffered data + # (which may include a close_notify message). + @lockdata ctx begin + n = ccall((:mbedtls_ssl_read, libmbedtls), Cint, + (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + ctx.data, C_NULL, 0) + if n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY + ctx.isopen = false + end + end + + return ctx.isopen && isopen(ctx.bio) +end function get_peer_cert(ctx::SSLContext) data = ccall((:mbedtls_ssl_get_peer_cert, libmbedtls), Ptr{Cvoid}, (Ptr{Cvoid},), ctx.data) From 37bca395354b1172d5720294abf215261909ea42 Mon Sep 17 00:00:00 2001 From: Sam O'Connor Date: Tue, 17 Apr 2018 10:38:43 +1000 Subject: [PATCH 2/3] In handshake(ctx::SSLContext): - @schedule task to keep socket in active state - and monitor for MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY Whenever MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY is recieved call close(::SSLContext). move zero-byte read into function: decrypt_available_bytes --- src/ssl.jl | 57 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 24d9b13..cc5c21c 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -179,6 +179,17 @@ function handshake(ctx::SSLContext) wait(ctx) end ctx.isopen = true + + @schedule while ctx.isopen && isopen(ctx.bio) + # Ensure that libuv is reading data from the socket in case the peer + # has sent a close_notify message on an otherwise idle connection. + # https://tools.ietf.org/html/rfc5246#section-7.2.1 + Base.start_reading(ctx.bio) + wait(ctx.bio.readnotify) + yield() + decrypt_available_bytes(ctx) + end + return end @@ -223,7 +234,7 @@ function Base.unsafe_read(ctx::SSLContext, buf::Ptr{UInt8}, nbytes::UInt; err=tr ctx.data, buf + nread, nbytes - nread) end if n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || n == 0 - ctx.isopen = false + close(ctx) err ? throw(EOFError()) : return nread elseif n == MBEDTLS_ERR_SSL_WANT_READ wait(ctx) @@ -276,26 +287,28 @@ function Base.isopen(ctx::SSLContext) return false end - if bytesavailable(ctx.bio) == 0 - # Ensure that libuv is processing data from this socket in case the peer - # has sent a close_notify message on an otherwise idle connection. - # https://tools.ietf.org/html/rfc5246#section-7.2.1 - Base.start_reading(ctx.bio) - yield() - end + decrypt_available_bytes(ctx) - # Zero-byte read causes MbedTLS to process buffered data - # (which may include a close_notify message). - @lockdata ctx begin - n = ccall((:mbedtls_ssl_read, libmbedtls), Cint, + return ctx.isopen && isopen(ctx.bio) +end + +function decrypt_available_bytes(ctx::SSLContext) + + # Zero-byte read causes MbedTLS to call f_recv (always non-blocking) + # and decrypt any bytes that are already in the LibuvStream read buffer. + # https://esp32.com/viewtopic.php?t=1101#p4884 + n = @lockdata ctx begin + ccall((:mbedtls_ssl_read, libmbedtls), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), ctx.data, C_NULL, 0) - if n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY - ctx.isopen = false - end end - - return ctx.isopen && isopen(ctx.bio) + if n == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY + close(ctx) + elseif n == MBEDTLS_ERR_SSL_WANT_READ + # ignore + elseif n < 0 + mbed_err(n) + end end function get_peer_cert(ctx::SSLContext) @@ -325,15 +338,9 @@ end function _bytesavailable(ctx::SSLContext) - @lockdata ctx begin + decrypt_available_bytes(ctx) - # First do a zero-byte read. - # This causes MbedTLS to call f_recv (which is always non-blocking) - # and decrypt any bytes that are already in the LibuvStream read buffer. - # https://esp32.com/viewtopic.php?t=1101#p4884 - ccall((:mbedtls_ssl_read, libmbedtls), Cint, - (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), - ctx.data, C_NULL, 0) + @lockdata ctx begin # Now that the bufferd bytes have been processed, find out how many # decrypted bytes are available. From 697c5d54ff082520ce53ae8a24275ae99fad48e2 Mon Sep 17 00:00:00 2001 From: Sam O'Connor Date: Tue, 17 Apr 2018 11:01:01 +1000 Subject: [PATCH 3/3] no need to call decrypt_available_bytes in async start_reading loop, calling isopen(::SSLContext) does that for us --- src/ssl.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index cc5c21c..f3c7bba 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -180,14 +180,13 @@ function handshake(ctx::SSLContext) end ctx.isopen = true - @schedule while ctx.isopen && isopen(ctx.bio) + @schedule while isopen(ctx) # Ensure that libuv is reading data from the socket in case the peer # has sent a close_notify message on an otherwise idle connection. # https://tools.ietf.org/html/rfc5246#section-7.2.1 Base.start_reading(ctx.bio) wait(ctx.bio.readnotify) yield() - decrypt_available_bytes(ctx) end return