@@ -1607,13 +1607,28 @@ peeraddr_ip_str(VALUE self)
16071607 return rb_rescue2 (peer_ip_address , self , fallback_peer_ip_address , (VALUE )0 , rb_eSystemCallError , NULL );
16081608}
16091609
1610+ static int
1611+ is_real_socket (VALUE io )
1612+ {
1613+ return false;
1614+ return RB_TYPE_P (io , T_FILE );// && false;
1615+ }
1616+
16101617/*
16111618 * call-seq:
16121619 * SSLSocket.new(io) => aSSLSocket
16131620 * SSLSocket.new(io, ctx) => aSSLSocket
16141621 *
1615- * Creates a new SSL socket from _io_ which must be a real IO object (not an
1616- * IO-like object that responds to read/write).
1622+ * Creates a new SSL socket from _io_ which must be an IO object
1623+ * or an IO-like object that at least implements the following methods:
1624+ *
1625+ * - <tt>write_nonblock</tt> with <tt>exception: false</tt>
1626+ * - <tt>read_nonblock</tt> with <tt>exception: false</tt>
1627+ * - <tt>wait_readable</tt>
1628+ * - <tt>wait_writable</tt>
1629+ * - <tt>flush</tt>
1630+ * - <tt>close</tt>
1631+ * - <tt>closed?</tt>
16171632 *
16181633 * If _ctx_ is provided the SSL Sockets initial params will be taken from
16191634 * the context.
@@ -1641,9 +1656,20 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
16411656 rb_ivar_set (self , id_i_context , v_ctx );
16421657 ossl_sslctx_setup (v_ctx );
16431658
1644- if (rb_respond_to (io , rb_intern ("nonblock=" )))
1645- rb_funcall (io , rb_intern ("nonblock=" ), 1 , Qtrue );
1646- Check_Type (io , T_FILE );
1659+ if (is_real_socket (io )) {
1660+ rb_io_t * fptr ;
1661+ GetOpenFile (io , fptr );
1662+ rb_io_set_nonblock (fptr );
1663+ }
1664+ else {
1665+ // Not meant to be a comprehensive check
1666+ if (!rb_respond_to (io , rb_intern ("read_nonblock" )) ||
1667+ !rb_respond_to (io , rb_intern ("write_nonblock" )))
1668+ rb_raise (rb_eTypeError , "io must be a real IO object or an IO-like "
1669+ "object that responds to read_nonblock and write_nonblock" );
1670+ if (rb_respond_to (io , rb_intern ("nonblock=" )))
1671+ rb_funcall (io , rb_intern ("nonblock=" ), 1 , Qtrue );
1672+ }
16471673 rb_ivar_set (self , id_i_io , io );
16481674
16491675 ssl = SSL_new (ctx );
@@ -1679,18 +1705,28 @@ ossl_ssl_setup(VALUE self)
16791705{
16801706 VALUE io ;
16811707 SSL * ssl ;
1682- rb_io_t * fptr ;
16831708
16841709 GetSSL (self , ssl );
16851710 if (ssl_started (ssl ))
16861711 return Qtrue ;
16871712
16881713 io = rb_attr_get (self , id_i_io );
1689- GetOpenFile (io , fptr );
1690- rb_io_check_readable (fptr );
1691- rb_io_check_writable (fptr );
1692- if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1693- ossl_raise (eSSLError , "SSL_set_fd" );
1714+ if (is_real_socket (io )) {
1715+ rb_io_t * fptr ;
1716+ GetOpenFile (io , fptr );
1717+ rb_io_check_readable (fptr );
1718+ rb_io_check_writable (fptr );
1719+ if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1720+ ossl_raise (eSSLError , "SSL_set_fd" );
1721+ }
1722+ else {
1723+ BIO * bio = BIO_new (ossl_bio_meth );
1724+ if (!bio )
1725+ ossl_raise (eSSLError , "BIO_new(ossl_bio_meth)" );
1726+ BIO_set_data (bio , (void * )io );
1727+ // Returns void currently (but wouldn't it be technically possible to fail?)
1728+ SSL_set_bio (ssl , bio , bio );
1729+ }
16941730
16951731 return Qtrue ;
16961732}
@@ -1701,6 +1737,32 @@ ossl_ssl_setup(VALUE self)
17011737#define ssl_get_error (ssl , ret ) SSL_get_error((ssl), (ret))
17021738#endif
17031739
1740+ static void
1741+ handle_ossl_bio_error (SSL * ssl , BIO * bio , int ret )
1742+ {
1743+ int state = ossl_bio_restore_error (bio );
1744+ if (!state )
1745+ return ;
1746+
1747+ /*
1748+ * Operation may succeed while the underlying socket reports
1749+ * an error in one corner case: TLS 1.3 server tries to send a
1750+ * NewSessionTicket on a closed socket (IOW, when the client
1751+ * disconnects right after finishing a handshake).
1752+ *
1753+ * According to ssl/statem/statem_srvr.c conn_is_closed(), EPIPE and
1754+ * ECONNRESET may be ignored.
1755+ */
1756+ int error_code = ssl_get_error (ssl , ret );
1757+ if ((ret > 0 || error_code == SSL_ERROR_ZERO_RETURN || error_code == SSL_ERROR_SSL ) &&
1758+ rb_obj_is_kind_of (rb_errinfo (), rb_eSystemCallError )) {
1759+ rb_set_errinfo (Qnil );
1760+ return ;
1761+ }
1762+ ossl_clear_error ();
1763+ rb_jump_tag (state );
1764+ }
1765+
17041766static void
17051767write_would_block (int nonblock )
17061768{
@@ -1739,6 +1801,11 @@ no_exception_p(VALUE opts)
17391801static void
17401802io_wait_writable (VALUE io )
17411803{
1804+ if (!is_real_socket (io )) {
1805+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_writable" ), 0 , NULL )))
1806+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
1807+ return ;
1808+ }
17421809#ifdef HAVE_RB_IO_MAYBE_WAIT
17431810 if (!rb_io_maybe_wait_writable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
17441811 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
@@ -1753,6 +1820,11 @@ io_wait_writable(VALUE io)
17531820static void
17541821io_wait_readable (VALUE io )
17551822{
1823+ if (!is_real_socket (io )) {
1824+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_readable" ), 0 , NULL )))
1825+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
1826+ return ;
1827+ }
17561828#ifdef HAVE_RB_IO_MAYBE_WAIT
17571829 if (!rb_io_maybe_wait_readable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
17581830 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
@@ -1777,8 +1849,12 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17771849 GetSSL (self , ssl );
17781850
17791851 VALUE io = rb_attr_get (self , id_i_io );
1852+ BIO * bio = SSL_get_rbio (ssl );
1853+
17801854 for (;;) {
17811855 ret = func (ssl );
1856+ if (!is_real_socket (io ))
1857+ handle_ossl_bio_error (ssl , bio , ret );
17821858
17831859 cb_state = rb_attr_get (self , ID_callback_state );
17841860 if (!NIL_P (cb_state )) {
@@ -1967,10 +2043,14 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19672043 return str ;
19682044
19692045 VALUE io = rb_attr_get (self , id_i_io );
2046+ BIO * bio = SSL_get_rbio (ssl );
19702047
19712048 rb_str_locktmp (str );
19722049 for (;;) {
19732050 int nread = SSL_read (ssl , RSTRING_PTR (str ), ilen );
2051+ if (!is_real_socket (io ))
2052+ handle_ossl_bio_error (ssl , bio , nread );
2053+
19742054 switch (ssl_get_error (ssl , nread )) {
19752055 case SSL_ERROR_NONE :
19762056 rb_str_unlocktmp (str );
@@ -2067,6 +2147,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
20672147
20682148 tmp = rb_str_new_frozen (StringValue (str ));
20692149 VALUE io = rb_attr_get (self , id_i_io );
2150+ BIO * bio = SSL_get_rbio (ssl );
20702151
20712152 /* SSL_write(3ssl) manpage states num == 0 is undefined */
20722153 num = RSTRING_LENINT (tmp );
@@ -2075,6 +2156,9 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
20752156
20762157 for (;;) {
20772158 int nwritten = SSL_write (ssl , RSTRING_PTR (tmp ), num );
2159+ if (!is_real_socket (io ))
2160+ handle_ossl_bio_error (ssl , bio , nwritten );
2161+
20782162 switch (ssl_get_error (ssl , nwritten )) {
20792163 case SSL_ERROR_NONE :
20802164 return INT2NUM (nwritten );
@@ -2152,7 +2236,15 @@ ossl_ssl_stop(VALUE self)
21522236 GetSSL (self , ssl );
21532237 if (!ssl_started (ssl ))
21542238 return Qnil ;
2239+
21552240 ret = SSL_shutdown (ssl );
2241+
2242+ /* XXX: Suppressing errors from the underlying socket */
2243+ VALUE io = rb_attr_get (self , id_i_io );
2244+ BIO * bio = SSL_get_rbio (ssl );
2245+ if (!is_real_socket (io ) && ossl_bio_restore_error (bio ))
2246+ rb_set_errinfo (Qnil );
2247+
21562248 if (ret == 1 ) /* Have already received close_notify */
21572249 return Qnil ;
21582250 if (ret == 0 ) /* Sent close_notify, but we don't wait for reply */
0 commit comments