From 74efef0be85061c3c9e24ee0cfdf5f83c2af9a22 Mon Sep 17 00:00:00 2001 From: knu Date: Fri, 25 Apr 2008 06:51:21 +0000 Subject: Merge from ruby_1_8. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8_7@16193 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 49 +++++ NEWS | 4 + array.c | 2 +- dln.c | 41 ++-- eval.c | 32 ++- ext/openssl/lib/openssl/ssl.rb | 84 +++++-- ext/openssl/ossl_ssl.c | 106 ++++++--- ext/tk/lib/tk.rb | 13 +- ext/tk/lib/tk/scale.rb | 13 +- ext/tk/lib/tk/scrollbar.rb | 13 +- ext/tk/lib/tkextlib/blt/ted.rb | 2 +- ext/tk/lib/tkextlib/blt/unix_dnd.rb | 6 +- ext/tk/lib/tkextlib/treectrl/tktreectrl.rb | 8 +- ext/tk/lib/tkextlib/version.rb | 2 +- ext/tk/sample/ttk_wrapper.rb | 4 +- lib/net/pop.rb | 228 ++++++++++++++----- test/gdbm/test_gdbm.rb | 4 +- test/openssl/test_ssl.rb | 343 ++++++++++++++++++++++++----- version.h | 6 +- 19 files changed, 752 insertions(+), 208 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0ae1cd89ba..1ce27b6aab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,52 @@ +Fri Apr 25 15:46:37 2008 Hidetoshi NAGAI + + * ext/tk/lib/tk.rb, ext/tk/lib/tk/scrollbar.rb, ext/tk/lib/tk/scale.rb: + improve unknonw-option check when create a widget. + + * ext/tk/lib/tkextlib/blt/unix_dnd.rb, ext/tk/lib/tkextlib/blt/ted.rb, + ext/tk/lib/tkextlib/treectrl/tktreectrl.rb: bug fix on 'cget'. + + * ext/tk/lib/tk/menuspec.rb: option check will fail when + TkConfigMethod.__IGNORE_UNKNOWN_CONFIGURE_OPTION__ is true. + + * ext/tk/lib/tk/palette.rb: bug fix. + +Fri Apr 25 12:37:54 2008 Nobuyoshi Nakada + + * array.c (flatten): returns an instance of same class. + [ruby-core:16554] + +Thu Apr 24 23:47:50 2008 Kazuhiro NISHIYAMA + + * lib/net/pop.rb: backported from 1.9. bug#19003 + + * ext/openssl/lib/openssl/ssl.rb: set_params; backported from 1.9. + bug#19552, [ruby-dev:34402] + + * ext/openssl/ossl_ssl.c: ditto. + + * test/openssl/test_ssl.rb: ditto. + +Thu Apr 24 17:06:34 2008 Yukihiro Matsumoto + + * eval.c (THREAD_SAVE_CONTEXT): remove unnecessary + FLUSH_REGISTER_WINDOWS before calling setjmp(). [ruby-core:16285] + +Thu Apr 24 14:15:11 2008 Nobuyoshi Nakada + + * dln.c (dln_find_1): prior files with extensions to files sans + extensions. [ruby-core:16517] + +Wed Apr 23 15:39:31 2008 Akinori MUSHA + + * eval.c (bind_eval): Add Binding#eval, a shorthand method for + eval(str, binding, ..); backported from 1.9. + +Wed Apr 23 15:28:52 2008 Kazuhiro NISHIYAMA + + * test/gdbm/test_gdbm.rb (TestGDBM#test_s_open_no_create): failed + notice moved from comment to assertion message. [ruby-dev:29127] + Wed Apr 23 14:00:05 2008 Akinori MUSHA * lib/mkmf.rb (create_makefile): Add a missing dependency on the diff --git a/NEWS b/NEWS index 568debf36c..6e3de72a37 100644 --- a/NEWS +++ b/NEWS @@ -73,6 +73,10 @@ with all sufficient information, see the ChangeLog file. New methods. + * Binding#eval + + New method. + * Dir#each * Dir#foreach diff --git a/array.c b/array.c index 3789e4f6d9..423030ac41 100644 --- a/array.c +++ b/array.c @@ -3082,7 +3082,7 @@ flatten(ary, level, modified) st_data_t id; stack = rb_ary_new(); - result = rb_ary_new(); + result = ary_new(rb_class_of(ary), RARRAY_LEN(ary)); memo = st_init_numtable(); st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue); *modified = 0; diff --git a/dln.c b/dln.c index 5a96626411..5b99425141 100644 --- a/dln.c +++ b/dln.c @@ -1781,26 +1781,9 @@ dln_find_1(fname, path, exe_flag) } memcpy(bp, fname, i + 1); -#ifndef __MACOS__ - if (stat(fbuf, &st) == 0) { - if (exe_flag == 0) return fbuf; - /* looking for executable */ - if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0) - return fbuf; - } -#else - if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf)) { - if (exe_flag == 0) return mac_fullpath; - /* looking for executable */ - if (stat(mac_fullpath, &st) == 0) { - if (!S_ISDIR(st.st_mode) && eaccess(mac_fullpath, X_OK) == 0) - return mac_fullpath; - } - } -#endif #if defined(DOSISH) if (exe_flag) { - static const char *const extension[] = { + static const char extension[][5] = { #if defined(MSDOS) ".com", ".exe", ".bat", #if defined(DJGPP) @@ -1813,11 +1796,10 @@ dln_find_1(fname, path, exe_flag) ".r", ".R", ".x", ".X", ".bat", ".BAT", /* __human68k__ */ #endif - (char *) NULL }; int j; - for (j = 0; extension[j]; j++) { + for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) { if (fspace < strlen(extension[j])) { fprintf(stderr, "openpath: pathname too long (ignored)\n"); fprintf(stderr, "\tDirectory \"%.*s\"\n", (int) (bp - fbuf), fbuf); @@ -1834,9 +1816,28 @@ dln_find_1(fname, path, exe_flag) #endif } + goto next; } #endif /* MSDOS or _WIN32 or __human68k__ or __EMX__ */ +#ifndef __MACOS__ + if (stat(fbuf, &st) == 0) { + if (exe_flag == 0) return fbuf; + /* looking for executable */ + if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0) + return fbuf; + } +#else + if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf)) { + if (exe_flag == 0) return mac_fullpath; + /* looking for executable */ + if (stat(mac_fullpath, &st) == 0) { + if (!S_ISDIR(st.st_mode) && eaccess(mac_fullpath, X_OK) == 0) + return mac_fullpath; + } + } +#endif + next: /* if not, and no other alternatives, life is bleak */ if (*ep == '\0') { diff --git a/eval.c b/eval.c index 9994015cf5..f691514c26 100644 --- a/eval.c +++ b/eval.c @@ -8471,6 +8471,35 @@ rb_f_binding(self) return bind; } +/* + * call-seq: + * binding.eval(string [, filename [,lineno]]) => obj + * + * Evaluates the Ruby expression(s) in string, in the + * binding's context. If the optional filename and + * lineno parameters are present, they will be used when + * reporting syntax errors. + * + * def getBinding(param) + * return binding + * end + * b = getBinding("hello") + * b.eval("param") #=> "hello" + */ + +static VALUE +bind_eval(argc, argv, bindval) + int argc; + VALUE *argv; + VALUE bindval; +{ + VALUE args[4]; + + rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]); + args[1] = bindval; + return rb_f_eval(argc+1, args, Qnil /* self will be searched in eval */); +} + #define PROC_TSHIFT (FL_USHIFT+1) #define PROC_TMASK (FL_USER1|FL_USER2|FL_USER3) #define PROC_TMAX (PROC_TMASK >> PROC_TSHIFT) @@ -9899,6 +9928,7 @@ Init_Binding() rb_undef_method(CLASS_OF(rb_cBinding), "new"); rb_define_method(rb_cBinding, "clone", proc_clone, 0); rb_define_method(rb_cBinding, "dup", proc_dup, 0); + rb_define_method(rb_cBinding, "eval", bind_eval, -1); rb_define_global_function("binding", rb_f_binding, 0); } @@ -10441,7 +10471,7 @@ rb_thread_switch(n) } #define THREAD_SAVE_CONTEXT(th) \ - (rb_thread_switch((FLUSH_REGISTER_WINDOWS, ruby_setjmp(rb_thread_save_context(th), (th)->context)))) + (rb_thread_switch(ruby_setjmp(rb_thread_save_context(th), (th)->context))) NORETURN(static void rb_thread_restore_context _((rb_thread_t,int))); NORETURN(NOINLINE(static void rb_thread_restore_context_0(rb_thread_t,int,void*))); diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index ad9559f6b6..f722cb0c45 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -20,6 +20,33 @@ require "fcntl" module OpenSSL module SSL + class SSLContext + DEFAULT_PARAMS = { + :ssl_version => "SSLv23", + :verify_mode => OpenSSL::SSL::VERIFY_PEER, + :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW", + :options => OpenSSL::SSL::OP_ALL, + } + + DEFAULT_CERT_STORE = OpenSSL::X509::Store.new + DEFAULT_CERT_STORE.set_default_paths + if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL) + DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + end + + def set_params(params={}) + params = DEFAULT_PARAMS.merge(params) + self.ssl_version = params.delete(:ssl_version) + params.each{|name, value| self.__send__("#{name}=", value) } + if self.verify_mode != OpenSSL::SSL::VERIFY_NONE + unless self.ca_file or self.ca_path or self.cert_store + self.cert_store = DEFAULT_CERT_STORE + end + end + return params + end + end + module SocketForwarder def addr to_io.addr @@ -59,36 +86,43 @@ module OpenSSL end end + def verify_certificate_identity(cert, hostname) + should_verify_common_name = true + cert.extensions.each{|ext| + next if ext.oid != "subjectAltName" + ext.value.split(/,\s+/).each{|general_name| + if /\ADNS:(.*)/ =~ general_name + should_verify_common_name = false + reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + elsif /\AIP Address:(.*)/ =~ general_name + should_verify_common_name = false + return true if $1 == hostname + end + } + } + if should_verify_common_name + cert.subject.to_a.each{|oid, value| + if oid == "CN" + reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + end + } + end + return false + end + module_function :verify_certificate_identity + class SSLSocket include Buffering include SocketForwarder include Nonblock def post_connection_check(hostname) - check_common_name = true - cert = peer_cert - cert.extensions.each{|ext| - next if ext.oid != "subjectAltName" - ext.value.split(/,\s+/).each{|general_name| - if /\ADNS:(.*)/ =~ general_name - check_common_name = false - reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") - return true if /\A#{reg}\z/i =~ hostname - elsif /\AIP Address:(.*)/ =~ general_name - check_common_name = false - return true if $1 == hostname - end - } - } - if check_common_name - cert.subject.to_a.each{|oid, value| - if oid == "CN" - reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") - return true if /\A#{reg}\z/i =~ hostname - end - } + unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname) + raise SSLError, "hostname was not match with the server certificate" end - raise SSLError, "hostname was not match with the server certificate" + return true end def session @@ -120,6 +154,10 @@ module OpenSSL @svr.listen(backlog) end + def shutdown(how=Socket::SHUT_RDWR) + @svr.shutdown(how) + end + def accept sock = @svr.accept begin diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 98c374b95f..675fe657b0 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -141,31 +141,14 @@ ossl_sslctx_s_alloc(VALUE klass) return Data_Wrap_Struct(klass, 0, ossl_sslctx_free, ctx); } -/* - * call-seq: - * SSLContext.new => ctx - * SSLContext.new(:TLSv1) => ctx - * SSLContext.new("SSLv23_client") => ctx - * - * You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS - */ static VALUE -ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self) +ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method) { - VALUE ssl_method; SSL_METHOD *method = NULL; - SSL_CTX *ctx; - int i; const char *s; + int i; - for(i = 0; i < numberof(ossl_sslctx_attrs); i++){ - char buf[32]; - snprintf(buf, sizeof(buf), "@%s", ossl_sslctx_attrs[i]); - rb_iv_set(self, buf, Qnil); - } - if (rb_scan_args(argc, argv, "01", &ssl_method) == 0){ - return self; - } + SSL_CTX *ctx; if(TYPE(ssl_method) == T_SYMBOL) s = rb_id2name(SYM2ID(ssl_method)); else @@ -184,6 +167,33 @@ ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self) ossl_raise(eSSLError, "SSL_CTX_set_ssl_version:"); } + return ssl_method; +} + +/* + * call-seq: + * SSLContext.new => ctx + * SSLContext.new(:TLSv1) => ctx + * SSLContext.new("SSLv23_client") => ctx + * + * You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS + */ +static VALUE +ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE ssl_method; + int i; + + for(i = 0; i < numberof(ossl_sslctx_attrs); i++){ + char buf[32]; + snprintf(buf, sizeof(buf), "@%s", ossl_sslctx_attrs[i]); + rb_iv_set(self, buf, Qnil); + } + if (rb_scan_args(argc, argv, "01", &ssl_method) == 0){ + return self; + } + ossl_sslctx_set_ssl_version(self, ssl_method); + return self; } @@ -436,6 +446,14 @@ ossl_sslctx_add_extra_chain_cert_i(VALUE i, VALUE arg) return i; } +/* + * call-seq: + * ctx.setup => Qtrue # first time + * ctx.setup => nil # thereafter + * + * This method is called automatically when a new SSLSocket is created. + * Normally you do not need to call this method (unless you are writing an extension in C). + */ static VALUE ossl_sslctx_setup(VALUE self) { @@ -769,18 +787,18 @@ ossl_sslctx_get_session_cache_stats(VALUE self) Data_Get_Struct(self, SSL_CTX, ctx); hash = rb_hash_new(); - rb_hash_aset(hash, rb_str_new2("cache_num"), LONG2NUM(SSL_CTX_sess_number(ctx))); - rb_hash_aset(hash, rb_str_new2("connect"), LONG2NUM(SSL_CTX_sess_connect(ctx))); - rb_hash_aset(hash, rb_str_new2("connect_good"), LONG2NUM(SSL_CTX_sess_connect_good(ctx))); - rb_hash_aset(hash, rb_str_new2("connect_renegotiate"), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx))); - rb_hash_aset(hash, rb_str_new2("accept"), LONG2NUM(SSL_CTX_sess_accept(ctx))); - rb_hash_aset(hash, rb_str_new2("accept_good"), LONG2NUM(SSL_CTX_sess_accept_good(ctx))); - rb_hash_aset(hash, rb_str_new2("accept_renegotiate"), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx))); - rb_hash_aset(hash, rb_str_new2("cache_hits"), LONG2NUM(SSL_CTX_sess_hits(ctx))); - rb_hash_aset(hash, rb_str_new2("cb_hits"), LONG2NUM(SSL_CTX_sess_cb_hits(ctx))); - rb_hash_aset(hash, rb_str_new2("cache_misses"), LONG2NUM(SSL_CTX_sess_misses(ctx))); - rb_hash_aset(hash, rb_str_new2("cache_full"), LONG2NUM(SSL_CTX_sess_cache_full(ctx))); - rb_hash_aset(hash, rb_str_new2("timeouts"), LONG2NUM(SSL_CTX_sess_timeouts(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("cache_num")), LONG2NUM(SSL_CTX_sess_number(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("connect")), LONG2NUM(SSL_CTX_sess_connect(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("connect_good")), LONG2NUM(SSL_CTX_sess_connect_good(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("connect_renegotiate")), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("accept")), LONG2NUM(SSL_CTX_sess_accept(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("accept_good")), LONG2NUM(SSL_CTX_sess_accept_good(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("accept_renegotiate")), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("cache_hits")), LONG2NUM(SSL_CTX_sess_hits(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("cb_hits")), LONG2NUM(SSL_CTX_sess_cb_hits(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("cache_misses")), LONG2NUM(SSL_CTX_sess_misses(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("cache_full")), LONG2NUM(SSL_CTX_sess_cache_full(ctx))); + rb_hash_aset(hash, ID2SYM(rb_intern("timeouts")), LONG2NUM(SSL_CTX_sess_timeouts(ctx))); return hash; } @@ -1300,6 +1318,19 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) return arg1; } +static VALUE +ossl_ssl_get_verify_result(VALUE self) +{ + SSL *ssl; + + Data_Get_Struct(self, SSL, ssl); + if (!ssl) { + rb_warning("SSL session is not started yet."); + return Qnil; + } + + return INT2FIX(SSL_get_verify_result(ssl)); +} void Init_ossl_ssl() @@ -1330,18 +1361,22 @@ Init_ossl_ssl() * * The following attributes are available but don't show up in rdoc. * All attributes must be set before calling SSLSocket.new(io, ctx). - * * cert, key, client_ca, ca_file, ca_path, timeout, verify_mode, verify_depth - * * client_cert_cb, tmp_dh_callback, session_id_context, - * * session_add_cb, session_new_cb, session_remove_cb + * * ssl_version, cert, key, client_ca, ca_file, ca_path, timeout, + * * verify_mode, verify_depth client_cert_cb, tmp_dh_callback, + * * session_id_context, session_add_cb, session_new_cb, session_remove_cb */ cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject); rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc); for(i = 0; i < numberof(ossl_sslctx_attrs); i++) rb_attr(cSSLContext, rb_intern(ossl_sslctx_attrs[i]), 1, 1, Qfalse); + rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1); + rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1); rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0); rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1); + rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); + rb_define_const(cSSLContext, "SESSION_CACHE_OFF", LONG2FIX(SSL_SESS_CACHE_OFF)); rb_define_const(cSSLContext, "SESSION_CACHE_CLIENT", LONG2FIX(SSL_SESS_CACHE_CLIENT)); /* doesn't actually do anything in 0.9.8e */ @@ -1395,6 +1430,7 @@ Init_ossl_ssl() rb_define_method(cSSLSocket, "pending", ossl_ssl_pending, 0); rb_define_method(cSSLSocket, "session_reused?", ossl_ssl_session_reused, 0); rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1); + rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0); #define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, INT2FIX(SSL_##x)) diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index a5cb8cb67d..2aafcfecc3 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -4799,8 +4799,15 @@ class TkWindow. # @@ -10,18 +10,24 @@ # # This program is free software. You can re-distribute and/or # modify this program under the same terms as Ruby itself, -# Ruby Distribute License or GNU General Public License. +# Ruby Distribute License. # -# NOTE: You can find Japanese version of this document in -# the doc/net directory of the standard ruby interpreter package. +# NOTE: You can find Japanese version of this document at: +# http://www.ruby-lang.org/ja/man/html/net_pop.html # # $Id$ -# +# # See Net::POP3 for documentation. # require 'net/protocol' require 'digest/md5' +require 'timeout' + +begin + require "openssl/ssl" +rescue LoadError +end module Net @@ -196,10 +202,19 @@ module Net # Class Parameters # - # The default port for POP3 connections, port 110 def POP3.default_port + default_pop3_port() + end + + # The default port for POP3 connections, port 110 + def POP3.default_pop3_port 110 end + + # The default port for POP3S connections, port 995 + def POP3.default_pop3s_port + 995 + end def POP3.socket_type #:nodoc: obsolete Net::InternetMessageIO @@ -220,7 +235,7 @@ module Net # .... # end # - def POP3.APOP( isapop ) + def POP3.APOP(isapop) isapop ? APOP : POP3 end @@ -244,9 +259,9 @@ module Net # m.delete if $DELETE # end # - def POP3.foreach( address, port = nil, - account = nil, password = nil, - isapop = false, &block ) # :yields: message + def POP3.foreach(address, port = nil, + account = nil, password = nil, + isapop = false, &block) # :yields: message start(address, port, account, password, isapop) {|pop| pop.each_mail(&block) } @@ -265,9 +280,9 @@ module Net # file.write m.pop # end # - def POP3.delete_all( address, port = nil, - account = nil, password = nil, - isapop = false, &block ) + def POP3.delete_all(address, port = nil, + account = nil, password = nil, + isapop = false, &block) start(address, port, account, password, isapop) {|pop| pop.delete_all(&block) } @@ -287,22 +302,75 @@ module Net # Net::POP3.auth_only('pop.example.com', 110, # 'YourAccount', 'YourPassword', true) # - def POP3.auth_only( address, port = nil, - account = nil, password = nil, - isapop = false ) + def POP3.auth_only(address, port = nil, + account = nil, password = nil, + isapop = false) new(address, port, isapop).auth_only account, password end # Starts a pop3 session, attempts authentication, and quits. # This method must not be called while POP3 session is opened. # This method raises POPAuthenticationError if authentication fails. - def auth_only( account, password ) + def auth_only(account, password) raise IOError, 'opening previously opened POP session' if started? start(account, password) { ; } end + # + # SSL + # + + @ssl_params = nil + + # call-seq: + # Net::POP.enable_ssl(params = {}) + # + # Enable SSL for all new instances. + # +params+ is passed to OpenSSL::SSLContext#set_params. + def POP3.enable_ssl(*args) + @ssl_params = create_ssl_params(*args) + end + + def POP3.create_ssl_params(verify_or_params = {}, certs = nil) + begin + params = verify_or_params.to_hash + rescue NoMethodError + params = {} + params[:verify_mode] = verify_or_params + if certs + if File.file?(certs) + params[:ca_file] = certs + elsif File.directory?(certs) + params[:ca_path] = certs + end + end + end + return params + end + + # Disable SSL for all new instances. + def POP3.disable_ssl + @ssl_params = nil + end + + def POP3.ssl_params + return @ssl_params + end + + def POP3.use_ssl? + return !@ssl_params.nil? + end + + def POP3.verify + return @ssl_params[:verify_mode] + end + + def POP3.certs + return @ssl_params[:ca_file] || @ssl_params[:ca_path] + end + # # Session management # @@ -323,27 +391,28 @@ module Net # end # end # - def POP3.start( address, port = nil, - account = nil, password = nil, - isapop = false, &block ) # :yield: pop + def POP3.start(address, port = nil, + account = nil, password = nil, + isapop = false, &block) # :yield: pop new(address, port, isapop).start(account, password, &block) end - + # Creates a new POP3 object. # # +address+ is the hostname or ip address of your POP3 server. # - # The optional +port+ is the port to connect to; it defaults to 110. + # The optional +port+ is the port to connect to. # # The optional +isapop+ specifies whether this connection is going # to use APOP authentication; it defaults to +false+. # # This method does *not* open the TCP connection. - def initialize( addr, port = nil, isapop = false ) + def initialize(addr, port = nil, isapop = false) @address = addr - @port = port || self.class.default_port + @ssl_params = POP3.ssl_params + @port = port @apop = isapop - + @command = nil @socket = nil @started = false @@ -361,6 +430,32 @@ module Net @apop end + # does this instance use SSL? + def use_ssl? + return !@ssl_params.nil? + end + + # call-seq: + # Net::POP#enable_ssl(params = {}) + # + # Enables SSL for this instance. Must be called before the connection is + # established to have any effect. + # +params[:port]+ is port to establish the SSL connection on; Defaults to 995. + # +params+ (except :port) is passed to OpenSSL::SSLContext#set_params. + def enable_ssl(verify_or_params = {}, certs = nil, port = nil) + begin + @ssl_params = verify_or_params.to_hash.dup + @port = @ssl_params.delete(:port) || @port + rescue NoMethodError + @ssl_params = POP3.create_ssl_params(verify_or_params, certs) + @port = port || @port + end + end + + def disable_ssl + @ssl_params = nil + end + # Provide human-readable stringification of class state. def inspect "#<#{self.class} #{@address}:#{@port} open=#{@started}>" @@ -379,7 +474,7 @@ module Net # .... # end # - def set_debug_output( arg ) + def set_debug_output(arg) @debug_output = arg end @@ -387,7 +482,9 @@ module Net attr_reader :address # The port number to connect to. - attr_reader :port + def port + return @port || (use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port) + end # Seconds to wait until a connection is opened. # If the POP3 object cannot open a connection within this time, @@ -400,7 +497,7 @@ module Net attr_reader :read_timeout # Set the read timeout. - def read_timeout=( sec ) + def read_timeout=(sec) @command.socket.read_timeout = sec if @command @read_timeout = sec end @@ -418,9 +515,8 @@ module Net # closes the session after block call finishes. # # This method raises a POPAuthenticationError if authentication fails. - def start( account, password ) # :yield: pop + def start(account, password) # :yield: pop raise IOError, 'POP session already started' if @started - if block_given? begin do_start account, password @@ -434,9 +530,23 @@ module Net end end - def do_start( account, password ) - @socket = self.class.socket_type.old_open(@address, @port, - @open_timeout, @read_timeout, @debug_output) + def do_start(account, password) + s = timeout(@open_timeout) { TCPSocket.open(@address, port) } + if use_ssl? + raise 'openssl library not installed' unless defined?(OpenSSL) + context = OpenSSL::SSL::SSLContext.new + context.set_params(@ssl_params) + s = OpenSSL::SSL::SSLSocket.new(s, context) + s.sync_close = true + s.connect + if context.verify_mode != OpenSSL::SSL::VERIFY_NONE + s.post_connection_check(@address) + end + end + @socket = InternetMessageIO.new(s) + logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})" + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output on_connect @command = POP3Command.new(@socket) if apop? @@ -446,7 +556,12 @@ module Net end @started = true ensure - do_finish if not @started + # Authentication failed, clean up connection. + unless @started + s.close if s and not s.closed? + @socket = nil + @command = nil + end end private :do_start @@ -526,7 +641,7 @@ module Net # end # # This method raises a POPError if an error occurs. - def each_mail( &block ) # :yield: message + def each_mail(&block) # :yield: message mails().each(&block) end @@ -573,6 +688,10 @@ module Net end end + def logging(msg) + @debug_output << msg + "\n" if @debug_output + end + end # class POP3 # class aliases @@ -600,7 +719,7 @@ module Net # class POPMail - def initialize( num, len, pop, cmd ) #:nodoc: + def initialize(num, len, pop, cmd) #:nodoc: @number = num @length = len @pop = pop @@ -679,7 +798,7 @@ module Net # The optional +dest+ argument is obsolete. # # This method raises a POPError if an error occurs. - def top( lines, dest = '' ) + def top(lines, dest = '') @command.top(@number, lines) do |chunk| dest << chunk end @@ -691,7 +810,7 @@ module Net # The optional +dest+ argument is obsolete. # # This method raises a POPError if an error occurs. - def header( dest = '' ) + def header(dest = '') top(0, dest) end @@ -739,7 +858,7 @@ module Net alias uidl unique_id - def uid=( uid ) #:nodoc: internal use only (used from POP3#set_all_uids) + def uid=(uid) #:nodoc: internal use only @uid = uid end @@ -748,7 +867,7 @@ module Net class POP3Command #:nodoc: internal use only - def initialize( sock ) + def initialize(sock) @socket = sock @error_occured = false res = check_response(critical { recv_response() }) @@ -759,14 +878,14 @@ module Net "#<#{self.class} socket=#{@socket}>" end - def auth( account, password ) + def auth(account, password) check_response_auth(critical { check_response_auth(get_response('USER %s', account)) get_response('PASS %s', password) }) end - def apop( account, password ) + def apop(account, password) raise POPAuthenticationError, 'not APOP server; cannot login' \ unless @apop_stamp check_response_auth(critical { @@ -797,28 +916,28 @@ module Net end def rset - check_response(critical { get_response 'RSET' }) + check_response(critical { get_response('RSET') }) end - def top( num, lines = 0, &block ) + def top(num, lines = 0, &block) critical { getok('TOP %d %d', num, lines) @socket.each_message_chunk(&block) } end - def retr( num, &block ) + def retr(num, &block) critical { getok('RETR %d', num) @socket.each_message_chunk(&block) } end - def dele( num ) + def dele(num) check_response(critical { get_response('DELE %d', num) }) end - def uidl( num = nil ) + def uidl(num = nil) if num res = check_response(critical { get_response('UIDL %d', num) }) return res.split(/ /)[1] @@ -841,12 +960,12 @@ module Net private - def getok( fmt, *fargs ) + def getok(fmt, *fargs) @socket.writeline sprintf(fmt, *fargs) check_response(recv_response()) end - def get_response( fmt, *fargs ) + def get_response(fmt, *fargs) @socket.writeline sprintf(fmt, *fargs) recv_response() end @@ -855,13 +974,13 @@ module Net @socket.readline end - def check_response( res ) - raise POPError, res unless /\A\+OK/i === res + def check_response(res) + raise POPError, res unless /\A\+OK/i =~ res res end - def check_response_auth( res ) - raise POPAuthenticationError, res unless /\A\+OK/i === res + def check_response_auth(res) + raise POPAuthenticationError, res unless /\A\+OK/i =~ res res end @@ -878,4 +997,3 @@ module Net end # class POP3Command end # module Net - diff --git a/test/gdbm/test_gdbm.rb b/test/gdbm/test_gdbm.rb index b84221e547..5b25497211 100644 --- a/test/gdbm/test_gdbm.rb +++ b/test/gdbm/test_gdbm.rb @@ -90,8 +90,8 @@ if defined? GDBM end end def test_s_open_no_create - # this test is failed on libgdbm 1.8.0 - assert_nil(gdbm = GDBM.open("tmptest_gdbm", nil)) + assert_nil(gdbm = GDBM.open("tmptest_gdbm", nil), + "this test is failed on libgdbm 1.8.0") ensure gdbm.close if gdbm end diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index ec1bf6b8c8..82caa26cff 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -6,14 +6,18 @@ end require "rbconfig" require "socket" require "test/unit" +begin + loadpath = $:.dup + $:.replace($: | [File.expand_path("../ruby", File.dirname(__FILE__))]) + require 'envutil' +ensure + $:.replace(loadpath) +end if defined?(OpenSSL) class OpenSSL::TestSSL < Test::Unit::TestCase - RUBY = ENV["RUBY"] || File.join( - ::Config::CONFIG["bindir"], - ::Config::CONFIG["ruby_install_name"] + ::Config::CONFIG["EXEEXT"] - ) + RUBY = EnvUtil.rubybin SSL_SERVER = File.join(File.dirname(__FILE__), "ssl_server.rb") PORT = 20443 ITERATIONS = ($0 == __FILE__) ? 100 : 10 @@ -54,28 +58,84 @@ class OpenSSL::TestSSL < Test::Unit::TestCase OpenSSL::TestUtils.issue_crl(*arg) end - def start_server(port0, verify_mode, start_immediately, &block) - server = nil + def readwrite_loop(ctx, ssl) + while line = ssl.gets + if line =~ /^STARTTLS$/ + ssl.accept + next + end + ssl.write(line) + end + rescue OpenSSL::SSL::SSLError + rescue IOError + ensure + ssl.close rescue nil + end + + def server_loop(ctx, ssls, server_proc) + loop do + ssl = nil + begin + ssl = ssls.accept + rescue OpenSSL::SSL::SSLError + retry + end + + Thread.start do + Thread.current.abort_on_exception = true + server_proc.call(ctx, ssl) + end + end + rescue Errno::EBADF, IOError + end + + def start_server(port0, verify_mode, start_immediately, args = {}, &block) + ctx_proc = args[:ctx_proc] + server_proc = args[:server_proc] + server_proc ||= method(:readwrite_loop) + + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + ctx = OpenSSL::SSL::SSLContext.new + ctx.cert_store = store + #ctx.extra_chain_cert = [ ca_cert ] + ctx.cert = @svr_cert + ctx.key = @svr_key + ctx.verify_mode = verify_mode + ctx_proc.call(ctx) if ctx_proc + + Socket.do_not_reverse_lookup = true + tcps = nil + port = port0 begin - cmd = [RUBY] - cmd << "-d" if $DEBUG - cmd << SSL_SERVER << port0.to_s << verify_mode.to_s - cmd << (start_immediately ? "yes" : "no") - server = IO.popen(cmd.join(" "), "w+") - server.write(@ca_cert.to_pem) - server.write(@svr_cert.to_pem) - server.write(@svr_key.to_pem) - pid = Integer(server.gets) - if port = server.gets - if $DEBUG - $stderr.printf("%s started: pid=%d port=%d\n", SSL_SERVER, pid, port) - end - block.call(server, port.to_i) + tcps = TCPServer.new("127.0.0.1", port) + rescue Errno::EADDRINUSE + port += 1 + retry + end + + ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) + ssls.start_immediately = start_immediately + + begin + server = Thread.new do + Thread.current.abort_on_exception = true + server_loop(ctx, ssls, server_proc) end + + $stderr.printf("%s started: pid=%d port=%d\n", SSL_SERVER, pid, port) if $DEBUG + + block.call(server, port.to_i) ensure - if server - Process.kill(:KILL, pid) - server.close + tcps.close if (tcps) + if (server) + server.join(5) + if server.alive? + server.kill + server.join + flunk("TCPServer was closed and SSLServer is still alive") unless $! + end end end end @@ -89,16 +149,22 @@ class OpenSSL::TestSSL < Test::Unit::TestCase ssl.connect end + def test_ctx_setup + ctx = OpenSSL::SSL::SSLContext.new + assert_equal(ctx.setup, true) + assert_equal(ctx.setup, nil) + end + def test_connect_and_close - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) assert(ssl.connect) ssl.close assert(!sock.closed?) sock.close - sock = TCPSocket.new("127.0.0.1", p) + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true # !! assert(ssl.connect) @@ -108,8 +174,8 @@ class OpenSSL::TestSSL < Test::Unit::TestCase end def test_read_and_write - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true ssl.connect @@ -127,13 +193,6 @@ class OpenSSL::TestSSL < Test::Unit::TestCase assert_equal(str, buf) } - # puts and gets - ITERATIONS.times{ - str = "x" * 100 + "\n" - ssl.puts(str) - assert_equal(str, ssl.gets) - } - # read and write ITERATIONS.times{|i| str = "x" * 100 + "\n" @@ -153,9 +212,9 @@ class OpenSSL::TestSSL < Test::Unit::TestCase def test_client_auth vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT - start_server(PORT, vflag, true){|s, p| + start_server(PORT, vflag, true){|server, port| assert_raises(OpenSSL::SSL::SSLError){ - sock = TCPSocket.new("127.0.0.1", p) + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect } @@ -163,7 +222,7 @@ class OpenSSL::TestSSL < Test::Unit::TestCase ctx = OpenSSL::SSL::SSLContext.new ctx.key = @cli_key ctx.cert = @cli_cert - sock = TCPSocket.new("127.0.0.1", p) + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true ssl.connect @@ -173,11 +232,11 @@ class OpenSSL::TestSSL < Test::Unit::TestCase called = nil ctx = OpenSSL::SSL::SSLContext.new - ctx.client_cert_cb = Proc.new{|ssl| + ctx.client_cert_cb = Proc.new{ |sslconn| called = true [@cli_cert, @cli_key] } - sock = TCPSocket.new("127.0.0.1", p) + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true ssl.connect @@ -189,8 +248,8 @@ class OpenSSL::TestSSL < Test::Unit::TestCase end def test_starttls - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, false){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, false){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true str = "x" * 1000 + "\n" @@ -213,10 +272,10 @@ class OpenSSL::TestSSL < Test::Unit::TestCase def test_parallel GC.start - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| ssls = [] 10.times{ - sock = TCPSocket.new("127.0.0.1", p) + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect ssl.sync_close = true @@ -233,17 +292,76 @@ class OpenSSL::TestSSL < Test::Unit::TestCase } end + def test_verify_result + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result) + + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params( + :verify_callback => Proc.new do |preverify_ok, store_ctx| + store_ctx.error = OpenSSL::X509::V_OK + true + end + ) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.connect + assert_equal(OpenSSL::X509::V_OK, ssl.verify_result) + + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params( + :verify_callback => Proc.new do |preverify_ok, store_ctx| + store_ctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION + false + end + ) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, ssl.verify_result) + } + end + + def test_sslctx_set_params + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params + assert_equal(OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode) + assert_equal(OpenSSL::SSL::OP_ALL, ctx.options) + ciphers = ctx.ciphers + ciphers_versions = ciphers.collect{|_, v, _, _| v } + ciphers_names = ciphers.collect{|v, _, _, _| v } + assert(ciphers_names.all?{|v| /ADH/ !~ v }) + assert(ciphers_versions.all?{|v| /SSLv2/ !~ v }) + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } + assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result) + } + end + def test_post_connection_check sslerr = OpenSSL::SSL::SSLError - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect assert_raises(sslerr){ssl.post_connection_check("localhost.localdomain")} assert_raises(sslerr){ssl.post_connection_check("127.0.0.1")} assert(ssl.post_connection_check("localhost")) assert_raises(sslerr){ssl.post_connection_check("foo.example.com")} + + cert = ssl.peer_cert + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } now = Time.now @@ -254,14 +372,20 @@ class OpenSSL::TestSSL < Test::Unit::TestCase ] @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect assert(ssl.post_connection_check("localhost.localdomain")) assert(ssl.post_connection_check("127.0.0.1")) assert_raises(sslerr){ssl.post_connection_check("localhost")} assert_raises(sslerr){ssl.post_connection_check("foo.example.com")} + + cert = ssl.peer_cert + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } now = Time.now @@ -271,16 +395,135 @@ class OpenSSL::TestSSL < Test::Unit::TestCase ] @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) - start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|s, p| - sock = TCPSocket.new("127.0.0.1", p) + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port| + sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect assert(ssl.post_connection_check("localhost.localdomain")) assert_raises(sslerr){ssl.post_connection_check("127.0.0.1")} assert_raises(sslerr){ssl.post_connection_check("localhost")} assert_raises(sslerr){ssl.post_connection_check("foo.example.com")} + cert = ssl.peer_cert + assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) + assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } end + + def test_client_session + last_session = nil + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port| + 2.times do + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.session = last_session if last_session + ssl.connect + + session = ssl.session + if last_session + assert(ssl.session_reused?) + + if session.respond_to?(:id) + assert_equal(session.id, last_session.id) + end + assert_equal(session.to_pem, last_session.to_pem) + assert_equal(session.to_der, last_session.to_der) + # Older version of OpenSSL may not be consistent. Look up which versions later. + assert_equal(session.to_text, last_session.to_text) + else + assert(!ssl.session_reused?) + end + last_session = session + + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + + ssl.close + end + end + end + + def test_server_session + connections = 0 + saved_session = nil + + ctx_proc = Proc.new do |ctx, ssl| +# add test for session callbacks here + end + + server_proc = Proc.new do |ctx, ssl| + session = ssl.session + stats = ctx.session_cache_stats + + case connections + when 0 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 0) + assert_equal(stats[:cache_misses], 0) + assert(!ssl.session_reused?) + when 1 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 1) + assert_equal(stats[:cache_misses], 0) + assert(ssl.session_reused?) + ctx.session_remove(session) + saved_session = session + when 2 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 1) + assert_equal(stats[:cache_misses], 1) + assert(!ssl.session_reused?) + ctx.session_add(saved_session) + when 3 + assert_equal(stats[:cache_num], 2) + assert_equal(stats[:cache_hits], 2) + assert_equal(stats[:cache_misses], 1) + assert(ssl.session_reused?) + ctx.flush_sessions(Time.now + 5000) + when 4 + assert_equal(stats[:cache_num], 1) + assert_equal(stats[:cache_hits], 2) + assert_equal(stats[:cache_misses], 2) + assert(!ssl.session_reused?) + ctx.session_add(saved_session) + end + connections += 1 + + readwrite_loop(ctx, ssl) + end + + first_session = nil + start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port| + 10.times do |i| + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.session = first_session if first_session + ssl.connect + + session = ssl.session + if first_session + case i + when 1; assert(ssl.session_reused?) + when 2; assert(!ssl.session_reused?) + when 3; assert(ssl.session_reused?) + when 4; assert(!ssl.session_reused?) + when 5..10; assert(ssl.session_reused?) + end + end + first_session ||= session + + str = "x" * 100 + "\n" + ssl.puts(str) + assert_equal(str, ssl.gets) + + ssl.close + end + end + end end end diff --git a/version.h b/version.h index f51e4832f8..39e52831aa 100644 --- a/version.h +++ b/version.h @@ -1,7 +1,7 @@ #define RUBY_VERSION "1.8.7" -#define RUBY_RELEASE_DATE "2008-04-23" +#define RUBY_RELEASE_DATE "2008-04-25" #define RUBY_VERSION_CODE 187 -#define RUBY_RELEASE_CODE 20080423 +#define RUBY_RELEASE_CODE 20080425 #define RUBY_PATCHLEVEL 0 #define RUBY_VERSION_MAJOR 1 @@ -9,7 +9,7 @@ #define RUBY_VERSION_TEENY 7 #define RUBY_RELEASE_YEAR 2008 #define RUBY_RELEASE_MONTH 4 -#define RUBY_RELEASE_DAY 23 +#define RUBY_RELEASE_DAY 25 #ifdef RUBY_EXTERN RUBY_EXTERN const char ruby_version[]; -- cgit v1.2.3