@@ -37,7 +37,7 @@ static VALUE eSSLErrorWaitReadable;
37
37
static VALUE eSSLErrorWaitWritable ;
38
38
39
39
static 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 ;
41
41
static VALUE sym_exception , sym_wait_readable , sym_wait_writable ;
42
42
43
43
static 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)
1557
1557
rb_eSystemCallError , (VALUE )0 );
1558
1558
}
1559
1559
1560
+ static int
1561
+ is_real_socket (VALUE io )
1562
+ {
1563
+ return RB_TYPE_P (io , T_FILE );
1564
+ }
1565
+
1560
1566
/*
1561
1567
* call-seq:
1562
1568
* SSLSocket.new(io) => aSSLSocket
1563
1569
* SSLSocket.new(io, ctx) => aSSLSocket
1564
1570
*
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.
1567
1575
*
1568
1576
* If _ctx_ is provided the SSL Sockets initial params will be taken from
1569
1577
* the context.
@@ -1572,6 +1580,22 @@ peeraddr_ip_str(VALUE self)
1572
1580
*
1573
1581
* This method will freeze the SSLContext if one is provided;
1574
1582
* 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.
1575
1599
*/
1576
1600
static VALUE
1577
1601
ossl_ssl_initialize (int argc , VALUE * argv , VALUE self )
@@ -1591,9 +1615,18 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
1591
1615
rb_ivar_set (self , id_i_context , v_ctx );
1592
1616
ossl_sslctx_setup (v_ctx );
1593
1617
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
+ }
1597
1630
rb_ivar_set (self , id_i_io , io );
1598
1631
1599
1632
ssl = SSL_new (ctx );
@@ -1625,27 +1658,59 @@ ossl_ssl_setup(VALUE self)
1625
1658
{
1626
1659
VALUE io ;
1627
1660
SSL * ssl ;
1628
- rb_io_t * fptr ;
1629
1661
1630
1662
GetSSL (self , ssl );
1631
1663
if (ssl_started (ssl ))
1632
1664
return Qtrue ;
1633
1665
1634
1666
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
+ }
1640
1684
1641
1685
return Qtrue ;
1642
1686
}
1643
1687
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 )) {
1644
1700
#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 ());
1648
1702
#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
+ }
1649
1714
1650
1715
static void
1651
1716
write_would_block (int nonblock )
@@ -1685,6 +1750,11 @@ no_exception_p(VALUE opts)
1685
1750
static void
1686
1751
io_wait_writable (VALUE io )
1687
1752
{
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
+ }
1688
1758
#ifdef HAVE_RB_IO_MAYBE_WAIT
1689
1759
if (!rb_io_maybe_wait_writable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
1690
1760
rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
@@ -1699,6 +1769,11 @@ io_wait_writable(VALUE io)
1699
1769
static void
1700
1770
io_wait_readable (VALUE io )
1701
1771
{
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
+ }
1702
1777
#ifdef HAVE_RB_IO_MAYBE_WAIT
1703
1778
if (!rb_io_maybe_wait_readable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
1704
1779
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)
1715
1790
{
1716
1791
SSL * ssl ;
1717
1792
int ret , ret2 ;
1718
- VALUE cb_state ;
1719
1793
int nonblock = opts != Qfalse ;
1720
1794
1721
- rb_ivar_set (self , ID_callback_state , Qnil );
1722
-
1723
1795
GetSSL (self , ssl );
1724
1796
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 );
1726
1801
for (;;) {
1727
1802
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 );
1735
1804
1736
1805
if (ret > 0 )
1737
1806
break ;
1738
1807
1739
- switch ((ret2 = ssl_get_error (ssl , ret ))) {
1808
+ switch ((ret2 = SSL_get_error (ssl , ret ))) {
1740
1809
case SSL_ERROR_WANT_WRITE :
1741
1810
if (no_exception_p (opts )) { return sym_wait_writable ; }
1742
1811
write_would_block (nonblock );
@@ -1886,7 +1955,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
1886
1955
{
1887
1956
SSL * ssl ;
1888
1957
int ilen ;
1889
- VALUE len , str , cb_state ;
1958
+ VALUE len , str ;
1890
1959
VALUE opts = Qnil ;
1891
1960
1892
1961
if (nonblock ) {
@@ -1914,21 +1983,17 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
1914
1983
return str ;
1915
1984
}
1916
1985
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 );
1918
1988
1989
+ rb_ivar_set (self , ID_callback_state , Qnil );
1919
1990
for (;;) {
1920
1991
rb_str_locktmp (str );
1921
1992
int nread = SSL_read (ssl , RSTRING_PTR (str ), ilen );
1922
1993
rb_str_unlocktmp (str );
1994
+ check_bio_error (self , ssl , bobj , nread );
1923
1995
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 )) {
1932
1997
case SSL_ERROR_NONE :
1933
1998
rb_str_set_len (str , nread );
1934
1999
return str ;
@@ -2020,30 +2085,25 @@ ossl_ssl_write_internal_safe(VALUE _args)
2020
2085
2021
2086
SSL * ssl ;
2022
2087
int num , nonblock = opts != Qfalse ;
2023
- VALUE cb_state ;
2024
2088
2025
2089
GetSSL (self , ssl );
2026
2090
if (!ssl_started (ssl ))
2027
2091
rb_raise (eSSLError , "SSL session is not started yet" );
2028
2092
2029
- VALUE io = rb_attr_get (self , id_i_io );
2030
-
2031
2093
/* SSL_write(3ssl) manpage states num == 0 is undefined */
2032
2094
num = RSTRING_LENINT (str );
2033
2095
if (num == 0 )
2034
2096
return INT2FIX (0 );
2035
2097
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 );
2036
2102
for (;;) {
2037
2103
int nwritten = SSL_write (ssl , RSTRING_PTR (str ), num );
2104
+ check_bio_error (self , ssl , bobj , nwritten );
2038
2105
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 )) {
2047
2107
case SSL_ERROR_NONE :
2048
2108
return INT2NUM (nwritten );
2049
2109
case SSL_ERROR_WANT_WRITE :
@@ -2142,7 +2202,14 @@ ossl_ssl_stop(VALUE self)
2142
2202
GetSSL (self , ssl );
2143
2203
if (!ssl_started (ssl ))
2144
2204
return Qnil ;
2205
+
2145
2206
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
+
2146
2213
if (ret == 1 ) /* Have already received close_notify */
2147
2214
return Qnil ;
2148
2215
if (ret == 0 ) /* Sent close_notify, but we don't wait for reply */
@@ -3120,6 +3187,7 @@ Init_ossl_ssl(void)
3120
3187
id_tmp_dh_callback = rb_intern_const ("tmp_dh_callback" );
3121
3188
id_npn_protocols_encoded = rb_intern_const ("npn_protocols_encoded" );
3122
3189
id_each = rb_intern_const ("each" );
3190
+ id_bio = rb_intern_const ("bio" );
3123
3191
3124
3192
#define DefIVarID (name ) do \
3125
3193
id_i_##name = rb_intern_const("@"#name); while (0)
0 commit comments