diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c index 42833d901..b4e029592 100644 --- a/ext/openssl/ossl_bio.c +++ b/ext/openssl/ossl_bio.c @@ -40,3 +40,108 @@ ossl_membio2str(BIO *bio) return ret; } + +int +ossl_membio_sock_read(BIO* bio, VALUE io) { + VALUE nonblock_kwargs = rb_hash_new(); + rb_hash_aset(nonblock_kwargs, ID2SYM(rb_intern("exception")), Qfalse); + + printf("reading...\n"); + + VALUE fargs[] = { INT2NUM(4096), nonblock_kwargs }; + VALUE ret = rb_funcallv_public_kw(io, rb_intern("read_nonblock"), 2, fargs, RB_PASS_KEYWORDS); + printf("just read...\n"); + int len; + char *bstr; + + if (RB_TYPE_P(ret, T_STRING)) { + len = RSTRING_LENINT(ret); + bstr = RSTRING_PTR(ret); + printf("read the nonblock: %d...\n", len); + } + else if (ret == ID2SYM(rb_intern("wait_readable"))) { + // BIO_set_retry_read(bio); + return SSL_ERROR_WANT_READ; + } + else if (ret == ID2SYM(rb_intern("wait_writable"))) { + // BIO_set_retry_write(bio); + return SSL_ERROR_WANT_WRITE; + } + else if (NIL_P(ret)) { + printf("fuck the nil\n"); + return SSL_ERROR_ZERO_RETURN; + } + else { + printf("elsing\n"); + rb_raise(rb_eTypeError, "write_nonblock must return an Integer, " + ":wait_readable, or :wait_writable"); + } + + while (len > 0) { + int n = BIO_write(bio, bstr, len); + BIO_clear_retry_flags(bio); + + if (n<=0) + return SSL_ERROR_SYSCALL; // unrecoverable + + bstr += n; + len -= n; + + // // finish handshake if required + // if (!SSL_is_init_finished(client.ssl)) { + // if (do_ssl_handshake() == SSLSTATUS_FAIL) + // return SSL_ERROR_SYSCALL; + // if (!SSL_is_init_finished(client.ssl)) + // // assume there are bytes missing + // return SSL_ERROR_WANT_READ; + // } + } + return SSL_ERROR_NONE; +} + +int +ossl_membio_sock_write(BIO* bio, VALUE io) { + char buf[4096]; + char *p = buf; + + int n = BIO_read(bio, p, 4096); + BIO_clear_retry_flags(bio); + if (n <= 0) { + if (!BIO_should_retry(bio)) + // TODO: raise exception + return -1; + } + + printf("writing to bio 2: %d\n", n); + + VALUE nonblock_kwargs = rb_hash_new(); + rb_hash_aset(nonblock_kwargs, ID2SYM(rb_intern("exception")), Qfalse); + + VALUE fargs[] = { rb_str_new_static(buf, n), nonblock_kwargs }; + + // rb_io_write(rb_stdout ,rb_sprintf("%s\n", RSTRING_PTR(*biobuf))); + // rb_p(*biobuf); + VALUE ret = rb_funcallv_public_kw(io, rb_intern("write_nonblock"), 2, fargs, RB_PASS_KEYWORDS); + + if (RB_INTEGER_TYPE_P(ret)) { + // TODO: resize buffer + return SSL_ERROR_NONE; + } + else if (ret == ID2SYM(rb_intern("wait_readable"))) { + printf("wred\n"); + // BIO_set_retry_read(bio); + return SSL_ERROR_WANT_READ; + } + else if (ret == ID2SYM(rb_intern("wait_writable"))) { + printf("wwrit\n"); + // BIO_set_retry_write(bio); + return SSL_ERROR_WANT_WRITE; + } else if (NIL_P(ret)) { + printf("closed\n"); + return SSL_ERROR_ZERO_RETURN; + } + else { + rb_raise(rb_eTypeError, "write_nonblock must return an Integer, " + ":wait_readable, or :wait_writable"); + } +} \ No newline at end of file diff --git a/ext/openssl/ossl_bio.h b/ext/openssl/ossl_bio.h index da68c5e5a..a07b8572e 100644 --- a/ext/openssl/ossl_bio.h +++ b/ext/openssl/ossl_bio.h @@ -12,5 +12,7 @@ BIO *ossl_obj2bio(volatile VALUE *); VALUE ossl_membio2str(BIO*); +int ossl_membio_sock_read(BIO *, VALUE); +int ossl_membio_sock_write(BIO * , VALUE); #endif diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 9f374b65f..0f3f0ce67 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -57,8 +57,15 @@ static ID id_i_io, id_i_context, id_i_hostname; static int ossl_ssl_ex_vcb_idx; static int ossl_ssl_ex_ptr_idx; +static int ossl_ssl_ex_rbio_idx; +static int ossl_ssl_ex_wbio_idx; static int ossl_sslctx_ex_ptr_idx; + +int IsSock(VALUE io) { + return RB_TYPE_P(io, T_FILE); +} + static void ossl_sslctx_mark(void *ptr) { @@ -1639,7 +1646,7 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) if (rb_respond_to(io, rb_intern("nonblock="))) rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); - Check_Type(io, T_FILE); + // Check_Type(io, T_FILE); rb_ivar_set(self, id_i_io, io); ssl = SSL_new(ctx); @@ -1682,11 +1689,22 @@ ossl_ssl_setup(VALUE self) return Qtrue; io = rb_attr_get(self, id_i_io); - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - rb_io_check_writable(fptr); - if (!SSL_set_fd(ssl, TO_SOCKET(rb_io_descriptor(io)))) - ossl_raise(eSSLError, "SSL_set_fd"); + + if (IsSock(io)) { + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + rb_io_check_writable(fptr); + if (!SSL_set_fd(ssl, TO_SOCKET(rb_io_descriptor(io)))) + ossl_raise(eSSLError, "SSL_set_fd"); + } else { + // something which quacks like an IO + // TODO: how to best ensure this from the C API?? + BIO *rbio = BIO_new(BIO_s_mem()); + BIO *wbio = BIO_new(BIO_s_mem()); + SSL_set_bio(ssl, rbio, wbio); + SSL_set_ex_data(ssl, ossl_ssl_ex_rbio_idx, (void *)rbio); + SSL_set_ex_data(ssl, ossl_ssl_ex_wbio_idx, (void *)wbio); + } return Qtrue; } @@ -1732,6 +1750,22 @@ no_exception_p(VALUE opts) #endif +int ossl_ssl_connect_impl(SSL *ssl) +{ + return SSL_connect(ssl); +} + +int ossl_ssl_read_impl(SSL *ssl, VALUE str, int ilen) +{ + return SSL_read(ssl, RSTRING_PTR(str), ilen); +} + +int ossl_ssl_write_impl(SSL *ssl, VALUE tmp, int num) +{ + return SSL_write(ssl, RSTRING_PTR(tmp), num); +} + + static void io_wait_writable(VALUE io) { @@ -1773,6 +1807,15 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) GetSSL(self, ssl); VALUE io = rb_attr_get(self, id_i_io); + + int is_sock = IsSock(io); + BIO *rbio, *wbio; + + if (!is_sock) { + rbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_rbio_idx); + wbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_wbio_idx); + } + for (;;) { ret = func(ssl); @@ -1782,11 +1825,35 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) ossl_clear_error(); rb_jump_tag(NUM2INT(cb_state)); } + printf("connect ret: %d\n", ret); if (ret > 0) break; - switch ((ret2 = ssl_get_error(ssl, ret))) { + ret2 = ssl_get_error(ssl, ret); + printf("WANT_READ: %d, WANT_WRITE. %d\n", SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE); + printf("connect ret2: %d\n", ret2); + + if (!is_sock) { + if(ret2 == SSL_ERROR_WANT_WRITE) { + ret2 = ossl_membio_sock_read(rbio, io); + if (ret2 == SSL_ERROR_NONE) { + ret2 = SSL_ERROR_WANT_WRITE; + printf("out fuckerz\n"); + break; + } + } else if (ret2 == SSL_ERROR_WANT_READ) { + ret2 = ossl_membio_sock_write(wbio, io); + if (ret2 == SSL_ERROR_NONE) { + printf("out fuckerz\n"); + break; + // continue; + } + } + } + printf("connect after is_sock ret2: %d\n", ret2); + + switch ((ret2)) { case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); @@ -1848,7 +1915,7 @@ ossl_ssl_connect(VALUE self) { ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse); + return ossl_start_ssl(self, ossl_ssl_connect_impl, "SSL_connect", Qfalse); } /* @@ -1881,7 +1948,7 @@ ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self) ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_connect, "SSL_connect", opts); + return ossl_start_ssl(self, ossl_ssl_connect_impl, "SSL_connect", opts); } /* @@ -1938,6 +2005,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) int ilen; VALUE len, str; VALUE opts = Qnil; + BIO *rbio, *wbio; if (nonblock) { rb_scan_args(argc, argv, "11:", &len, &str, &opts); @@ -1963,11 +2031,28 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) return str; VALUE io = rb_attr_get(self, id_i_io); + int is_sock = IsSock(io); + + if (!is_sock) { + rbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_rbio_idx); + wbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_wbio_idx); + } rb_str_locktmp(str); for (;;) { - int nread = SSL_read(ssl, RSTRING_PTR(str), ilen); - switch (ssl_get_error(ssl, nread)) { + int ret, nread; + + if (!is_sock) { + ret = ossl_membio_sock_read(rbio, io); + if (ret == SSL_ERROR_NONE) { + ret = ossl_membio_sock_write(wbio, io); + } + } else { + nread = ossl_ssl_read_impl(ssl, str, ilen); + ret = ssl_get_error(ssl, nread); + } + + switch (ret) { case SSL_ERROR_NONE: rb_str_unlocktmp(str); rb_str_set_len(str, nread); @@ -2064,7 +2149,16 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) tmp = rb_str_new_frozen(StringValue(str)); VALUE io = rb_attr_get(self, id_i_io); - GetOpenFile(io, fptr); + int is_sock = IsSock(io); + // BIO *rbio; + BIO *wbio; + + if (!is_sock) { + // rbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_rbio_idx); + wbio = (BIO *)SSL_get_ex_data(ssl, ossl_ssl_ex_wbio_idx); + } else { + GetOpenFile(io, fptr); + } /* SSL_write(3ssl) manpage states num == 0 is undefined */ num = RSTRING_LENINT(tmp); @@ -2072,8 +2166,18 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) return INT2FIX(0); for (;;) { - int nwritten = SSL_write(ssl, RSTRING_PTR(tmp), num); - switch (ssl_get_error(ssl, nwritten)) { + + + int nwritten = ossl_ssl_write_impl(ssl, tmp, num); + int ret = ssl_get_error(ssl, nwritten); + + if (!is_sock) { + if (ret == SSL_ERROR_NONE) { + ret = ossl_membio_sock_write(wbio, io); + } + } + + switch (ret) { case SSL_ERROR_NONE: return INT2NUM(nwritten); case SSL_ERROR_WANT_WRITE: @@ -2607,6 +2711,12 @@ Init_ossl_ssl(void) ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0); if (ossl_ssl_ex_ptr_idx < 0) ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_ssl_ex_rbio_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_rbio_idx", 0, 0, 0); + if (ossl_ssl_ex_rbio_idx < 0) + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_ssl_ex_wbio_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_wbio_idx", 0, 0, 0); + if (ossl_ssl_ex_wbio_idx < 0) + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0); if (ossl_sslctx_ex_ptr_idx < 0) ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); @@ -3136,6 +3246,11 @@ Init_ossl_ssl(void) id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded"); id_each = rb_intern_const("each"); + nonblock_kwargs = rb_hash_new(); + rb_hash_aset(nonblock_kwargs, sym_exception, Qfalse); + rb_obj_freeze(nonblock_kwargs); + rb_global_variable(&nonblock_kwargs); + #define DefIVarID(name) do \ id_i_##name = rb_intern_const("@"#name); while (0) diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h index 535c56097..29c9de091 100644 --- a/ext/openssl/ossl_ssl.h +++ b/ext/openssl/ossl_ssl.h @@ -29,6 +29,7 @@ extern const rb_data_type_t ossl_ssl_session_type; extern VALUE mSSL; extern VALUE cSSLSocket; extern VALUE cSSLSession; +static VALUE nonblock_kwargs; void Init_ossl_ssl(void); void Init_ossl_ssl_session(void);