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

ruby-changes:10217

From: technorama <ko1@a...>
Date: Sun, 25 Jan 2009 06:47:49 +0900 (JST)
Subject: [ruby-changes:10217] Ruby:r21761 (trunk): * ext/openssl/ossl_ssl.c: Server Name Indication support.

technorama	2009-01-25 06:45:42 +0900 (Sun, 25 Jan 2009)

  New Revision: 21761

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=21761

  Log:
    * ext/openssl/ossl_ssl.c: Server Name Indication support.
      new methods SSLContext#server_name_cb=, SSLSocket#hostname=.
    * test/openssl/test_ssl.rb: Tests for above.

  Modified files:
    trunk/ChangeLog
    trunk/ext/openssl/extconf.rb
    trunk/ext/openssl/ossl_ssl.c
    trunk/test/openssl/test_ssl.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 21760)
+++ ChangeLog	(revision 21761)
@@ -1,3 +1,10 @@
+Sun Jan 25 06:44:58 2009  Technorama Ltd.  <oss-ruby@t...>
+
+	* ext/openssl/ossl_ssl.c: Server Name Indication support.
+	  new methods SSLContext#server_name_cb=, SSLSocket#hostname=.
+
+	* test/openssl/test_ssl.rb: Tests for above.
+
 Sat Jan 24 08:22:35 2009  Nobuyoshi Nakada  <nobu@r...>
 
 	* lib/mkmf.rb (configuration): tools under the top source
Index: ext/openssl/ossl_ssl.c
===================================================================
--- ext/openssl/ossl_ssl.c	(revision 21760)
+++ ext/openssl/ossl_ssl.c	(revision 21761)
@@ -67,6 +67,9 @@
     "verify_callback", "options", "cert_store", "extra_chain_cert",
     "client_cert_cb", "tmp_dh_callback", "session_id_context",
     "session_get_cb", "session_new_cb", "session_remove_cb",
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+    "servername_cb",
+#endif
 };
 
 #define ossl_ssl_get_io(o)           rb_iv_get((o),"@io")
@@ -84,7 +87,12 @@
 #define ossl_ssl_set_tmp_dh(o,v)     rb_iv_set((o),"@tmp_dh",(v))
 
 static const char *ossl_ssl_attr_readers[] = { "io", "context", };
-static const char *ossl_ssl_attrs[] = { "sync_close", };
+static const char *ossl_ssl_attrs[] = {
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+    "hostname",
+#endif
+    "sync_close", 
+};
 
 ID ID_callback_state;
 
@@ -446,6 +454,66 @@
     return i;
 }
 
