-
Notifications
You must be signed in to change notification settings - Fork 172
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
ssl: support IO-like object as the underlying transport #736
base: master
Are you sure you want to change the base?
Conversation
ext/openssl/ossl_bio.c
Outdated
BIO_clear_retry_flags(p->bio); | ||
|
||
VALUE fargs[] = { INT2NUM(p->dlen), nonblock_kwargs }; | ||
VALUE ret = rb_funcallv_public_kw(io, rb_intern("read_nonblock"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should't this ret
be somhow marked so it's not moved? (thinking of compaction here).
probably too early for optimizations, ,but this bit could benefit of a socket-local string to act as a buffer (second argument of :read_nonblock
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The content is copied in this function and the String object won't be kept, so it shouldn't be necessary.
probably too early for optimizations, ,but this bit could benefit of a socket-local string to act as a buffer (second argument of
:read_nonblock
)
Yes, this is definitely a possible optimization.
rb_io_set_nonblock(fptr); | ||
} | ||
else { | ||
// Not meant to be a comprehensive check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per the BIO impl, shouldn't this also verify "#flush"? (hypothetically, understand this wasn't as exhaustive check).
@@ -1735,6 +1805,11 @@ no_exception_p(VALUE opts) | |||
static void | |||
io_wait_writable(VALUE io) | |||
{ | |||
if (!is_real_socket(io)) { | |||
if (!RTEST(rb_funcallv(io, rb_intern("wait_writable"), 0, NULL))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be the same as the rb_io_maybe_wait_writable
call below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, this should be the same behavior. IO#wait_writable
without any argument should honor IO#timeout=
value.
@rhenium anything I can help with to move this forward? |
@rhenium friendly ping |
ded7cb0
to
84ead32
Compare
Implement a bare minimal BIO_METHOD required for SSL/TLS. The underlying IO-like object must implement the following methods: - #read_nonblock(len, exception: false) - #write_nonblock(str, exception: false) - #flush A later commit will wire it into OpenSSL::SSL::SSLSocket.
An exception raised in the SSLContext#servername_cb callback aborts the handshake and sends an "unrecognized_name" alert to the client. Add more direct assertions for this scenario.
This is no longer necessary as of commit 22e601a (Remove usage of IO internals, 2023-05-29).
rb_eSystemCallError is defined in Ruby's public header files, so let's just use it. Also, clean up the arguments to the rb_rescue2() call.
The result value is used for generating an informative error message. Let's just say "unsupported" if it's not available.
The value is used to determine whether SSLSocket should skip buffering in OpenSSL::Buffering or not. Defaulting to true (no buffering) should be a safe option.
There are use cases to establish a TLS connection on top of a non-OS stream, such as another TLS connection or an HTTP/2 tunnel. To achieve this today, a workaround using dummy socket pairs is necessary. Currently, OpenSSL::SSL::SSLSocket.new requires an IO (socket) object backed by a file descriptor. This is because we pass the file descriptor to OpenSSL. This patch changes it to allow any Ruby object that responds to necessary non-blocking IO methods, such as read_nonblock. OpenSSL's TLS implementation uses an IO abstraction layer called BIO to interact with the underlying socket. By passing the file descriptor to SSL_set_fd(), a BIO with the BIO_s_socket() BIO_METHOD is implicitly created. We can set up our own BIO and let OpenSSL use it instead. The previous patch added such a BIO_METHOD implementation. For performance reason, this patch continues to use the socket BIO if the user passes a real IO object, so this should not change the behavior of existing programs in any way.
Let's see what will break with this.
I just pushed what I have in my local branch so far, rebased on top of current master to resolve merge conflicts. This is not ready to merge yet. Some unresolved issues:
|
Indeed, I didn't know that doing that was dangerous, though that One option could be to assume that the IO-like object must indeed be an SSLSocket, and one could deal with it by setting an ivar "flag" telling the internal implementation to skip 2nd-level
Can they happen at the same time? Perhaps suggestion above deals with it? An alternative approach could be to go back to the drawing board and try again the approach using |
Please see I think the segfault is fixable by (ab)using
|
An implementation of #731. The test suite passes on my box, but it needs more testing especially around the error handling.
This adds support for IO-like object that is not backed by a file descriptor by defining a
BIO_METHOD
to wrap the following Ruby methods.