@@ -1551,7 +1551,11 @@ static void
15511551ossl_ssl_mark (void * ptr )
15521552{
15531553 SSL * ssl = ptr ;
1554- rb_gc_mark ((VALUE )SSL_get_ex_data (ssl , ossl_ssl_ex_ptr_idx ));
1554+ VALUE obj = (VALUE )SSL_get_ex_data (ssl , ossl_ssl_ex_ptr_idx );
1555+
1556+ // Ensure GC compaction won't move objects referenced by OpenSSL objects
1557+ rb_gc_mark (obj );
1558+ rb_gc_mark (rb_attr_get (obj , id_i_io ));
15551559}
15561560
15571561static void
@@ -1601,13 +1605,29 @@ peeraddr_ip_str(VALUE self)
16011605 return rb_rescue2 (peer_ip_address , self , fallback_peer_ip_address , (VALUE )0 , rb_eSystemCallError , NULL );
16021606}
16031607
1608+ static int
1609+ is_real_socket (VALUE io )
1610+ {
1611+ // FIXME: DO NOT MERGE
1612+ return 0 ;
1613+ return RB_TYPE_P (io , T_FILE );
1614+ }
1615+
16041616/*
16051617 * call-seq:
16061618 * SSLSocket.new(io) => aSSLSocket
16071619 * SSLSocket.new(io, ctx) => aSSLSocket
16081620 *
1609- * Creates a new SSL socket from _io_ which must be a real IO object (not an
1610- * IO-like object that responds to read/write).
1621+ * Creates a new SSL socket from _io_ which must be an IO object
1622+ * or an IO-like object that at least implements the following methods:
1623+ *
1624+ * - <tt>write_nonblock</tt> with <tt>exception: false</tt>
1625+ * - <tt>read_nonblock</tt> with <tt>exception: false</tt>
1626+ * - <tt>wait_readable</tt>
1627+ * - <tt>wait_writable</tt>
1628+ * - <tt>flush</tt>
1629+ * - <tt>close</tt>
1630+ * - <tt>closed?</tt>
16111631 *
16121632 * If _ctx_ is provided the SSL Sockets initial params will be taken from
16131633 * the context.
@@ -1635,9 +1655,18 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
16351655 rb_ivar_set (self , id_i_context , v_ctx );
16361656 ossl_sslctx_setup (v_ctx );
16371657
1638- if (rb_respond_to (io , rb_intern ("nonblock=" )))
1639- rb_funcall (io , rb_intern ("nonblock=" ), 1 , Qtrue );
1640- Check_Type (io , T_FILE );
1658+ if (is_real_socket (io )) {
1659+ rb_io_t * fptr ;
1660+ GetOpenFile (io , fptr );
1661+ rb_io_set_nonblock (fptr );
1662+ }
1663+ else {
1664+ // Not meant to be a comprehensive check
1665+ if (!rb_respond_to (io , rb_intern ("read_nonblock" )) ||
1666+ !rb_respond_to (io , rb_intern ("write_nonblock" )))
1667+ rb_raise (rb_eTypeError , "io must be a real IO object or an IO-like "
1668+ "object that responds to read_nonblock and write_nonblock" );
1669+ }
16411670 rb_ivar_set (self , id_i_io , io );
16421671
16431672 ssl = SSL_new (ctx );
@@ -1669,18 +1698,24 @@ ossl_ssl_setup(VALUE self)
16691698{
16701699 VALUE io ;
16711700 SSL * ssl ;
1672- rb_io_t * fptr ;
16731701
16741702 GetSSL (self , ssl );
16751703 if (ssl_started (ssl ))
16761704 return Qtrue ;
16771705
16781706 io = rb_attr_get (self , id_i_io );
1679- GetOpenFile (io , fptr );
1680- rb_io_check_readable (fptr );
1681- rb_io_check_writable (fptr );
1682- if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1683- ossl_raise (eSSLError , "SSL_set_fd" );
1707+ if (is_real_socket (io )) {
1708+ rb_io_t * fptr ;
1709+ GetOpenFile (io , fptr );
1710+ rb_io_check_readable (fptr );
1711+ rb_io_check_writable (fptr );
1712+ if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1713+ ossl_raise (eSSLError , "SSL_set_fd" );
1714+ }
1715+ else {
1716+ BIO * bio = ossl_bio_new (io );
1717+ SSL_set_bio (ssl , bio , bio );
1718+ }
16841719
16851720 return Qtrue ;
16861721}
@@ -1691,6 +1726,38 @@ ossl_ssl_setup(VALUE self)
16911726#define ssl_get_error (ssl , ret ) SSL_get_error((ssl), (ret))
16921727#endif
16931728
1729+ static void
1730+ check_bio_error (SSL * ssl , VALUE io , int ret )
1731+ {
1732+ if (is_real_socket (io ))
1733+ return ;
1734+
1735+ BIO * bio = SSL_get_rbio (ssl );
1736+ int state = ossl_bio_state (bio );
1737+ if (!state )
1738+ return ;
1739+
1740+ /*
1741+ * Operation may succeed while the underlying socket reports an error in
1742+ * some cases. For example, when TLS 1.3 server tries to send a
1743+ * NewSessionTicket on a closed socket (IOW, when the client disconnects
1744+ * right after finishing a handshake).
1745+ *
1746+ * According to ssl/statem/statem_srvr.c conn_is_closed(), EPIPE and
1747+ * ECONNRESET may be ignored.
1748+ *
1749+ * FIXME BEFORE MERGE: Currently ignoring all SystemCallError.
1750+ */
1751+ int error_code = SSL_get_error (ssl , ret );
1752+ if ((ret > 0 || error_code == SSL_ERROR_ZERO_RETURN || error_code == SSL_ERROR_SSL ) &&
1753+ rb_obj_is_kind_of (rb_errinfo (), rb_eSystemCallError )) {
1754+ rb_set_errinfo (Qnil );
1755+ return ;
1756+ }
1757+ ossl_clear_error ();
1758+ rb_jump_tag (state );
1759+ }
1760+
16941761static void
16951762write_would_block (int nonblock )
16961763{
@@ -1729,6 +1796,11 @@ no_exception_p(VALUE opts)
17291796static void
17301797io_wait_writable (VALUE io )
17311798{
1799+ if (!is_real_socket (io )) {
1800+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_writable" ), 0 , NULL )))
1801+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
1802+ return ;
1803+ }
17321804#ifdef HAVE_RB_IO_MAYBE_WAIT
17331805 if (!rb_io_maybe_wait_writable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
17341806 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
@@ -1743,6 +1815,11 @@ io_wait_writable(VALUE io)
17431815static void
17441816io_wait_readable (VALUE io )
17451817{
1818+ if (!is_real_socket (io )) {
1819+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_readable" ), 0 , NULL )))
1820+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
1821+ return ;
1822+ }
17461823#ifdef HAVE_RB_IO_MAYBE_WAIT
17471824 if (!rb_io_maybe_wait_readable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
17481825 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
@@ -1767,8 +1844,10 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17671844 GetSSL (self , ssl );
17681845
17691846 VALUE io = rb_attr_get (self , id_i_io );
1847+
17701848 for (;;) {
17711849 ret = func (ssl );
1850+ check_bio_error (ssl , io , ret );
17721851
17731852 cb_state = rb_attr_get (self , ID_callback_state );
17741853 if (!NIL_P (cb_state )) {
@@ -1963,6 +2042,8 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19632042 rb_str_locktmp (str );
19642043 for (;;) {
19652044 int nread = SSL_read (ssl , RSTRING_PTR (str ), ilen );
2045+ check_bio_error (ssl , io , nread );
2046+
19662047 switch (ssl_get_error (ssl , nread )) {
19672048 case SSL_ERROR_NONE :
19682049 rb_str_unlocktmp (str );
@@ -2067,6 +2148,8 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
20672148
20682149 for (;;) {
20692150 int nwritten = SSL_write (ssl , RSTRING_PTR (tmp ), num );
2151+ check_bio_error (ssl , io , nwritten );
2152+
20702153 switch (ssl_get_error (ssl , nwritten )) {
20712154 case SSL_ERROR_NONE :
20722155 return INT2NUM (nwritten );
@@ -2144,7 +2227,15 @@ ossl_ssl_stop(VALUE self)
21442227 GetSSL (self , ssl );
21452228 if (!ssl_started (ssl ))
21462229 return Qnil ;
2230+
21472231 ret = SSL_shutdown (ssl );
2232+
2233+ /* XXX: Suppressing errors from the underlying socket */
2234+ VALUE io = rb_attr_get (self , id_i_io );
2235+ BIO * bio = SSL_get_rbio (ssl );
2236+ if (!is_real_socket (io ) && ossl_bio_state (bio ))
2237+ rb_set_errinfo (Qnil );
2238+
21482239 if (ret == 1 ) /* Have already received close_notify */
21492240 return Qnil ;
21502241 if (ret == 0 ) /* Sent close_notify, but we don't wait for reply */
0 commit comments