[前][次][番号順一覧][スレッド一覧]

ruby-changes:4690

From: ko1@a...
Date: Thu, 24 Apr 2008 23:52:42 +0900 (JST)
Subject: [ruby-changes:4690] kazu - Ruby:r16184 (ruby_1_8): * lib/net/pop.rb: backported from 1.9. bug#19003

kazu	2008-04-24 23:52:21 +0900 (Thu, 24 Apr 2008)

  New Revision: 16184

  Modified files:
    branches/ruby_1_8/ChangeLog
    branches/ruby_1_8/ext/openssl/lib/openssl/ssl.rb
    branches/ruby_1_8/ext/openssl/ossl_ssl.c
    branches/ruby_1_8/lib/net/pop.rb
    branches/ruby_1_8/test/openssl/test_ssl.rb

  Log:
    * 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.


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/test/openssl/test_ssl.rb?r1=16184&r2=16183&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/lib/net/pop.rb?r1=16184&r2=16183&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ChangeLog?r1=16184&r2=16183&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ext/openssl/lib/openssl/ssl.rb?r1=16184&r2=16183&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ext/openssl/ossl_ssl.c?r1=16184&r2=16183&diff_format=u

Index: ruby_1_8/ext/openssl/ossl_ssl.c
===================================================================
--- ruby_1_8/ext/openssl/ossl_ssl.c	(revision 16183)
+++ ruby_1_8/ext/openssl/ossl_ssl.c	(revision 16184)
@@ -141,31 +141,14 @@
     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;
+    const char *s;
     int i;
-    const char *s;
 
-    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_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 @@
     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 @@
     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,7 +1318,20 @@
     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 @@
      *
      * 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 @@
     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))
 
Index: ruby_1_8/ext/openssl/lib/openssl/ssl.rb
===================================================================
--- ruby_1_8/ext/openssl/lib/openssl/ssl.rb	(revision 16183)
+++ ruby_1_8/ext/openssl/lib/openssl/ssl.rb	(revision 16184)
@@ -20,6 +20,33 @@
 
 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 @@
       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 @@
         @svr.listen(backlog)
       end
 
+      def shutdown(how=Socket::SHUT_RDWR)
+        @svr.shutdown(how)
+      end
+
       def accept
         sock = @svr.accept
         begin
Index: ruby_1_8/ChangeLog
===================================================================
--- ruby_1_8/ChangeLog	(revision 16183)
+++ ruby_1_8/ChangeLog	(revision 16184)
@@ -1,3 +1,14 @@
+Thu Apr 24 23:47:50 2008  Kazuhiro NISHIYAMA  <zn@m...>
+
+	* 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@r...>
 
 	* eval.c (THREAD_SAVE_CONTEXT): remove unnecessary
Index: ruby_1_8/lib/net/pop.rb
===================================================================
--- ruby_1_8/lib/net/pop.rb	(revision 16183)
+++ ruby_1_8/lib/net/pop.rb	(revision 16184)
@@ -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@l...>.
 #
@@ -10,19 +10,25 @@
 # 
 # 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
 
   # Non-authentication POP3 protocol error
@@ -196,10 +202,19 @@
     # Class Parameters
     #
 
+    def POP3.default_port
+      default_pop3_port()
+    end
+
     # The default port for POP3 connections, port 110
-    def POP3.default_port
+    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 @@
     #       ....
     #     end
     #
-    def POP3.APOP( isapop )
+    def POP3.APOP(isapop)
       isapop ? APOP : POP3
     end
 
@@ -244,9 +259,9 @@
     #       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 @@
     #       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,16 +302,16 @@
     #     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) {
         ;
@@ -304,6 +319,59 @@
     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 @@
     #      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 @@
       @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 @@
     #     ....
     #   end
     #
-    def set_debug_output( arg )
+    def set_debug_output(arg)
       @debug_output = arg
     end
 
@@ -387,7 +482,9 @@
     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 @@
     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 @@
     # 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 @@
       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 @@
       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 @@
     #   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 @@
       end
     end
 
+    def logging(msg)
+      @debug_output << msg + "\n" if @debug_output
+    end
+
   end   # class POP3
 
   # class aliases
@@ -600,7 +719,7 @@
   #
   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 @@
     # 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 @@
     # 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 @@
 
     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 @@
 
   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 @@
       "#<#{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 @@
     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 @@
 
     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 @@
       @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 @@
   end   # class POP3Command
 
 end   # module Net
-
Index: ruby_1_8/test/openssl/test_ssl.rb
===================================================================
--- ruby_1_8/test/openssl/test_ssl.rb	(revision 16183)
+++ ruby_1_8/test/openssl/test_ssl.rb	(revision 16184)
@@ -6,14 +6,18 @@
 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 @@
     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 @@
     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 @@
   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 @@
         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 @@
 
   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 @@
       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 @@
 
       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 @@
   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 @@
 
   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 @@
     }
   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 @@
     ]
     @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 @@
     ]
     @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

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]