mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@16184 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
0007c24a5c
commit
545c0d6aa3
5 changed files with 610 additions and 164 deletions
11
ChangeLog
11
ChangeLog
|
|
@ -1,3 +1,14 @@
|
|||
Thu Apr 24 23:47:50 2008 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
|
||||
|
||||
* 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 <matz@ruby-lang.org>
|
||||
|
||||
* eval.c (THREAD_SAVE_CONTEXT): remove unnecessary
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
228
lib/net/pop.rb
228
lib/net/pop.rb
|
|
@ -1,8 +1,8 @@
|
|||
# = net/pop.rb
|
||||
#
|
||||
# Copyright (c) 1999-2003 Yukihiro Matsumoto.
|
||||
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
|
||||
#
|
||||
# Copyright (c) 1999-2003 Minero Aoki.
|
||||
# Copyright (c) 1999-2007 Minero Aoki.
|
||||
#
|
||||
# Written & maintained by Minero Aoki <aamine@loveruby.net>.
|
||||
#
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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)
|
||||
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
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue