@@ -1607,13 +1607,28 @@ peeraddr_ip_str(VALUE self)
1607
1607
return rb_rescue2 (peer_ip_address , self , fallback_peer_ip_address , (VALUE )0 , rb_eSystemCallError , NULL );
1608
1608
}
1609
1609
1610
+ static int
1611
+ is_real_socket (VALUE io )
1612
+ {
1613
+ return false;
1614
+ return RB_TYPE_P (io , T_FILE );// && false;
1615
+ }
1616
+
1610
1617
/*
1611
1618
* call-seq:
1612
1619
* SSLSocket.new(io) => aSSLSocket
1613
1620
* SSLSocket.new(io, ctx) => aSSLSocket
1614
1621
*
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>
1617
1632
*
1618
1633
* If _ctx_ is provided the SSL Sockets initial params will be taken from
1619
1634
* the context.
@@ -1641,9 +1656,20 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
1641
1656
rb_ivar_set (self , id_i_context , v_ctx );
1642
1657
ossl_sslctx_setup (v_ctx );
1643
1658
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
+ }
1647
1673
rb_ivar_set (self , id_i_io , io );
1648
1674
1649
1675
ssl = SSL_new (ctx );
@@ -1679,18 +1705,28 @@ ossl_ssl_setup(VALUE self)
1679
1705
{
1680
1706
VALUE io ;
1681
1707
SSL * ssl ;
1682
- rb_io_t * fptr ;
1683
1708
1684
1709
GetSSL (self , ssl );
1685
1710
if (ssl_started (ssl ))
1686
1711
return Qtrue ;
1687
1712
1688
1713
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
+ }
1694
1730
1695
1731
return Qtrue ;
1696
1732
}
@@ -1701,6 +1737,32 @@ ossl_ssl_setup(VALUE self)
1701
1737
#define ssl_get_error (ssl , ret ) SSL_get_error((ssl), (ret))
1702
1738
#endif
1703
1739
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
+
1704
1766
static void
1705
1767
write_would_block (int nonblock )
1706
1768
{
@@ -1739,6 +1801,11 @@ no_exception_p(VALUE opts)
1739
1801
static void
1740
1802
io_wait_writable (VALUE io )
1741
1803
{
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
+ }
1742
1809
#ifdef HAVE_RB_IO_MAYBE_WAIT
1743
1810
if (!rb_io_maybe_wait_writable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
1744
1811
rb_raise (IO_TIMEOUT_ERROR , "Timed out while waiting to become writable!" );
@@ -1753,6 +1820,11 @@ io_wait_writable(VALUE io)
1753
1820
static void
1754
1821
io_wait_readable (VALUE io )
1755
1822
{
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
+ }
1756
1828
#ifdef HAVE_RB_IO_MAYBE_WAIT
1757
1829
if (!rb_io_maybe_wait_readable (errno , io , RUBY_IO_TIMEOUT_DEFAULT )) {
1758
1830
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)
1777
1849
GetSSL (self , ssl );
1778
1850
1779
1851
VALUE io = rb_attr_get (self , id_i_io );
1852
+ BIO * bio = SSL_get_rbio (ssl );
1853
+
1780
1854
for (;;) {
1781
1855
ret = func (ssl );
1856
+ if (!is_real_socket (io ))
1857
+ handle_ossl_bio_error (ssl , bio , ret );
1782
1858
1783
1859
cb_state = rb_attr_get (self , ID_callback_state );
1784
1860
if (!NIL_P (cb_state )) {
@@ -1967,10 +2043,14 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
1967
2043
return str ;
1968
2044
1969
2045
VALUE io = rb_attr_get (self , id_i_io );
2046
+ BIO * bio = SSL_get_rbio (ssl );
1970
2047
1971
2048
rb_str_locktmp (str );
1972
2049
for (;;) {
1973
2050
int nread = SSL_read (ssl , RSTRING_PTR (str ), ilen );
2051
+ if (!is_real_socket (io ))
2052
+ handle_ossl_bio_error (ssl , bio , nread );
2053
+
1974
2054
switch (ssl_get_error (ssl , nread )) {
1975
2055
case SSL_ERROR_NONE :
1976
2056
rb_str_unlocktmp (str );
@@ -2067,6 +2147,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
2067
2147
2068
2148
tmp = rb_str_new_frozen (StringValue (str ));
2069
2149
VALUE io = rb_attr_get (self , id_i_io );
2150
+ BIO * bio = SSL_get_rbio (ssl );
2070
2151
2071
2152
/* SSL_write(3ssl) manpage states num == 0 is undefined */
2072
2153
num = RSTRING_LENINT (tmp );
@@ -2075,6 +2156,9 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
2075
2156
2076
2157
for (;;) {
2077
2158
int nwritten = SSL_write (ssl , RSTRING_PTR (tmp ), num );
2159
+ if (!is_real_socket (io ))
2160
+ handle_ossl_bio_error (ssl , bio , nwritten );
2161
+
2078
2162
switch (ssl_get_error (ssl , nwritten )) {
2079
2163
case SSL_ERROR_NONE :
2080
2164
return INT2NUM (nwritten );
@@ -2152,7 +2236,15 @@ ossl_ssl_stop(VALUE self)
2152
2236
GetSSL (self , ssl );
2153
2237
if (!ssl_started (ssl ))
2154
2238
return Qnil ;
2239
+
2155
2240
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
+
2156
2248
if (ret == 1 ) /* Have already received close_notify */
2157
2249
return Qnil ;
2158
2250
if (ret == 0 ) /* Sent close_notify, but we don't wait for reply */
0 commit comments