Skip to content

Commit

Permalink
create a new structure BIOStream{T<:IO} to handle BIO using IO
Browse files Browse the repository at this point in the history
  • Loading branch information
grlap committed May 18, 2023
1 parent bfd3373 commit ee21d15
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 107 deletions.
155 changes: 149 additions & 6 deletions src/OpenSSL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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},
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -1577,6 +1589,7 @@ mutable struct BIO
102,
1,
C_NULL)

# Mark BIO as initalized.
ccall(
(:BIO_set_init, libcrypto),
Expand Down Expand Up @@ -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),
Expand All @@ -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.
"""
Expand Down Expand Up @@ -2944,8 +3089,6 @@ function Base.String(x509_ext::X509Extension)
return "C_NULL"
end

io = IOBuffer()

bio = BIO(BIOMethodMemory())

_ = ccall(
Expand Down
97 changes: 0 additions & 97 deletions src/ssl.jl
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
7 changes: 3 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ee21d15

Please sign in to comment.