ext/rubysl/openssl/ossl_ssl.c in rubysl-openssl-2.1.0 vs ext/rubysl/openssl/ossl_ssl.c in rubysl-openssl-2.2.0

- old
+ new

@@ -27,10 +27,13 @@ VALUE mSSL; VALUE eSSLError; VALUE cSSLContext; VALUE cSSLSocket; +static VALUE eSSLErrorWaitReadable; +static VALUE eSSLErrorWaitWritable; + #define ossl_sslctx_set_cert(o,v) rb_iv_set((o),"@cert",(v)) #define ossl_sslctx_set_key(o,v) rb_iv_set((o),"@key",(v)) #define ossl_sslctx_set_client_ca(o,v) rb_iv_set((o),"@client_ca",(v)) #define ossl_sslctx_set_ca_file(o,v) rb_iv_set((o),"@ca_file",(v)) #define ossl_sslctx_set_ca_path(o,v) rb_iv_set((o),"@ca_path",(v)) @@ -98,10 +101,12 @@ "sync_close", }; ID ID_callback_state; +static VALUE sym_exception; + /* * SSLContext class */ struct { const char *name; @@ -416,11 +421,11 @@ if (state) { rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); } /* - * return 0 which means to OpenSSL that the the session is still + * return 0 which means to OpenSSL that the session is still * valid (since we created Ruby Session object) and was not freed by us * with SSL_SESSION_free(). Call SSLContext#remove_session(sess) in * session_get_cb block if you don't want OpenSSL to cache the session * internally. */ @@ -470,11 +475,11 @@ */ } } static VALUE -ossl_sslctx_add_extra_chain_cert_i(VALUE i, VALUE arg) +ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) { X509 *x509; SSL_CTX *ctx; Data_Get_Struct(arg, SSL_CTX, ctx); @@ -711,11 +716,11 @@ val = ossl_sslctx_get_client_ca(self); if(!NIL_P(val)){ if(TYPE(val) == T_ARRAY){ for(i = 0; i < RARRAY_LEN(val); i++){ - client_ca = GetX509CertPtr(rb_ary_entry(val, i)); + client_ca = GetX509CertPtr(RARRAY_PTR(val)[i]); if (!SSL_CTX_add_client_CA(ctx, client_ca)){ /* Copies X509_NAME => FREE it. */ ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } } @@ -1090,10 +1095,11 @@ } /* * SSLSocket class */ +#ifndef OPENSSL_NO_SOCK static void ossl_ssl_shutdown(SSL *ssl) { int i, rc; @@ -1103,11 +1109,11 @@ for (i = 0; i < 4; ++i) { /* * Ignore the case SSL_shutdown returns -1. Empty handshake_func * must not happen. */ - if (rc = SSL_shutdown(ssl)) + if ((rc = SSL_shutdown(ssl))) break; } SSL_clear(ssl); ERR_clear_error(); } @@ -1129,11 +1135,11 @@ * call-seq: * SSLSocket.new(io) => aSSLSocket * SSLSocket.new(io, ctx) => aSSLSocket * * Creates a new SSL socket from +io+ which must be a real ruby object (not an - * IO-like object that responds to read/write. + * IO-like object that responds to read/write). * * If +ctx+ is provided the SSL Sockets initial params will be taken from * the context. * * The OpenSSL::Buffering module provides additional IO methods. @@ -1227,22 +1233,20 @@ static void write_would_block(int nonblock) { if (nonblock) { - VALUE exc = ossl_exc_new(eSSLError, "write would block"); - rb_extend_object(exc, rb_mWaitWritable); + VALUE exc = ossl_exc_new(eSSLErrorWaitWritable, "write would block"); rb_exc_raise(exc); } } static void read_would_block(int nonblock) { if (nonblock) { - VALUE exc = ossl_exc_new(eSSLError, "read would block"); - rb_extend_object(exc, rb_mWaitReadable); + VALUE exc = ossl_exc_new(eSSLErrorWaitReadable, "read would block"); rb_exc_raise(exc); } } static VALUE @@ -1369,14 +1373,20 @@ static VALUE ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) { SSL *ssl; int ilen, nread = 0; + int no_exception = 0; VALUE len, str; rb_io_t *fptr; + VALUE opts = Qnil; - rb_scan_args(argc, argv, "11", &len, &str); + rb_scan_args(argc, argv, "11:", &len, &str, &opts); + + if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception)) + no_exception = 1; + ilen = NUM2INT(len); if(NIL_P(str)) str = rb_str_new(0, ilen); else{ StringValue(str); rb_str_modify(str); @@ -1393,21 +1403,27 @@ nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LENINT(str)); switch(ssl_get_error(ssl, nread)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_ZERO_RETURN: + if (no_exception) { return Qnil; } rb_eof_error(); case SSL_ERROR_WANT_WRITE: + if (no_exception) { return ID2SYM(rb_intern("wait_writable")); } write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: + if (no_exception) { return ID2SYM(rb_intern("wait_readable")); } read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: - if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); + if(ERR_peek_error() == 0 && nread == 0) { + if (no_exception) { return Qnil; } + rb_eof_error(); + } rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_read"); } } @@ -1441,13 +1457,15 @@ /* * call-seq: * ssl.sysread_nonblock(length) => string * ssl.sysread_nonblock(length, buffer) => buffer + * ssl.sysread_nonblock(length[, buffer [, opts]) => buffer * * A non-blocking version of #sysread. Raises an SSLError if reading would - * block. + * block. If "exception: false" is passed, this method returns a symbol of + * :wait_readable, :wait_writable, or nil, rather than raising an exception. * * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ * is provided the data will be written into it. */ static VALUE @@ -1455,11 +1473,11 @@ { return ossl_ssl_read_internal(argc, argv, self, 1); } static VALUE -ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock) +ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock, int no_exception) { SSL *ssl; int nwrite = 0; rb_io_t *fptr; @@ -1472,14 +1490,16 @@ nwrite = SSL_write(ssl, RSTRING_PTR(str), RSTRING_LENINT(str)); switch(ssl_get_error(ssl, nwrite)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_WANT_WRITE: + if (no_exception) { return ID2SYM(rb_intern("wait_writable")); } write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: + if (no_exception) { return ID2SYM(rb_intern("wait_readable")); } read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(0); @@ -1505,24 +1525,33 @@ * Writes +string+ to the SSL connection. */ static VALUE ossl_ssl_write(VALUE self, VALUE str) { - return ossl_ssl_write_internal(self, str, 0); + return ossl_ssl_write_internal(self, str, 0, 0); } /* * call-seq: * ssl.syswrite_nonblock(string) => Integer * * Writes +string+ to the SSL connection in a non-blocking manner. Raises an * SSLError if writing would block. */ static VALUE -ossl_ssl_write_nonblock(VALUE self, VALUE str) +ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self) { - return ossl_ssl_write_internal(self, str, 1); + VALUE str; + VALUE opts = Qnil; + int no_exception = 0; + + rb_scan_args(argc, argv, "1:", &str, &opts); + + if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception)) + no_exception = 1; + + return ossl_ssl_write_internal(self, str, 1, no_exception); } /* * call-seq: * ssl.sysclose => nil @@ -1631,11 +1660,11 @@ return ary; } /* * call-seq: -* ssl.version => String +* ssl.ssl_version => String * * Returns a String representing the SSL/TLS version that was negotiated * for the connection, for example "TLSv1.2". */ static VALUE @@ -1792,11 +1821,11 @@ ca = SSL_get_client_CA_list(ssl); return ossl_x509name_sk2ary(ca); } -#ifdef HAVE_OPENSSL_NPN_NEGOTIATED +# ifdef HAVE_OPENSSL_NPN_NEGOTIATED /* * call-seq: * ssl.npn_protocol => String * * Returns the protocol string that was finally selected by the client @@ -1815,11 +1844,12 @@ if (!outlen) return Qnil; else return rb_str_new((const char *) out, outlen); } -#endif +# endif +#endif /* !defined(OPENSSL_NO_SOCK) */ void Init_ossl_ssl() { int i; @@ -1850,10 +1880,14 @@ /* Document-class: OpenSSL::SSL::SSLError * * Generic error class raised by SSLSocket and SSLContext. */ eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError); + eSSLErrorWaitReadable = rb_define_class_under(mSSL, "SSLErrorWaitReadable", eSSLError); + rb_include_module(eSSLErrorWaitReadable, rb_mWaitReadable); + eSSLErrorWaitWritable = rb_define_class_under(mSSL, "SSLErrorWaitWritable", eSSLError); + rb_include_module(eSSLErrorWaitWritable, rb_mWaitWritable); Init_ossl_ssl_session(); /* Document-class: OpenSSL::SSL::SSLContext * @@ -1968,11 +2002,11 @@ */ rb_attr(cSSLContext, rb_intern("tmp_dh_callback"), 1, 1, Qfalse); /* * Sets the context in which a session can be reused. This allows - * sessions for multiple applications to be distinguished, for exapmle, by + * sessions for multiple applications to be distinguished, for example, by * name. */ rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse); /* @@ -2142,10 +2176,13 @@ * The following attributes are available but don't show up in rdoc. * * io, context, sync_close * */ cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject); +#ifdef OPENSSL_NO_SOCK + rb_define_method(cSSLSocket, "initialize", rb_notimplement, -1); +#else rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); for(i = 0; i < numberof(ossl_ssl_attr_readers); i++) rb_attr(cSSLSocket, rb_intern(ossl_ssl_attr_readers[i]), 1, 0, Qfalse); for(i = 0; i < numberof(ossl_ssl_attrs); i++) rb_attr(cSSLSocket, rb_intern(ossl_ssl_attrs[i]), 1, 1, Qfalse); @@ -2156,11 +2193,11 @@ rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0); rb_define_method(cSSLSocket, "accept_nonblock", ossl_ssl_accept_nonblock, 0); rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1); rb_define_private_method(cSSLSocket, "sysread_nonblock", ossl_ssl_read_nonblock, -1); rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1); - rb_define_private_method(cSSLSocket, "syswrite_nonblock", ossl_ssl_write_nonblock, 1); + rb_define_private_method(cSSLSocket, "syswrite_nonblock", ossl_ssl_write_nonblock, -1); rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0); rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0); rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0); rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0); rb_define_method(cSSLSocket, "ssl_version", ossl_ssl_get_version, 0); @@ -2170,12 +2207,13 @@ rb_define_method(cSSLSocket, "session_reused?", ossl_ssl_session_reused, 0); /* implementation of OpenSSL::SSL::SSLSocket#session is in lib/openssl/ssl.rb */ rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1); rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0); rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0); -#ifdef HAVE_OPENSSL_NPN_NEGOTIATED +# ifdef HAVE_OPENSSL_NPN_NEGOTIATED rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); +# endif #endif #define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, INT2NUM(SSL_##x)) ossl_ssl_def_const(VERIFY_NONE); @@ -2228,6 +2266,8 @@ #endif ossl_ssl_def_const(OP_PKCS1_CHECK_1); ossl_ssl_def_const(OP_PKCS1_CHECK_2); ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG); ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); + + sym_exception = ID2SYM(rb_intern("exception")); }