From 57f035df91e89bcfe0a51c07def27df4ca815856 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Wed, 10 May 2023 14:17:44 -0700 Subject: [PATCH 01/17] Fix writing to BIO --- src/ssl.jl | 4 ++-- test/runtests.jl | 32 ++++++++------------------------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 3f94309..db42eb2 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -35,7 +35,7 @@ bio_clear_flags(bio::BIO) = bio_set_flags(bio, 0x00) function on_bio_stream_read(bio::BIO, out::Ptr{Cchar}, outlen::Cint) try bio_clear_flags(bio) - io = bio_get_data(bio)::TCPSocket + io = bio_get_data(bio)::IO n = bytesavailable(io) if n == 0 bio_set_read_retry(bio) @@ -51,7 +51,7 @@ end function on_bio_stream_write(bio::BIO, in::Ptr{Cchar}, inlen::Cint)::Cint try - io = bio_get_data(bio)::TCPSocket + io = bio_get_data(bio)::IO written = unsafe_write(io, in, inlen) return Cint(written) catch e diff --git a/test/runtests.jl b/test/runtests.jl index 96d4449..13c9a9d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -285,18 +285,10 @@ end sign_certificate(x509_certificate, evp_pkey) - port, server = Sockets.listenany(10000) - iob = connect(port) - sob = accept(server) - local cert_pem - try - write(iob, x509_certificate) - cert_pem = String(readavailable(sob)) - finally - close(iob) - close(sob) - close(server) - end + iob = IOBuffer() + write(iob, x509_certificate) + + cert_pem = String(take!(iob)) x509_certificate2 = X509Certificate(cert_pem) @@ -575,18 +567,10 @@ end @testset "SerializePrivateKey" begin evp_pkey = EvpPKey(rsa_generate_key()) - port, server = Sockets.listenany(10000) - iob = connect(port) - sob = accept(server) - local pkey_pem - try - write(iob, evp_pkey) - pkey_pem = String(readavailable(sob)) - finally - close(iob) - close(sob) - close(server) - end + iob = IOBuffer() + write(iob, evp_pkey) + + pkey_pem = String(take!(iob)) @test startswith(pkey_pem, "-----BEGIN PRIVATE KEY-----") From bfd33739cb06fd98e36bd8852028200529ffd267 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Sat, 13 May 2023 17:12:57 -0700 Subject: [PATCH 02/17] on_bio_stream_read returns -1 if read fails, calls eof before calling bytesavailable on_bio_stream_write return -1 if write fails (bss_sock.c) This enables server side mode, however the logic in the bio still does not match what OpenSSL is doing. --- src/ssl.jl | 6 +++-- test/http_helpers.jl | 58 ++++++++++++++++++-------------------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index db42eb2..78f872c 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -29,6 +29,7 @@ function bio_set_flags(bio::BIO, flags) (BIO, Cint), bio, flags) end + bio_set_read_retry(bio::BIO) = bio_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY) bio_clear_flags(bio::BIO) = bio_set_flags(bio, 0x00) @@ -36,6 +37,7 @@ function on_bio_stream_read(bio::BIO, out::Ptr{Cchar}, outlen::Cint) try bio_clear_flags(bio) io = bio_get_data(bio)::IO + eof(io) n = bytesavailable(io) if n == 0 bio_set_read_retry(bio) @@ -45,7 +47,7 @@ function on_bio_stream_read(bio::BIO, out::Ptr{Cchar}, outlen::Cint) return Cint(min(n, outlen)) catch e # we don't want to throw a Julia exception from a C callback - return Cint(0) + return Cint(-1) end end @@ -56,7 +58,7 @@ function on_bio_stream_write(bio::BIO, in::Ptr{Cchar}, inlen::Cint)::Cint return Cint(written) catch e # we don't want to throw a Julia exception from a C callback - return Cint(0) + return Cint(-1) end end diff --git a/test/http_helpers.jl b/test/http_helpers.jl index 11cc713..df0f6d8 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -1,6 +1,7 @@ using Dates using OpenSSL using Sockets +using Test function test_server() x509_certificate = X509Certificate() @@ -22,40 +23,34 @@ function test_server() sign_certificate(x509_certificate, evp_pkey) server_socket = listen(5000) - try - accepted_socket = accept(server_socket) + accepted_socket = accept(server_socket) - # Create and configure server SSLContext. - ssl_ctx = OpenSSL.SSLContext(OpenSSL.TLSServerMethod()) - _ = OpenSSL.ssl_set_options(ssl_ctx, OpenSSL.SSL_OP_NO_COMPRESSION) + # Create and configure server SSLContext. + ssl_ctx = OpenSSL.SSLContext(OpenSSL.TLSServerMethod()) + _ = OpenSSL.ssl_set_options(ssl_ctx, OpenSSL.SSL_OP_NO_COMPRESSION) - OpenSSL.ssl_set_ciphersuites(ssl_ctx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256") - OpenSSL.ssl_use_certificate(ssl_ctx, x509_certificate) - OpenSSL.ssl_use_private_key(ssl_ctx, evp_pkey) + OpenSSL.ssl_set_ciphersuites(ssl_ctx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256") + OpenSSL.ssl_use_certificate(ssl_ctx, x509_certificate) + OpenSSL.ssl_use_private_key(ssl_ctx, evp_pkey) - ssl = SSLStream(ssl_ctx, accepted_socket) + ssl = SSLStream(ssl_ctx, accepted_socket) - OpenSSL.accept(ssl) + OpenSSL.accept(ssl) - @test !eof(ssl) - request = readavailable(ssl) - reply = "reply: $(String(request))" + @test !eof(ssl) + request = readavailable(ssl) + reply = "reply: $(String(request))" + + # eof(ssl) will block - # eof(ssl) will block + # Verify the are no more bytes available in the stream. + @test bytesavailable(ssl) == 0 - # Verify the are no more bytes available in the stream. - @test bytesavailable(ssl) == 0 + unsafe_write(ssl, pointer(reply), length(reply)) - write(ssl, reply) + close(ssl) + finalize(ssl_ctx) - try - close(ssl) - catch - end - finalize(ssl_ctx) - finally - close(server_socket) - end return nothing end @@ -63,13 +58,12 @@ function test_client() tcp_stream = connect(5000) ssl_ctx = OpenSSL.SSLContext(OpenSSL.TLSClientMethod()) - ssl_options = OpenSSL.ssl_set_options(ssl_ctx, OpenSSL.SSL_OP_NO_COMPRESSION) + _ = OpenSSL.ssl_set_options(ssl_ctx, OpenSSL.SSL_OP_NO_COMPRESSION) # Create SSL stream. ssl = SSLStream(ssl_ctx, tcp_stream) - #TODO expose connect - OpenSSL.connect(ssl) + connect(ssl; require_ssl_verification = false) # Verify the server certificate. x509_server_cert = OpenSSL.get_peer_certificate(ssl) @@ -87,12 +81,8 @@ function test_client() response_str = String(readavailable(ssl)) - @test response_str == "reply: $request_str" + @test response_str == "reply: $(request_str)" - try - close(ssl) - catch - end + close(ssl) finalize(ssl_ctx) - return nothing end From ee21d15c4a523cf0531abbba5b711fa71ce7eb44 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Thu, 18 May 2023 12:25:48 -0700 Subject: [PATCH 03/17] create a new structure BIOStream{T<:IO} to handle BIO using IO --- src/OpenSSL.jl | 155 +++++++++++++++++++++++++++++++++++++++++++++-- src/ssl.jl | 97 ----------------------------- test/runtests.jl | 7 +-- 3 files changed, 152 insertions(+), 107 deletions(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index 91176a3..d1e935e 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -210,6 +210,18 @@ const BIO_TYPE_SOURCE_SINK = 0x0400 # BIO_TYPE_START = 128) +""" + These are used in the following macros and are passed to BIO_ctrl(). +""" +#@enum(BIOFlags::Cint, +# +#); +const BIO_FLAGS_SHOULD_RETRY = 0x08 +const BIO_FLAGS_READ = 0x01 +const BIO_FLAGS_WRITE = 0x02 +const BIO_FLAGS_IO_SPECIAL = 0x04 + + # Some values are reserved until OpenSSL 3.0.0 because they were previously # included in SSL_OP_ALL in a 1.1.x release. @bitflag SSLOptions::Culong begin @@ -1544,7 +1556,7 @@ mutable struct BIO Creates a BIO object using IO stream method. The BIO object is not registered with the finalizer. """ - function BIO(data=nothing; finalize::Bool=true) + function BIO(io::T; finalize::Bool=true) where {T<:IO} bio = ccall( (:BIO_new, libcrypto), Ptr{Cvoid}, @@ -1557,7 +1569,7 @@ mutable struct BIO bio = new(bio) finalize && finalizer(free, bio) - # note that `data` must be held as a reference somewhere else + # note that `io` must be held as a reference somewhere else # since it is not referenced by the BIO directly # e.g. in SSLStream, we keep the `io` reference that is passed to # the read/write BIOs @@ -1566,7 +1578,7 @@ mutable struct BIO Cvoid, (BIO, Ptr{Cvoid}), bio, - data === nothing ? C_NULL : pointer_from_objref(data)) + pointer_from_objref(io)) # Set BIO as non-blocking ccall( @@ -1577,6 +1589,7 @@ mutable struct BIO 102, 1, C_NULL) + # Mark BIO as initalized. ccall( (:BIO_set_init, libcrypto), @@ -1674,7 +1687,6 @@ end """ BIO write. """ -## throw error here function Base.unsafe_write(bio::BIO, out_buffer::Ptr{UInt8}, out_length::Int) result = ccall( (:BIO_write, libcrypto), @@ -1690,6 +1702,139 @@ end Base.write(bio::BIO, out_data) = return unsafe_write(bio, pointer(out_data), length(out_data)) +""" + BIOStream. + BIO with IO. +""" +mutable struct BIOStream{T<:IO} + bio::Ptr{Cvoid} +end + +function bio_stream_get_io(bio_stream::BIOStream{T})::T where {T<:IO} + data = ccall( + (:BIO_get_data, libcrypto), + Ptr{Cvoid}, + (BIOStream{T},), + bio_stream) + return unsafe_pointer_to_objref(data)::T +end + +""" + BIOStream callbacks. +""" + +""" + Called to initialize new BIO Stream object. +""" +on_bio_stream_create(::BIOStream{T}) where {T<:IO} = Cint(1) +on_bio_stream_destroy(::BIOStream{T}) where {T<:IO} = Cint(0) + +#function bio_get_data(bio::BIO) +# data = ccall( +# (:BIO_get_data, libcrypto), +# Ptr{Cvoid}, +# (BIO,), +# bio) +# return unsafe_pointer_to_objref(data) +#end + +function bio_set_flags(bio_stream::BIOStream{T}, flags) where {T<:IO} + return ccall( + (:BIO_set_flags, libcrypto), + Cint, + (BIOStream{T}, Cint), + bio_stream, flags) +end + +bio_set_read_retry(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY) +bio_clear_flags(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, 0x00) + +""" +static int fd_read(BIO *b, char *out, int outl) +{ + int ret = 0; + + if (out != NULL) { + clear_sys_error(); + ret = UP_read(b->num, out, outl); + BIO_clear_retry_flags(b); + if (ret <= 0) { + if (BIO_fd_should_retry(ret)) + BIO_set_retry_read(b); + else if (ret == 0) + b->flags |= BIO_FLAGS_IN_EOF; + } + } + return ret; +} +""" + +function on_bio_stream_read(bio_stream::BIOStream{T}, out::Ptr{Cchar}, outlen::Cint) where {T<:IO} + try + bio_clear_flags(bio_stream) + io::T = bio_stream_get_io(bio_stream) + n = bytesavailable(io) + if n == 0 + eof(io) + n = bytesavailable(io) + end + + if n == 0 + bio_set_read_retry(bio_stream) + return Cint(0) + end + unsafe_read(io, out, min(UInt(n), outlen)) + return Cint(min(n, outlen)) + catch e + # we don't want to throw a Julia exception from a C callback + return Cint(-1) + end +end + +function on_bio_stream_write(bio_stream::BIOStream{T}, in::Ptr{Cchar}, inlen::Cint)::Cint where {T<:IO} + try + io::T = bio_stream_get_io(bio_stream) + written = unsafe_write(io, in, inlen) + return Cint(written) + catch e + # we don't want to throw a Julia exception from a C callback + return Cint(-1) + end +end + +on_bio_stream_puts(bio_stream::BIOStream{T}, in::Ptr{Cchar}) where {T<:IO} = Cint(0) + +on_bio_stream_ctrl(bio_stream::BIOStream{T}, cmd::BIOCtrl, num::Clong, ptr::Ptr{Cvoid}) where {T<:IO} = Clong(1) + +""" + BIO Stream callbacks. +""" +struct BIOStreamCallbacks + on_bio_create_ptr::Ptr{Nothing} + on_bio_destroy_ptr::Ptr{Nothing} + on_bio_read_ptr::Ptr{Nothing} + on_bio_write_ptr::Ptr{Nothing} + on_bio_puts_ptr::Ptr{Nothing} + on_bio_ctrl_ptr::Ptr{Nothing} + + function BIOStreamCallbacks() + on_bio_create_ptr = @cfunction on_bio_stream_create Cint (BIOStream{IO},) + on_bio_destroy_ptr = @cfunction on_bio_stream_destroy Cint (BIOStream{IO},) + on_bio_read_ptr = @cfunction on_bio_stream_read Cint (BIOStream{IO}, Ptr{Cchar}, Cint) + on_bio_write_ptr = @cfunction on_bio_stream_write Cint (BIOStream{IO}, Ptr{Cchar}, Cint) + on_bio_puts_ptr = @cfunction on_bio_stream_puts Cint (BIOStream{IO}, Ptr{Cchar}) + on_bio_ctrl_ptr = @cfunction on_bio_stream_ctrl Clong (BIOStream{IO}, BIOCtrl, Clong, Ptr{Cvoid}) + + return new( + on_bio_create_ptr, + on_bio_destroy_ptr, + on_bio_read_ptr, + on_bio_write_ptr, + on_bio_puts_ptr, + on_bio_ctrl_ptr) + end +end + """ ASN1_TIME. """ @@ -2944,8 +3089,6 @@ function Base.String(x509_ext::X509Extension) return "C_NULL" end - io = IOBuffer() - bio = BIO(BIOMethodMemory()) _ = ccall( diff --git a/src/ssl.jl b/src/ssl.jl index 78f872c..4f2371c 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -1,100 +1,3 @@ -""" - BIO Stream callbacks. -""" - -""" - Called to initialize new BIO Stream object. -""" -on_bio_stream_create(bio::BIO) = Cint(1) -on_bio_stream_destroy(bio::BIO)::Cint = Cint(0) - -function bio_get_data(bio::BIO) - data = ccall( - (:BIO_get_data, libcrypto), - Ptr{Cvoid}, - (BIO,), - bio) - return unsafe_pointer_to_objref(data) -end - -const BIO_FLAGS_SHOULD_RETRY = 0x08 -const BIO_FLAGS_READ = 0x01 -const BIO_FLAGS_WRITE = 0x02 -const BIO_FLAGS_IO_SPECIAL = 0x04 - -function bio_set_flags(bio::BIO, flags) - return ccall( - (:BIO_set_flags, libcrypto), - Cint, - (BIO, Cint), - bio, flags) -end - -bio_set_read_retry(bio::BIO) = bio_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY) -bio_clear_flags(bio::BIO) = bio_set_flags(bio, 0x00) - -function on_bio_stream_read(bio::BIO, out::Ptr{Cchar}, outlen::Cint) - try - bio_clear_flags(bio) - io = bio_get_data(bio)::IO - eof(io) - n = bytesavailable(io) - if n == 0 - bio_set_read_retry(bio) - return Cint(0) - end - unsafe_read(io, out, min(UInt(n), outlen)) - return Cint(min(n, outlen)) - catch e - # we don't want to throw a Julia exception from a C callback - return Cint(-1) - end -end - -function on_bio_stream_write(bio::BIO, in::Ptr{Cchar}, inlen::Cint)::Cint - try - io = bio_get_data(bio)::IO - written = unsafe_write(io, in, inlen) - return Cint(written) - catch e - # we don't want to throw a Julia exception from a C callback - return Cint(-1) - end -end - -on_bio_stream_puts(bio::BIO, in::Ptr{Cchar})::Cint = Cint(0) - -on_bio_stream_ctrl(bio::BIO, cmd::BIOCtrl, num::Clong, ptr::Ptr{Cvoid}) = Clong(1) - -""" - BIO Stream callbacks. -""" -struct BIOStreamCallbacks - on_bio_create_ptr::Ptr{Nothing} - on_bio_destroy_ptr::Ptr{Nothing} - on_bio_read_ptr::Ptr{Nothing} - on_bio_write_ptr::Ptr{Nothing} - on_bio_puts_ptr::Ptr{Nothing} - on_bio_ctrl_ptr::Ptr{Nothing} - - function BIOStreamCallbacks() - on_bio_create_ptr = @cfunction on_bio_stream_create Cint (BIO,) - on_bio_destroy_ptr = @cfunction on_bio_stream_destroy Cint (BIO,) - on_bio_read_ptr = @cfunction on_bio_stream_read Cint (BIO, Ptr{Cchar}, Cint) - on_bio_write_ptr = @cfunction on_bio_stream_write Cint (BIO, Ptr{Cchar}, Cint) - on_bio_puts_ptr = @cfunction on_bio_stream_puts Cint (BIO, Ptr{Cchar}) - on_bio_ctrl_ptr = @cfunction on_bio_stream_ctrl Clong (BIO, BIOCtrl, Clong, Ptr{Cvoid}) - - return new( - on_bio_create_ptr, - on_bio_destroy_ptr, - on_bio_read_ptr, - on_bio_write_ptr, - on_bio_puts_ptr, - on_bio_ctrl_ptr) - end -end - """ SSLMethod. TLSClientMethod. diff --git a/test/runtests.jl b/test/runtests.jl index 13c9a9d..a66baa8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -594,10 +594,9 @@ end @testset "SSLServer" begin server_task = @async test_server() client_task = @async test_client() - if isdefined(Base, :errormonitor) - errormonitor(server_task) - errormonitor(client_task) - end + + wait(server_task) + wait(client_task) end @testset "VersionNumber" begin From 30e4ea4dcca4b366d2902fbfa5aefa0e9e89b6e9 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Thu, 18 May 2023 16:59:17 -0700 Subject: [PATCH 04/17] eof in on_bio_stream_read is no longer required --- src/OpenSSL.jl | 21 +++++---------------- src/ssl.jl | 25 +++++++++++++------------ test/http_helpers.jl | 13 ++++++++++--- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index d1e935e..d972699 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1729,15 +1729,6 @@ end on_bio_stream_create(::BIOStream{T}) where {T<:IO} = Cint(1) on_bio_stream_destroy(::BIOStream{T}) where {T<:IO} = Cint(0) -#function bio_get_data(bio::BIO) -# data = ccall( -# (:BIO_get_data, libcrypto), -# Ptr{Cvoid}, -# (BIO,), -# bio) -# return unsafe_pointer_to_objref(data) -#end - function bio_set_flags(bio_stream::BIOStream{T}, flags) where {T<:IO} return ccall( (:BIO_set_flags, libcrypto), @@ -1773,18 +1764,16 @@ function on_bio_stream_read(bio_stream::BIOStream{T}, out::Ptr{Cchar}, outlen::C try bio_clear_flags(bio_stream) io::T = bio_stream_get_io(bio_stream) - n = bytesavailable(io) - if n == 0 - eof(io) - n = bytesavailable(io) - end + n = bytesavailable(io) if n == 0 bio_set_read_retry(bio_stream) return Cint(0) end - unsafe_read(io, out, min(UInt(n), outlen)) - return Cint(min(n, outlen)) + + bytes_to_read::Cint = min(UInt(n), outlen) + unsafe_read(io, out, bytes_to_read) + return bytes_to_read catch e # we don't want to throw a Julia exception from a C callback return Cint(-1) diff --git a/src/ssl.jl b/src/ssl.jl index 4f2371c..3fe07fb 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -237,21 +237,20 @@ function ssl_connect(ssl::SSL) end function ssl_accept(ssl::SSL) - if (ret = ccall( + return ccall( (:SSL_accept, libssl), Cint, (SSL,), - ssl)) != 1 - throw(OpenSSLError(ret)) - end + ssl) - ccall( - (:SSL_set_read_ahead, libssl), - Cvoid, - (SSL, Cint), - ssl, - Int32(1)) - return nothing + #TODO not enabled for now + #ccall( + # (:SSL_set_read_ahead, libssl), + # Cvoid, + # (SSL, Cint), + # ssl, + # Int32(1)) + #return nothing end """ @@ -459,7 +458,8 @@ function hostname!(ssl::SSLStream, host) end function Sockets.accept(ssl::SSLStream) - ssl_accept(ssl.ssl) + ret = @geterror ssl :accept ssl_accept(ssl.ssl) + @show "Sockets.accept SSLStream", ret end """ @@ -483,6 +483,7 @@ function Base.unsafe_read(ssl::SSLStream, buf::Ptr{UInt8}, nbytes::UInt) elseif ret == SSL_ERROR_WANT_READ # this means write is waiting for more data from the underlying socket # so call eof on the socket to wait for more bytes to come in + @show SSL_ERROR_WANT_READ eof(ssl.io) && throw(EOFError()) elseif ret == SSL_ERROR_WANT_WRITE flush(ssl.io) diff --git a/test/http_helpers.jl b/test/http_helpers.jl index df0f6d8..ce0ecc9 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -4,6 +4,9 @@ using Sockets using Test function test_server() + server_socket = listen(5000) + accepted_socket = accept(server_socket) + x509_certificate = X509Certificate() evp_pkey = EvpPKey(rsa_generate_key()) @@ -22,9 +25,6 @@ function test_server() sign_certificate(x509_certificate, evp_pkey) - server_socket = listen(5000) - accepted_socket = accept(server_socket) - # Create and configure server SSLContext. ssl_ctx = OpenSSL.SSLContext(OpenSSL.TLSServerMethod()) _ = OpenSSL.ssl_set_options(ssl_ctx, OpenSSL.SSL_OP_NO_COMPRESSION) @@ -37,6 +37,8 @@ function test_server() OpenSSL.accept(ssl) + # TODO BUG, this is required + # eof is required @test !eof(ssl) request = readavailable(ssl) reply = "reply: $(String(request))" @@ -76,12 +78,17 @@ function test_client() written = unsafe_write(ssl, pointer(request_str), length(request_str)) sleep(1) + + # TODO, BUG + # eof required + # @test !eof(ssl) @test length(request_str) == written response_str = String(readavailable(ssl)) @test response_str == "reply: $(request_str)" + @show response_str close(ssl) finalize(ssl_ctx) From 2bbc7f4913e334491801082defb6e2365686c603 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Thu, 18 May 2023 17:29:06 -0700 Subject: [PATCH 05/17] ssl_accept want_more --- src/ssl.jl | 34 ++++++++++++++++++++++++++++++++-- test/runtests.jl | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 3fe07fb..e9ccf52 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -437,6 +437,19 @@ function Sockets.connect(ssl::SSLStream; require_ssl_verification::Bool=true) ssl.ssl, Cint(1)) end + + ret = @geterror ssl :peek_ex ccall( + (:SSL_peek_ex, libssl), + Cint, + (SSL, Ptr{UInt8}, Cint, Ptr{Csize_t}), + ssl.ssl, + ssl.peekbuf, + 1, + ssl.peekbytes + ) + @show "connect", ret + #return nothing + return end @@ -458,8 +471,25 @@ function hostname!(ssl::SSLStream, host) end function Sockets.accept(ssl::SSLStream) - ret = @geterror ssl :accept ssl_accept(ssl.ssl) - @show "Sockets.accept SSLStream", ret + while true + ret = @geterror ssl :accept ssl_accept(ssl.ssl) + if ret == SSL_ERROR_NONE + break + elseif ret == SSL_ERROR_WANT_READ + # this means connect is waiting for more data from the underlying socket + # so call eof on the socket to wait for more bytes to come in + eof(ssl.io) && throw(EOFError()) + else + throw(Base.IOError(OpenSSLError(ret).msg, 0)) + end + end + + ccall( + (:SSL_set_read_ahead, libssl), + Cvoid, + (SSL, Cint), + ssl.ssl, + Int32(1)) end """ diff --git a/test/runtests.jl b/test/runtests.jl index a66baa8..71f2be1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -199,6 +199,7 @@ end sleep(2) write(io, readavailable(ssl)) response = String(take!(io)) + @show response @test startswith(response, "HTTP/1.1 200 OK\r\n") sleep(2) @test isempty(readavailable(ssl)) From 9bb40e35bbd07274cd81e75171e13d3afe74700f Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Thu, 18 May 2023 21:09:24 -0700 Subject: [PATCH 06/17] fix macro expansion bug --- src/ssl.jl | 80 +++++++++++++++++++++++++++++++------------- test/http_helpers.jl | 18 ++++++---- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index e9ccf52..4c1ea7d 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -242,26 +242,28 @@ function ssl_accept(ssl::SSL) Cint, (SSL,), ssl) - - #TODO not enabled for now - #ccall( - # (:SSL_set_read_ahead, libssl), - # Cvoid, - # (SSL, Cint), - # ssl, - # Int32(1)) - #return nothing end """ Shut down a TLS/SSL connection. """ function ssl_disconnect(ssl::SSL) - ccall( + result = ccall( (:SSL_shutdown, libssl), Cint, (SSL,), ssl) + @show "ssl_disconnect", result + + if result == 0 + result = ccall( + (:SSL_shutdown, libssl), + Cint, + (SSL,), + ssl) + end + @show "ssl_disconnect", result + return nothing end @@ -324,7 +326,7 @@ macro geterror(ssl, op, expr) esc(quote # lock our SSLStream while we clear errors # make a ccall, then check the error queue - Base.@lock ssl.lock begin + lock(ssl.lock) do # check that SSL is still open before ccall $ssl.closed && throwio($op) # clear the current error queue before openssl ccall @@ -438,18 +440,6 @@ function Sockets.connect(ssl::SSLStream; require_ssl_verification::Bool=true) Cint(1)) end - ret = @geterror ssl :peek_ex ccall( - (:SSL_peek_ex, libssl), - Cint, - (SSL, Ptr{UInt8}, Cint, Ptr{Csize_t}), - ssl.ssl, - ssl.peekbuf, - 1, - ssl.peekbytes - ) - @show "connect", ret - #return nothing - return end @@ -470,9 +460,49 @@ function hostname!(ssl::SSLStream, host) end end +""" +""" + function Sockets.accept(ssl::SSLStream) +""" while true - ret = @geterror ssl :accept ssl_accept(ssl.ssl) + _ret = ssl_accept(ssl.ssl) + if _ret != 1 + ret = get_error(ssl.ssl, _ret) + + if ret == SSL_ERROR_NONE + break + elseif ret == SSL_ERROR_WANT_READ + # this means connect is waiting for more data from the underlying socket + # so call eof on the socket to wait for more bytes to come in + eof(ssl.io) && throw(EOFError()) + else + throw(Base.IOError(OpenSSLError(ret).msg, 0)) + end + else + break + end + end + + ret = @geterror ssl :peek ccall( + (:SSL_peek_ex, libssl), + Cint, + (SSL, Ptr{UInt8}, Cint, Ptr{Csize_t}), + ssl.ssl, + ssl.peekbuf, + 1, + ssl.peekbytes + ) +""" + + while true + ret = @geterror ssl :ssl_accept ccall( + (:SSL_accept, libssl), + Cint, + (SSL,), + ssl.ssl + ) + if ret == SSL_ERROR_NONE break elseif ret == SSL_ERROR_WANT_READ @@ -496,6 +526,7 @@ end Read from the SSL stream. """ function Base.unsafe_read(ssl::SSLStream, buf::Ptr{UInt8}, nbytes::UInt) + @show "Base.unsafe_read(ssl::SSLStream", ssl.io, nbytes nread = 0 readbytes = ssl.readbytes while nread < nbytes @@ -508,6 +539,7 @@ function Base.unsafe_read(ssl::SSLStream, buf::Ptr{UInt8}, nbytes::UInt) nbytes - nread, readbytes ) + @show "SSL_read_ex", ret if ret == SSL_ERROR_NONE nread += Base.bitcast(Int, readbytes[]) elseif ret == SSL_ERROR_WANT_READ diff --git a/test/http_helpers.jl b/test/http_helpers.jl index ce0ecc9..7dbe767 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -4,9 +4,6 @@ using Sockets using Test function test_server() - server_socket = listen(5000) - accepted_socket = accept(server_socket) - x509_certificate = X509Certificate() evp_pkey = EvpPKey(rsa_generate_key()) @@ -33,13 +30,18 @@ function test_server() OpenSSL.ssl_use_certificate(ssl_ctx, x509_certificate) OpenSSL.ssl_use_private_key(ssl_ctx, evp_pkey) + server_socket = listen(5000) + accepted_socket = accept(server_socket) + ssl = SSLStream(ssl_ctx, accepted_socket) OpenSSL.accept(ssl) + @show "accepted" # TODO BUG, this is required # eof is required @test !eof(ssl) + @show "server after eof", bytesavailable(ssl) request = readavailable(ssl) reply = "reply: $(String(request))" @@ -50,8 +52,9 @@ function test_server() unsafe_write(ssl, pointer(reply), length(reply)) - close(ssl) - finalize(ssl_ctx) + #TODO bug + #close(ssl) + #finalize(ssl_ctx) return nothing end @@ -82,10 +85,11 @@ function test_client() # TODO, BUG # eof required # - @test !eof(ssl) - @test length(request_str) == written + #@test length(request_str) == written + eof(ssl) response_str = String(readavailable(ssl)) + @test response_str == "reply: $(request_str)" @show response_str From b92b2917c584d223c477418a55d26bbbf0c9486b Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 19 May 2023 10:31:07 -0700 Subject: [PATCH 07/17] close ssl --- test/http_helpers.jl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/http_helpers.jl b/test/http_helpers.jl index 7dbe767..1f5eb3d 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -53,8 +53,8 @@ function test_server() unsafe_write(ssl, pointer(reply), length(reply)) #TODO bug - #close(ssl) - #finalize(ssl_ctx) + close(ssl) + finalize(ssl_ctx) return nothing end @@ -79,17 +79,15 @@ function test_client() request_str = "GET / HTTP/1.1\r\nHost: localhost\r\nUser-Agent: curl\r\nAccept: */*\r\n\r\nRequest_body." written = unsafe_write(ssl, pointer(request_str), length(request_str)) + @test length(request_str) == written - sleep(1) - - # TODO, BUG - # eof required - # - #@test length(request_str) == written + # wait for the response. + while bytesavailable(ssl) == 0 + @show "[==>] calling eof" + eof(ssl) + end - eof(ssl) response_str = String(readavailable(ssl)) - @test response_str == "reply: $(request_str)" @show response_str From 618b1efcbd6abe2011ace22d63c05312254fbe0b Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 19 May 2023 10:40:01 -0700 Subject: [PATCH 08/17] single ssl_disconnect --- src/ssl.jl | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 4c1ea7d..59a251a 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -253,16 +253,15 @@ function ssl_disconnect(ssl::SSL) Cint, (SSL,), ssl) - @show "ssl_disconnect", result - - if result == 0 - result = ccall( - (:SSL_shutdown, libssl), - Cint, - (SSL,), - ssl) - end - @show "ssl_disconnect", result + #@show "ssl_disconnect", result + #if result == 0 + # result = ccall( + # (:SSL_shutdown, libssl), + # Cint, + # (SSL,), + # ssl) + #end + #@show "ssl_disconnect", result return nothing end @@ -526,7 +525,6 @@ end Read from the SSL stream. """ function Base.unsafe_read(ssl::SSLStream, buf::Ptr{UInt8}, nbytes::UInt) - @show "Base.unsafe_read(ssl::SSLStream", ssl.io, nbytes nread = 0 readbytes = ssl.readbytes while nread < nbytes @@ -539,13 +537,12 @@ function Base.unsafe_read(ssl::SSLStream, buf::Ptr{UInt8}, nbytes::UInt) nbytes - nread, readbytes ) - @show "SSL_read_ex", ret + if ret == SSL_ERROR_NONE nread += Base.bitcast(Int, readbytes[]) elseif ret == SSL_ERROR_WANT_READ # this means write is waiting for more data from the underlying socket # so call eof on the socket to wait for more bytes to come in - @show SSL_ERROR_WANT_READ eof(ssl.io) && throw(EOFError()) elseif ret == SSL_ERROR_WANT_WRITE flush(ssl.io) From 4a6a3ecfd72bae433cc8fc6076182e95c40edd7c Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 19 May 2023 10:51:36 -0700 Subject: [PATCH 09/17] test cleanup --- test/http_helpers.jl | 12 ++++++------ test/runtests.jl | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/http_helpers.jl b/test/http_helpers.jl index 1f5eb3d..ed12675 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -36,12 +36,13 @@ function test_server() ssl = SSLStream(ssl_ctx, accepted_socket) OpenSSL.accept(ssl) - @show "accepted" - # TODO BUG, this is required - # eof is required - @test !eof(ssl) - @show "server after eof", bytesavailable(ssl) + # wait for the request, as we are using `readavailable` + # we need to make sure there is a data in the buffer. + while bytesavailable(ssl) == 0 + eof(ssl) + end + request = readavailable(ssl) reply = "reply: $(String(request))" @@ -52,7 +53,6 @@ function test_server() unsafe_write(ssl, pointer(reply), length(reply)) - #TODO bug close(ssl) finalize(ssl_ctx) diff --git a/test/runtests.jl b/test/runtests.jl index 71f2be1..fecf5ce 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -199,7 +199,7 @@ end sleep(2) write(io, readavailable(ssl)) response = String(take!(io)) - @show response + @test startswith(response, "HTTP/1.1 200 OK\r\n") sleep(2) @test isempty(readavailable(ssl)) From 27bd16a10ceca680910541b65aa52699dd2c93ec Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Fri, 19 May 2023 13:27:16 -0700 Subject: [PATCH 10/17] client confirmation --- test/http_helpers.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/http_helpers.jl b/test/http_helpers.jl index ed12675..3054bc3 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -53,6 +53,11 @@ function test_server() unsafe_write(ssl, pointer(reply), length(reply)) + # Wait for the client confirmation then disconnect. + while bytesavailable(ssl) == 0 + eof(ssl) + end + close(ssl) finalize(ssl_ctx) @@ -92,6 +97,9 @@ function test_client() @test response_str == "reply: $(request_str)" @show response_str + # Send a message again, that is the information for the server to disonnect. + written = unsafe_write(ssl, pointer(request_str), length(request_str)) + close(ssl) finalize(ssl_ctx) end From 47bb79995630c0d8bf4e5423a20b2f2195d6bd93 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Fri, 19 May 2023 15:44:00 -0700 Subject: [PATCH 11/17] update comments --- src/ssl.jl | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 59a251a..4390f91 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -460,40 +460,9 @@ function hostname!(ssl::SSLStream, host) end """ + Accept SSL session connection request from a client application. """ - function Sockets.accept(ssl::SSLStream) -""" - while true - _ret = ssl_accept(ssl.ssl) - if _ret != 1 - ret = get_error(ssl.ssl, _ret) - - if ret == SSL_ERROR_NONE - break - elseif ret == SSL_ERROR_WANT_READ - # this means connect is waiting for more data from the underlying socket - # so call eof on the socket to wait for more bytes to come in - eof(ssl.io) && throw(EOFError()) - else - throw(Base.IOError(OpenSSLError(ret).msg, 0)) - end - else - break - end - end - - ret = @geterror ssl :peek ccall( - (:SSL_peek_ex, libssl), - Cint, - (SSL, Ptr{UInt8}, Cint, Ptr{Csize_t}), - ssl.ssl, - ssl.peekbuf, - 1, - ssl.peekbytes - ) -""" - while true ret = @geterror ssl :ssl_accept ccall( (:SSL_accept, libssl), From 9ec59d834cd4d7fd342de0274e4878e348c9f5f2 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Fri, 19 May 2023 16:30:46 -0700 Subject: [PATCH 12/17] cleanup --- src/ssl.jl | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index 4390f91..b82b982 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -248,21 +248,12 @@ end Shut down a TLS/SSL connection. """ function ssl_disconnect(ssl::SSL) - result = ccall( + _ = ccall( (:SSL_shutdown, libssl), Cint, (SSL,), ssl) - #@show "ssl_disconnect", result - #if result == 0 - # result = ccall( - # (:SSL_shutdown, libssl), - # Cint, - # (SSL,), - # ssl) - #end - #@show "ssl_disconnect", result - + return nothing end @@ -464,12 +455,7 @@ end """ function Sockets.accept(ssl::SSLStream) while true - ret = @geterror ssl :ssl_accept ccall( - (:SSL_accept, libssl), - Cint, - (SSL,), - ssl.ssl - ) + ret = @geterror ssl :accept ssl_accept(ssl.ssl) if ret == SSL_ERROR_NONE break From 95da9c04f2211802859a0e75cfad4bb063e22d9e Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Fri, 19 May 2023 17:41:30 -0700 Subject: [PATCH 13/17] SSL is using BIOStream{T<:IO} Print OpenSSL object is also using BIOStream{T<:IO} --- src/OpenSSL.jl | 215 ++++++++++++++++++++++++++++--------------------- 1 file changed, 122 insertions(+), 93 deletions(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index d972699..dee5dc3 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1552,62 +1552,6 @@ mutable struct BIO BIO(bio::Ptr{Cvoid}) = new(bio) - """ - Creates a BIO object using IO stream method. - The BIO object is not registered with the finalizer. - """ - function BIO(io::T; finalize::Bool=true) where {T<:IO} - bio = ccall( - (:BIO_new, libcrypto), - Ptr{Cvoid}, - (BIOMethod,), - BIO_STREAM_METHOD.x) - if bio == C_NULL - throw(OpenSSLError()) - end - - bio = new(bio) - finalize && finalizer(free, bio) - - # note that `io` must be held as a reference somewhere else - # since it is not referenced by the BIO directly - # e.g. in SSLStream, we keep the `io` reference that is passed to - # the read/write BIOs - ccall( - (:BIO_set_data, libcrypto), - Cvoid, - (BIO, Ptr{Cvoid}), - bio, - pointer_from_objref(io)) - - # Set BIO as non-blocking - ccall( - (:BIO_ctrl, libcrypto), - Cint, - (BIO, Cint, Cint, Ptr{Cvoid}), - bio, - 102, - 1, - C_NULL) - - # Mark BIO as initalized. - ccall( - (:BIO_set_init, libcrypto), - Cvoid, - (BIO, Cint), - bio, - 1) - - ccall( - (:BIO_set_shutdown, libcrypto), - Cvoid, - (BIO, Cint), - bio, - 0) - - return bio - end - """ Creates BIO for given BIOMethod. """ @@ -1708,6 +1652,73 @@ Base.write(bio::BIO, out_data) = return unsafe_write(bio, pointer(out_data), len """ mutable struct BIOStream{T<:IO} bio::Ptr{Cvoid} + + """ + Creates a BIOStream object using IO stream method. + The BIOStream object is not registered with the finalizer. + """ + function BIOStream(io::T; finalize::Bool=true) where {T<:IO} + bio = ccall( + (:BIO_new, libcrypto), + Ptr{Cvoid}, + (BIOMethod,), + BIO_STREAM_METHOD.x) + if bio == C_NULL + throw(OpenSSLError()) + end + + bio_stream = new{T}(bio) + finalize && finalizer(free, bio_stream) + + # note that `io` must be held as a reference somewhere else + # since it is not referenced by the BIO directly + # e.g. in SSLStream, we keep the `io` reference that is passed to + # the read/write BIOs + ccall( + (:BIO_set_data, libcrypto), + Cvoid, + (BIOStream{T}, Ptr{Cvoid}), + bio_stream, + pointer_from_objref(io)) + + # Set BIO as non-blocking + ccall( + (:BIO_ctrl, libcrypto), + Cint, + (BIOStream{T}, Cint, Cint, Ptr{Cvoid}), + bio_stream, + 102, + 1, + C_NULL) + + # Mark BIO as initalized. + ccall( + (:BIO_set_init, libcrypto), + Cvoid, + (BIOStream{T}, Cint), + bio_stream, + 1) + + ccall( + (:BIO_set_shutdown, libcrypto), + Cvoid, + (BIOStream{T}, Cint), + bio_stream, + 0) + + return bio_stream + end +end + +function free(bio_stream::BIOStream{T}) where {T<:IO} + ccall( + (:BIO_free, libcrypto), + Cvoid, + (BIOStream{T},), + bio_stream) + + bio_stream.bio = C_NULL + return nothing end function bio_stream_get_io(bio_stream::BIOStream{T})::T where {T<:IO} @@ -1719,6 +1730,24 @@ function bio_stream_get_io(bio_stream::BIOStream{T})::T where {T<:IO} return unsafe_pointer_to_objref(data)::T end +""" + BIO write. +""" +function Base.unsafe_write(bio_stream::BIOStream{T}, out_buffer::Ptr{UInt8}, out_length::Int) where {T<:IO} + result = ccall( + (:BIO_write, libcrypto), + Cint, + (BIOStream{T}, Ptr{Cvoid}, Cint), + bio_stream, + out_buffer, + out_length) + if result < 0 + throw(OpenSSLError()) + end +end + +Base.write(bio_stream::BIOStream{T}, out_data) where {T<:IO} = return unsafe_write(bio_stream, pointer(out_data), length(out_data)) + """ BIOStream callbacks. """ @@ -1995,24 +2024,24 @@ function Base.:(==)(evp_pkey_1::EvpPKey, evp_pkey_2::EvpPKey) return result == 1 end -function Base.write(io::IO, evp_pkey::EvpPKey, evp_cipher::EvpCipher=EvpCipher(C_NULL)) - bio = BIO(io) +function Base.write(io::T, evp_pkey::EvpPKey, evp_cipher::EvpCipher=EvpCipher(C_NULL)) where {T<:IO} + bio_stream = BIOStream(io) - GC.@preserve io begin - if ccall( - (:PEM_write_bio_PrivateKey, libcrypto), - Cint, - (BIO, EvpPKey, EvpCipher, Ptr{Cvoid}, Cint, Cint, Ptr{Cvoid}), - bio, - evp_pkey, - evp_cipher, - C_NULL, - 0, - 0, - C_NULL) != 1 - throw(OpenSSLError()) - end + if ccall( + (:PEM_write_bio_PrivateKey, libcrypto), + Cint, + (BIOStream{T}, EvpPKey, EvpCipher, Ptr{Cvoid}, Cint, Cint, Ptr{Cvoid}), + bio_stream, + evp_pkey, + evp_cipher, + C_NULL, + 0, + 0, + C_NULL) != 1 + throw(OpenSSLError()) end + + free(bio_stream) end function get_key_type(evp_pkey::EvpPKey)::EvpPKeyType @@ -2401,19 +2430,19 @@ function Base.:(==)(x509_cert_1::X509Certificate, x509_cert_2::X509Certificate) return result == 0 end -function Base.write(io::IO, x509_cert::X509Certificate) - bio = BIO(io) +function Base.write(io::T, x509_cert::X509Certificate) where {T<:IO} + bio_stream = BIOStream(io) - GC.@preserve io begin - if ccall( - (:PEM_write_bio_X509, libcrypto), - Cint, - (BIO, X509Certificate), - bio, - x509_cert) != 1 - throw(OpenSSLError()) - end + if ccall( + (:PEM_write_bio_X509, libcrypto), + Cint, + (BIOStream{T}, X509Certificate), + bio_stream, + x509_cert) != 1 + throw(OpenSSLError()) end + + free(bio_stream) end function sign_certificate(x509_cert::X509Certificate, evp_pkey::EvpPKey) @@ -2688,18 +2717,18 @@ function free(x509_req::X509Request) end function Base.write(io::IO, x509_req::X509Request) - bio = BIO(io) + bio_stream = BIOStream(io) - GC.@preserve io begin - if ccall( - (:PEM_write_bio_X509_REQ, libcrypto), - Cint, - (BIO, X509Request), - bio, - x509_req) != 1 - throw(OpenSSLError()) - end + if ccall( + (:PEM_write_bio_X509_REQ, libcrypto), + Cint, + (BIO, X509Request), + bio_stream, + x509_req) != 1 + throw(OpenSSLError()) end + + free(bio_stream) end function add_extensions(x509_req::X509Request, x509_exts::StackOf{X509Extension}) From c3f0d7b3a317552da97d5220409b2fba9231e7f0 Mon Sep 17 00:00:00 2001 From: Greg Lapinski Date: Fri, 19 May 2023 18:05:29 -0700 Subject: [PATCH 14/17] move to BIOStream{T} --- src/ssl.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ssl.jl b/src/ssl.jl index b82b982..67525b4 100644 --- a/src/ssl.jl +++ b/src/ssl.jl @@ -182,7 +182,7 @@ end mutable struct SSL ssl::Ptr{Cvoid} - function SSL(ssl_context::SSLContext, read_bio::BIO, write_bio::BIO)::SSL + function SSL(ssl_context::SSLContext, read_bio_stream::BIOStream{T}, write_bio_stream::BIOStream{T})::SSL where {T<:IO} ssl = ccall( (:SSL_new, libssl), Ptr{Cvoid}, @@ -197,10 +197,10 @@ mutable struct SSL ccall( (:SSL_set_bio, libssl), Cvoid, - (SSL, BIO, BIO), + (SSL, BIOStream{T}, BIOStream{T}), ssl, - read_bio, - write_bio) + read_bio_stream, + write_bio_stream) return ssl end @@ -272,8 +272,8 @@ end mutable struct SSLStream <: IO ssl::SSL ssl_context::SSLContext - rbio::BIO - wbio::BIO + rbio::BIOStream{TCPSocket} + wbio::BIOStream{TCPSocket} io::TCPSocket # used in `eof` where we want the call to `eof` on the underlying # socket and the SSL_peek call that processes bytes to be seen @@ -293,8 +293,8 @@ mutable struct SSLStream <: IO function SSLStream(ssl_context::SSLContext, io::TCPSocket) # Create a read and write BIOs. - bio_read::BIO = BIO(io; finalize=false) - bio_write::BIO = BIO(io; finalize=false) + bio_read = BIOStream(io; finalize=false) + bio_write = BIOStream(io; finalize=false) ssl = SSL(ssl_context, bio_read, bio_write) return new(ssl, ssl_context, bio_read, bio_write, io, ReentrantLock(), ReentrantLock(), Ref{Csize_t}(0), Ref{Csize_t}(0), Ref{UInt8}(0x00), Ref{Csize_t}(0), false) end From 48eca877420682cd2a0348f4334e0ab0f2c9d057 Mon Sep 17 00:00:00 2001 From: greg Date: Sat, 20 May 2023 14:19:30 -0700 Subject: [PATCH 15/17] BIO_STREAM_CALLBACKS_IO.x = BIOStreamCallbacks{IO}() BIO_STREAM_CALLBACKS_TCPSOCKET.x = BIOStreamCallbacks{TCPSocket}() --- src/OpenSSL.jl | 193 +++++++++++++++++++++++++---------------------- test/runtests.jl | 19 +++-- 2 files changed, 114 insertions(+), 98 deletions(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index dee5dc3..ad0d15a 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1419,85 +1419,6 @@ mutable struct BIOMethod bio_method::Ptr{Cvoid} BIOMethod(bio_method::Ptr{Cvoid}) = new(bio_method) - - function BIOMethod(bio_type::String) - bio_method_index = ccall( - (:BIO_get_new_index, libcrypto), - Cint, - ()) - if bio_method_index == -1 - throw(OpenSSLError()) - end - - bio_method = ccall( - (:BIO_meth_new, libcrypto), - Ptr{Cvoid}, - (Cint, Cstring), - bio_method_index, - bio_type) - if bio_method == C_NULL - throw(OpenSSLError()) - end - - bio_method = new(bio_method) - finalizer(free, bio_method) - - if ccall( - (:BIO_meth_set_create, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_create_ptr) != 1 - throw(OpenSSLError()) - end - - if ccall( - (:BIO_meth_set_destroy, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_destroy_ptr) != 1 - throw(OpenSSLError()) - end - - if ccall( - (:BIO_meth_set_read, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_read_ptr) != 1 - throw(OpenSSLError()) - end - - if ccall( - (:BIO_meth_set_write, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_write_ptr) != 1 - throw(OpenSSLError()) - end - - if ccall( - (:BIO_meth_set_puts, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_puts_ptr) != 1 - throw(OpenSSLError()) - end - - if ccall( - (:BIO_meth_set_ctrl, libcrypto), - Cint, - (BIOMethod, Ptr{Cvoid}), - bio_method, - BIO_STREAM_CALLBACKS.x.on_bio_ctrl_ptr) != 1 - throw(OpenSSLError()) - end - - return bio_method - end end """ @@ -1662,7 +1583,7 @@ mutable struct BIOStream{T<:IO} (:BIO_new, libcrypto), Ptr{Cvoid}, (BIOMethod,), - BIO_STREAM_METHOD.x) + BIO_STREAM_METHOD_IO.x) if bio == C_NULL throw(OpenSSLError()) end @@ -1827,7 +1748,7 @@ on_bio_stream_ctrl(bio_stream::BIOStream{T}, cmd::BIOCtrl, num::Clong, ptr::Ptr{ """ BIO Stream callbacks. """ -struct BIOStreamCallbacks +struct BIOStreamCallbacks{T<:IO} on_bio_create_ptr::Ptr{Nothing} on_bio_destroy_ptr::Ptr{Nothing} on_bio_read_ptr::Ptr{Nothing} @@ -1835,13 +1756,13 @@ struct BIOStreamCallbacks on_bio_puts_ptr::Ptr{Nothing} on_bio_ctrl_ptr::Ptr{Nothing} - function BIOStreamCallbacks() - on_bio_create_ptr = @cfunction on_bio_stream_create Cint (BIOStream{IO},) - on_bio_destroy_ptr = @cfunction on_bio_stream_destroy Cint (BIOStream{IO},) - on_bio_read_ptr = @cfunction on_bio_stream_read Cint (BIOStream{IO}, Ptr{Cchar}, Cint) - on_bio_write_ptr = @cfunction on_bio_stream_write Cint (BIOStream{IO}, Ptr{Cchar}, Cint) - on_bio_puts_ptr = @cfunction on_bio_stream_puts Cint (BIOStream{IO}, Ptr{Cchar}) - on_bio_ctrl_ptr = @cfunction on_bio_stream_ctrl Clong (BIOStream{IO}, BIOCtrl, Clong, Ptr{Cvoid}) + function BIOStreamCallbacks{T}() where {T<:IO} + on_bio_create_ptr = @cfunction on_bio_stream_create Cint (BIOStream{T},) + on_bio_destroy_ptr = @cfunction on_bio_stream_destroy Cint (BIOStream{T},) + on_bio_read_ptr = @cfunction on_bio_stream_read Cint (BIOStream{T}, Ptr{Cchar}, Cint) + on_bio_write_ptr = @cfunction on_bio_stream_write Cint (BIOStream{T}, Ptr{Cchar}, Cint) + on_bio_puts_ptr = @cfunction on_bio_stream_puts Cint (BIOStream{T}, Ptr{Cchar}) + on_bio_ctrl_ptr = @cfunction on_bio_stream_ctrl Clong (BIOStream{T}, BIOCtrl, Clong, Ptr{Cvoid}) return new( on_bio_create_ptr, @@ -1853,6 +1774,85 @@ struct BIOStreamCallbacks end end +function BIOMethod(bio_type::String, bio_stream_callbacks::BIOStreamCallbacks{T}) where {T<:IO} + bio_method_index = ccall( + (:BIO_get_new_index, libcrypto), + Cint, + ()) + if bio_method_index == -1 + throw(OpenSSLError()) + end + + bio_method = ccall( + (:BIO_meth_new, libcrypto), + Ptr{Cvoid}, + (Cint, Cstring), + bio_method_index, + bio_type) + if bio_method == C_NULL + throw(OpenSSLError()) + end + + bio_method = BIOMethod(bio_method) + finalizer(free, bio_method) + + if ccall( + (:BIO_meth_set_create, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_create_ptr) != 1 + throw(OpenSSLError()) + end + + if ccall( + (:BIO_meth_set_destroy, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_destroy_ptr) != 1 + throw(OpenSSLError()) + end + + if ccall( + (:BIO_meth_set_read, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_read_ptr) != 1 + throw(OpenSSLError()) + end + + if ccall( + (:BIO_meth_set_write, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_write_ptr) != 1 + throw(OpenSSLError()) + end + + if ccall( + (:BIO_meth_set_puts, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_puts_ptr) != 1 + throw(OpenSSLError()) + end + + if ccall( + (:BIO_meth_set_ctrl, libcrypto), + Cint, + (BIOMethod, Ptr{Cvoid}), + bio_method, + bio_stream_callbacks.on_bio_ctrl_ptr) != 1 + throw(OpenSSLError()) + end + + return bio_method +end + """ ASN1_TIME. """ @@ -3270,16 +3270,25 @@ end include("ssl.jl") const OPEN_SSL_INIT = Ref{OpenSSLInit}() -const BIO_STREAM_CALLBACKS = Ref{BIOStreamCallbacks}() -const BIO_STREAM_METHOD = Ref{BIOMethod}() +const BIO_STREAM_CALLBACKS_IO = Ref{BIOStreamCallbacks{IO}}() +const BIO_STREAM_CALLBACKS_TCPSOCKET = Ref{BIOStreamCallbacks{TCPSocket}}() +const BIO_STREAM_METHOD_IO = Ref{BIOMethod}() +const BIO_STREAM_METHOD_TCPSOCKET = Ref{BIOMethod}() """ Initialize module. """ function __init__() OPEN_SSL_INIT.x = OpenSSLInit() - BIO_STREAM_CALLBACKS.x = BIOStreamCallbacks() - BIO_STREAM_METHOD.x = BIOMethod("BIO_STREAM_METHOD") + BIO_STREAM_CALLBACKS_IO.x = BIOStreamCallbacks{IO}() + BIO_STREAM_CALLBACKS_TCPSOCKET.x = BIOStreamCallbacks{TCPSocket}() + BIO_STREAM_METHOD_IO.x = BIOMethod("BIO_STREAM_METHOD_IO", BIO_STREAM_CALLBACKS_IO.x) + BIO_STREAM_METHOD_TCPSOCKET.x = BIOMethod("BIO_STREAM_METHOD_TCPSOCKET", BIO_STREAM_CALLBACKS_TCPSOCKET.x) + + @show BIO_STREAM_METHOD_IO.x + @show BIO_STREAM_METHOD_TCPSOCKET.x + @show BIO_STREAM_CALLBACKS_IO.x + @show BIO_STREAM_CALLBACKS_TCPSOCKET.x # Set the openssl provider search path if version_number() ≥ v"3" diff --git a/test/runtests.jl b/test/runtests.jl index fecf5ce..ee9c38d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,12 +24,19 @@ end # Verifies calling into OpenSSL library. @testset "OpenSSL" begin - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_create_ptr != C_NULL - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_destroy_ptr != C_NULL - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_read_ptr != C_NULL - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_write_ptr != C_NULL - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_puts_ptr != C_NULL - @test OpenSSL.BIO_STREAM_CALLBACKS.x.on_bio_ctrl_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_create_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_destroy_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_read_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_write_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_puts_ptr != C_NULL + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_ctrl_ptr != C_NULL + + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_create_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_create_ptr + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_destroy_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_destroy_ptr + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_read_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_read_ptr + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_write_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_write_ptr + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_puts_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_puts_ptr + @test OpenSSL.BIO_STREAM_CALLBACKS_IO.x.on_bio_ctrl_ptr != OpenSSL.BIO_STREAM_CALLBACKS_TCPSOCKET.x.on_bio_ctrl_ptr end @testset "RandomBytes" begin From 796139554f0f37027919460ba840217b7b478650 Mon Sep 17 00:00:00 2001 From: greg Date: Sun, 21 May 2023 18:29:37 -0700 Subject: [PATCH 16/17] cleanup --- src/OpenSSL.jl | 13 ++++--------- test/http_helpers.jl | 1 - 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index ad0d15a..24008e0 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1687,8 +1687,8 @@ function bio_set_flags(bio_stream::BIOStream{T}, flags) where {T<:IO} bio_stream, flags) end -bio_set_read_retry(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY) -bio_clear_flags(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, 0x00) +bio_stream_set_read_retry(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY) +bio_stream_clear_flags(bio_stream::BIOStream{T}) where {T<:IO} = bio_set_flags(bio_stream, 0x00) """ static int fd_read(BIO *b, char *out, int outl) @@ -1712,12 +1712,12 @@ static int fd_read(BIO *b, char *out, int outl) function on_bio_stream_read(bio_stream::BIOStream{T}, out::Ptr{Cchar}, outlen::Cint) where {T<:IO} try - bio_clear_flags(bio_stream) + bio_stream_clear_flags(bio_stream) io::T = bio_stream_get_io(bio_stream) n = bytesavailable(io) if n == 0 - bio_set_read_retry(bio_stream) + bio_stream_set_read_retry(bio_stream) return Cint(0) end @@ -3285,11 +3285,6 @@ function __init__() BIO_STREAM_METHOD_IO.x = BIOMethod("BIO_STREAM_METHOD_IO", BIO_STREAM_CALLBACKS_IO.x) BIO_STREAM_METHOD_TCPSOCKET.x = BIOMethod("BIO_STREAM_METHOD_TCPSOCKET", BIO_STREAM_CALLBACKS_TCPSOCKET.x) - @show BIO_STREAM_METHOD_IO.x - @show BIO_STREAM_METHOD_TCPSOCKET.x - @show BIO_STREAM_CALLBACKS_IO.x - @show BIO_STREAM_CALLBACKS_TCPSOCKET.x - # Set the openssl provider search path if version_number() ≥ v"3" ossl_provider_set_default_search_path() diff --git a/test/http_helpers.jl b/test/http_helpers.jl index 3054bc3..6de2ff1 100644 --- a/test/http_helpers.jl +++ b/test/http_helpers.jl @@ -88,7 +88,6 @@ function test_client() # wait for the response. while bytesavailable(ssl) == 0 - @show "[==>] calling eof" eof(ssl) end From 656473b2792e0d212baee3cae0481f06d722d020 Mon Sep 17 00:00:00 2001 From: GregLapinski Date: Fri, 7 Jul 2023 20:25:59 -0700 Subject: [PATCH 17/17] choose BIO STREAM for TCPSOCKET or IO --- src/OpenSSL.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index 24008e0..b74229f 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -1574,6 +1574,10 @@ Base.write(bio::BIO, out_data) = return unsafe_write(bio, pointer(out_data), len mutable struct BIOStream{T<:IO} bio::Ptr{Cvoid} + get_bio_stream_method(::T) where {T<:IO} = BIO_STREAM_METHOD_IO.x + + get_bio_stream_method(::TCPSocket) = BIO_STREAM_METHOD_TCPSOCKET.x + """ Creates a BIOStream object using IO stream method. The BIOStream object is not registered with the finalizer. @@ -1583,7 +1587,7 @@ mutable struct BIOStream{T<:IO} (:BIO_new, libcrypto), Ptr{Cvoid}, (BIOMethod,), - BIO_STREAM_METHOD_IO.x) + get_bio_stream_method(io)) if bio == C_NULL throw(OpenSSLError()) end