@@ -37,7 +37,7 @@ static VALUE eSSLErrorWaitReadable;
3737static VALUE eSSLErrorWaitWritable ;
3838
3939static ID id_call , ID_callback_state , id_tmp_dh_callback ,
40- id_npn_protocols_encoded , id_each ;
40+ id_npn_protocols_encoded , id_each , id_bio ;
4141static VALUE sym_exception , sym_wait_readable , sym_wait_writable ;
4242
4343static ID id_i_cert_store , id_i_ca_file , id_i_ca_path , id_i_verify_mode ,
@@ -1557,13 +1557,21 @@ peeraddr_ip_str(VALUE self)
15571557 rb_eSystemCallError , (VALUE )0 );
15581558}
15591559
1560+ static int
1561+ is_real_socket (VALUE io )
1562+ {
1563+ return RB_TYPE_P (io , T_FILE );
1564+ }
1565+
15601566/*
15611567 * call-seq:
15621568 * SSLSocket.new(io) => aSSLSocket
15631569 * SSLSocket.new(io, ctx) => aSSLSocket
15641570 *
1565- * Creates a new SSL socket from _io_ which must be a real IO object (not an
1566- * IO-like object that responds to read/write).
1571+ * Creates a new SSL socket from the underlying socket _io_ and _ctx_.
1572+ *
1573+ * _io_ must be an IO object, typically a TCPSocket or Socket from the socket
1574+ * library, or an IO-like object that supports the typical IO methods.
15671575 *
15681576 * If _ctx_ is provided the SSL Sockets initial params will be taken from
15691577 * the context.
@@ -1572,6 +1580,22 @@ peeraddr_ip_str(VALUE self)
15721580 *
15731581 * This method will freeze the SSLContext if one is provided;
15741582 * however, session management is still allowed in the frozen SSLContext.
1583+ *
1584+ * == Support for IO-like objects
1585+ *
1586+ * Support for IO-like objects was added in version 3.3 and is experimental.
1587+ *
1588+ * As of version 3.3, SSLSocket uses the following methods:
1589+ *
1590+ * - <tt>write_nonblock</tt> with the <tt>exception: false</tt> option
1591+ * - <tt>read_nonblock</tt> with the <tt>exception: false</tt> option
1592+ * - <tt>wait_readable</tt>
1593+ * - <tt>wait_writable</tt>
1594+ * - <tt>flush</tt>
1595+ * - <tt>close</tt>
1596+ * - <tt>closed?</tt>
1597+ *
1598+ * Note that future versions may require additional methods to be implemented.
15751599 */
15761600static VALUE
15771601ossl_ssl_initialize (int argc , VALUE * argv , VALUE self )
@@ -1591,9 +1615,18 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
15911615 rb_ivar_set (self , id_i_context , v_ctx );
15921616 ossl_sslctx_setup (v_ctx );
15931617
1594- if (rb_respond_to (io , rb_intern ("nonblock=" )))
1595- rb_funcall (io , rb_intern ("nonblock=" ), 1 , Qtrue );
1596- Check_Type (io , T_FILE );
1618+ if (is_real_socket (io )) {
1619+ rb_io_t * fptr ;
1620+ GetOpenFile (io , fptr );
1621+ rb_io_set_nonblock (fptr );
1622+ }
1623+ else {
1624+ // Not meant to be a comprehensive check
1625+ if (!rb_respond_to (io , rb_intern ("read_nonblock" )) ||
1626+ !rb_respond_to (io , rb_intern ("write_nonblock" )))
1627+ rb_raise (rb_eTypeError , "io must be a real IO object or an IO-like "
1628+ "object that responds to read_nonblock and write_nonblock" );
1629+ }
15971630 rb_ivar_set (self , id_i_io , io );
15981631
15991632 ssl = SSL_new (ctx );
@@ -1625,27 +1658,59 @@ ossl_ssl_setup(VALUE self)
16251658{
16261659 VALUE io ;
16271660 SSL * ssl ;
1628- rb_io_t * fptr ;
16291661
16301662 GetSSL (self , ssl );
16311663 if (ssl_started (ssl ))
16321664 return Qtrue ;
16331665
16341666 io = rb_attr_get (self , id_i_io );
1635- GetOpenFile (io , fptr );
1636- rb_io_check_readable (fptr );
1637- rb_io_check_writable (fptr );
1638- if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1639- ossl_raise (eSSLError , "SSL_set_fd" );
1667+ if (is_real_socket (io )) {
1668+ rb_io_t * fptr ;
1669+ GetOpenFile (io , fptr );
1670+ rb_io_check_readable (fptr );
1671+ rb_io_check_writable (fptr );
1672+ if (!SSL_set_fd (ssl , TO_SOCKET (rb_io_descriptor (io ))))
1673+ ossl_raise (eSSLError , "SSL_set_fd" );
1674+ }
1675+ else {
1676+ VALUE bobj = ossl_bio_new (io );
1677+ rb_ivar_set (self , id_bio , bobj );
1678+
1679+ BIO * bio = ossl_bio_get (bobj );
1680+ if (!BIO_up_ref (bio ))
1681+ ossl_raise (eSSLError , "BIO_up_ref" );
1682+ SSL_set_bio (ssl , bio , bio );
1683+ }
16401684
16411685 return Qtrue ;
16421686}
16431687
1688+ static void
1689+ check_bio_error (VALUE self , SSL * ssl , VALUE bobj , int ret )
1690+ {
1691+ VALUE cb_state = rb_attr_get (self , ID_callback_state );
1692+ if (!NIL_P (cb_state )) {
1693+ /* must cleanup OpenSSL error stack before re-raising */
1694+ ossl_clear_error ();
1695+ rb_jump_tag (NUM2INT (cb_state ));
1696+ }
1697+
1698+ // Socket BIO -> nothing to do
1699+ if (NIL_P (bobj )) {
16441700#ifdef _WIN32
1645- #define ssl_get_error (ssl , ret ) (errno = rb_w32_map_errno(WSAGetLastError()), SSL_get_error((ssl), (ret)))
1646- #else
1647- #define ssl_get_error (ssl , ret ) SSL_get_error((ssl), (ret))
1701+ errno = rb_w32_map_errno (WSAGetLastError ());
16481702#endif
1703+ return ;
1704+ }
1705+
1706+ int state = ossl_bio_state (bobj );
1707+ if (!state ) {
1708+ errno = 0 ;
1709+ return ;
1710+ }
1711+ ossl_clear_error ();
1712+ rb_jump_tag (state );
1713+ }
16491714
16501715static void
16511716write_would_block (int nonblock )
@@ -1685,6 +1750,11 @@ no_exception_p(VALUE opts)
16851750static void
16861751io_wait_writable (VALUE io )
16871752{
1753+ if (!is_real_socket (io )) {
1754+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_writable" ), 0 , NULL )))
1755+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
1756+ return ;
1757+ }
16881758#ifdef HAVE_RB_IO_MAYBE_WAIT
16891759 if (!rb_io_maybe_wait_writable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
16901760 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
@@ -1699,6 +1769,11 @@ io_wait_writable(VALUE io)
16991769static void
17001770io_wait_readable (VALUE io )
17011771{
1772+ if (!is_real_socket (io )) {
1773+ if (!RTEST (rb_funcallv (io , rb_intern ("wait_readable" ), 0 , NULL )))
1774+ rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
1775+ return ;
1776+ }
17021777#ifdef HAVE_RB_IO_MAYBE_WAIT
17031778 if (!rb_io_maybe_wait_readable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
17041779 rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become readable!" );
@@ -1715,28 +1790,22 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
17151790{
17161791 SSL * ssl ;
17171792 int ret , ret2 ;
1718- VALUE cb_state ;
17191793 int nonblock = opts != Qfalse ;
17201794
1721- rb_ivar_set (self , ID_callback_state , Qnil );
1722-
17231795 GetSSL (self , ssl );
17241796
1725- VALUE io = rb_attr_get (self , id_i_io );
1797+ VALUE io = rb_attr_get (self , id_i_io ),
1798+ bobj = rb_attr_get (self , id_bio );
1799+
1800+ rb_ivar_set (self , ID_callback_state , Qnil );
17261801 for (;;) {
17271802 ret = func (ssl );
1728-
1729- cb_state = rb_attr_get (self , ID_callback_state );
1730- if (!NIL_P (cb_state )) {
1731- /* must cleanup OpenSSL error stack before re-raising */
1732- ossl_clear_error ();
1733- rb_jump_tag (NUM2INT (cb_state ));
1734- }
1803+ check_bio_error (self , ssl , bobj , ret );
17351804
17361805 if (ret > 0 )
17371806 break ;
17381807
1739- switch ((ret2 = ssl_get_error (ssl , ret ))) {
1808+ switch ((ret2 = SSL_get_error (ssl , ret ))) {
17401809 case SSL_ERROR_WANT_WRITE :
17411810 if (no_exception_p (opts )) { return sym_wait_writable ; }
17421811 write_would_block (nonblock );
@@ -1886,7 +1955,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
18861955{
18871956 SSL * ssl ;
18881957 int ilen ;
1889- VALUE len , str , cb_state ;
1958+ VALUE len , str ;
18901959 VALUE opts = Qnil ;
18911960
18921961 if (nonblock ) {
@@ -1914,21 +1983,17 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19141983 return str ;
19151984 }
19161985
1917- VALUE io = rb_attr_get (self , id_i_io );
1986+ VALUE io = rb_attr_get (self , id_i_io ),
1987+ bobj = rb_attr_get (self , id_bio );
19181988
1989+ rb_ivar_set (self , ID_callback_state , Qnil );
19191990 for (;;) {
19201991 rb_str_locktmp (str );
19211992 int nread = SSL_read (ssl , RSTRING_PTR (str ), ilen );
19221993 rb_str_unlocktmp (str );
1994+ check_bio_error (self , ssl , bobj , nread );
19231995
1924- cb_state = rb_attr_get (self , ID_callback_state );
1925- if (!NIL_P (cb_state )) {
1926- rb_ivar_set (self , ID_callback_state , Qnil );
1927- ossl_clear_error ();
1928- rb_jump_tag (NUM2INT (cb_state ));
1929- }
1930-
1931- switch (ssl_get_error (ssl , nread )) {
1996+ switch (SSL_get_error (ssl , nread )) {
19321997 case SSL_ERROR_NONE :
19331998 rb_str_set_len (str , nread );
19341999 return str ;
@@ -2020,30 +2085,25 @@ ossl_ssl_write_internal_safe(VALUE _args)
20202085
20212086 SSL * ssl ;
20222087 int num , nonblock = opts != Qfalse ;
2023- VALUE cb_state ;
20242088
20252089 GetSSL (self , ssl );
20262090 if (!ssl_started (ssl ))
20272091 rb_raise (eSSLError , "SSL session is not started yet" );
20282092
2029- VALUE io = rb_attr_get (self , id_i_io );
2030-
20312093 /* SSL_write(3ssl) manpage states num == 0 is undefined */
20322094 num = RSTRING_LENINT (str );
20332095 if (num == 0 )
20342096 return INT2FIX (0 );
20352097
2098+ VALUE io = rb_attr_get (self , id_i_io ),
2099+ bobj = rb_attr_get (self , id_bio );
2100+
2101+ rb_ivar_set (self , ID_callback_state , Qnil );
20362102 for (;;) {
20372103 int nwritten = SSL_write (ssl , RSTRING_PTR (str ), num );
2104+ check_bio_error (self , ssl , bobj , nwritten );
20382105
2039- cb_state = rb_attr_get (self , ID_callback_state );
2040- if (!NIL_P (cb_state )) {
2041- rb_ivar_set (self , ID_callback_state , Qnil );
2042- ossl_clear_error ();
2043- rb_jump_tag (NUM2INT (cb_state ));
2044- }
2045-
2046- switch (ssl_get_error (ssl , nwritten )) {
2106+ switch (SSL_get_error (ssl , nwritten )) {
20472107 case SSL_ERROR_NONE :
20482108 return INT2NUM (nwritten );
20492109 case SSL_ERROR_WANT_WRITE :
@@ -2142,7 +2202,14 @@ ossl_ssl_stop(VALUE self)
21422202 GetSSL (self , ssl );
21432203 if (!ssl_started (ssl ))
21442204 return Qnil ;
2205+
21452206 ret = SSL_shutdown (ssl );
2207+
2208+ /* XXX: Suppressing errors from the underlying socket */
2209+ VALUE bobj = rb_attr_get (self , id_bio );
2210+ if (!NIL_P (bobj ) && ossl_bio_state (bobj ))
2211+ rb_set_errinfo (Qnil );
2212+
21462213 if (ret == 1 ) /* Have already received close_notify */
21472214 return Qnil ;
21482215 if (ret == 0 ) /* Sent close_notify, but we don't wait for reply */
@@ -3120,6 +3187,7 @@ Init_ossl_ssl(void)
31203187 id_tmp_dh_callback = rb_intern_const ("tmp_dh_callback" );
31213188 id_npn_protocols_encoded = rb_intern_const ("npn_protocols_encoded" );
31223189 id_each = rb_intern_const ("each" );
3190+ id_bio = rb_intern_const ("bio" );
31233191
31243192#define DefIVarID (name ) do \
31253193 id_i_##name = rb_intern_const("@"#name); while (0)
0 commit comments