+static VALUE ossl_sslctx_setup(VALUE self);
+
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+static VALUE
+ossl_call_servername_cb(VALUE ary)
+{
+    VALUE ssl_obj, sslctx_obj, cb, ret_obj;
+
+    Check_Type(ary, T_ARRAY);
+    ssl_obj = rb_ary_entry(ary, 0);
+
+    sslctx_obj = rb_iv_get(ssl_obj, "@context");
+    if (NIL_P(sslctx_obj)) return Qnil;
+    cb = rb_iv_get(sslctx_obj, "@servername_cb");
+    if (NIL_P(cb)) return Qnil;
+
+    ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary);
+    if (rb_obj_is_kind_of(ret_obj, cSSLContext)) {
+        SSL *ssl;
+        SSL_CTX *ctx2;
+
+        ossl_sslctx_setup(ret_obj);
+        Data_Get_Struct(ssl_obj, SSL, ssl);
+        Data_Get_Struct(ret_obj, SSL_CTX, ctx2);
+        SSL_set_SSL_CTX(ssl, ctx2);
+    } else if (!NIL_P(ret_obj)) {
+            rb_raise(rb_eArgError, "servername_cb must return an OpenSSL::SSL::SSLContext object or nil");
+    }
+
+    return ret_obj;
+}
+
+static int
+ssl_servername_cb(SSL *ssl, int *ad, void *arg)
+{
+    VALUE ary, ssl_obj, ret_obj;
+    void *ptr;
+    int state = 0;
+    const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+
+    if (!servername)
+        return SSL_TLSEXT_ERR_OK;
+
+    if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL)
+    	return SSL_TLSEXT_ERR_ALERT_FATAL;
+    ssl_obj = (VALUE)ptr;
+    ary = rb_ary_new2(2);
+    rb_ary_push(ary, ssl_obj);
+    rb_ary_push(ary, rb_str_new2(servername));
+
+    ret_obj = rb_protect((VALUE(*)_((VALUE)))ossl_call_servername_cb, ary, &state);
+    if (state) {
+        rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
+    return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
 /*
  * call-seq:
  *    ctx.setup => Qtrue # first time
@@ -581,6 +649,15 @@
 	SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
 	OSSL_Debug("SSL SESSION remove callback added");
     }
+
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+    val = rb_iv_get(self, "@servername_cb");
+    if (!NIL_P(val)) {
+        SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
+	OSSL_Debug("SSL TLSEXT servername callback added");
+    }
+#endif
+
     return Qtrue;
 }
 
@@ -901,6 +978,10 @@
 
     Data_Get_Struct(self, SSL, ssl);
     if(!ssl){
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+	VALUE hostname = rb_iv_get(self, "@hostname");
+#endif
+
         v_ctx = ossl_ssl_get_ctx(self);
         Data_Get_Struct(v_ctx, SSL_CTX, ctx);
 
@@ -910,6 +991,12 @@
         }
         DATA_PTR(self) = ssl;
 
+#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
+        if (!NIL_P(hostname)) {
+           if (SSL_set_tlsext_host_name(ssl, StringValuePtr(hostname)) != 1)
+               ossl_raise(eSSLError, "SSL_set_tlsext_host_name:");
+        }
+#endif
         io = ossl_ssl_get_io(self);
         GetOpenFile(io, fptr);
         rb_io_check_readable(fptr);
@@ -946,7 +1033,15 @@
     Data_Get_Struct(self, SSL, ssl);
     GetOpenFile(ossl_ssl_get_io(self), fptr);
     for(;;){
-	if((ret = func(ssl)) > 0) break;
+	ret = func(ssl);
+
+        cb_state = rb_ivar_get(self, ID_callback_state);
+        if (!NIL_P(cb_state))
+            rb_jump_tag(NUM2INT(cb_state));
+
+	if (ret > 0)
+	    break;
+
 	switch((ret2 = ssl_get_error(ssl, ret))){
 	case SSL_ERROR_WANT_WRITE:
             rb_io_wait_writable(FPTR_TO_FD(fptr));
@@ -962,10 +1057,6 @@
 	}
     }
 
-    cb_state = rb_ivar_get(self, ID_callback_state);
-    if (!NIL_P(cb_state))
-        rb_jump_tag(NUM2INT(cb_state));
-
     return self;
 }
 
Index: ext/openssl/extconf.rb
===================================================================
--- ext/openssl/extconf.rb	(revision 21760)
+++ ext/openssl/extconf.rb	(revision 21761)
@@ -96,6 +96,9 @@
 have_func("OBJ_NAME_do_all_sorted")
 have_func("SSL_SESSION_get_id")
 have_func("OPENSSL_cleanse")
+unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h'])
+	have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME")
+end
 if try_compile("#define FOO(a, ...) foo(a, ##__VA_ARGS__)\n int x(){FOO(1);FOO(1,2);FOO(1,2,3);}\n")
   $defs.push("-DHAVE_VA_ARGS_MACRO")
 end
Index: test/openssl/test_ssl.rb
===================================================================
--- test/openssl/test_ssl.rb	(revision 21760)
+++ test/openssl/test_ssl.rb	(revision 21761)
@@ -570,6 +570,50 @@
       end
     end
   end
+
+  def test_tlsext_hostname
+    return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname)
+
+    ctx_proc = Proc.new do |ctx, ssl|
+      foo_ctx = ctx.dup
+
+      ctx.servername_cb = Proc.new do |ssl, hostname|
+        case hostname
+        when 'foo.example.com'
+          foo_ctx
+        when 'bar.example.com'
+          nil
+        else
+          raise "unknown hostname #{hostname.inspect}"
+        end
+      end
+    end
+
+    server_proc = Proc.new do |ctx, ssl|
+      readwrite_loop(ctx, ssl)
+    end
+
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+      2.times do |i|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ctx = OpenSSL::SSL::SSLContext.new
+        if defined?(OpenSSL::SSL::OP_NO_TICKET)
+          # disable RFC4507 support
+          ctx.options = OpenSSL::SSL::OP_NO_TICKET
+        end
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.sync_close = true
+        ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com'
+        ssl.connect
+
+        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/

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