From 060184c347822b11dff3db6bef915c04a564c4e4 Mon Sep 17 00:00:00 2001
From: emboss <emboss@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>
Date: Sun, 6 May 2012 22:26:08 +0000
Subject: [PATCH] * ext/openssl/ossl_ssl.c: support TLSv1.1 & TLSv1.1. Add  
 SSLContext#version to inspect the version that was negotiated for   a given
 connection. * ext/openssl/extconf.rb: detect TLS 1.1 & 1.2 support. *
 test/openssl/test_ssl.rb: add tests for TLS 1.1 & 1.2 given they   are
 supported by the native OpenSSL being used.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35549 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
---
 ChangeLog                |  9 +++++++++
 ext/openssl/extconf.rb   |  6 ++++++
 ext/openssl/ossl_ssl.c   | 43 +++++++++++++++++++++++++++++++++++-----
 test/openssl/test_ssl.rb | 29 +++++++++++++++++++++++++++
 4 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1351c8b768..d765423347 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Mon May 07 09:14:11 2012  Martin Bosslet  <Martin.Bosslet@googlemail.com>
+
+	* ext/openssl/ossl_ssl.c: support TLSv1.1 & TLSv1.1. Add 
+	  SSLContext#version to inspect the version that was negotiated for
+	  a given connection.
+	* ext/openssl/extconf.rb: detect TLS 1.1 & 1.2 support.
+	* test/openssl/test_ssl.rb: add tests for TLS 1.1 & 1.2 given they
+	  are supported by the native OpenSSL being used.
+
 Sun May  6 21:34:29 2012  NARUSE, Yui  <naruse@ruby-lang.org>
 
 	* io.c (io_encoding_set): suppress warnings. [ruby-dev:45627]
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 640348f64a..ff27fad327 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -103,6 +103,12 @@ have_func("OPENSSL_cleanse")
 have_func("SSLv2_method")
 have_func("SSLv2_server_method")
 have_func("SSLv2_client_method")
+have_func("TLSv1_1_method")
+have_func("TLSv1_1_server_method")
+have_func("TLSv1_1_client_method")
+have_func("TLSv1_2_method")
+have_func("TLSv1_2_server_method")
+have_func("TLSv1_2_client_method")
 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
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 79941db1ee..3746f97fa7 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -107,6 +107,18 @@ struct {
     OSSL_SSL_METHOD_ENTRY(TLSv1),
     OSSL_SSL_METHOD_ENTRY(TLSv1_server),
     OSSL_SSL_METHOD_ENTRY(TLSv1_client),
+#if defined(HAVE_TLSV1_2_METHOD) && defined(HAVE_TLSV1_2_SERVER_METHOD) && \
+        defined(HAVE_TLSV1_2_CLIENT_METHOD)
+    OSSL_SSL_METHOD_ENTRY(TLSv1_2),
+    OSSL_SSL_METHOD_ENTRY(TLSv1_2_server),
+    OSSL_SSL_METHOD_ENTRY(TLSv1_2_client),
+#endif
+#if defined(HAVE_TLSV1_1_METHOD) && defined(HAVE_TLSV1_1_SERVER_METHOD) && \
+        defined(HAVE_TLSV1_1_CLIENT_METHOD)
+    OSSL_SSL_METHOD_ENTRY(TLSv1_1),
+    OSSL_SSL_METHOD_ENTRY(TLSv1_1_server),
+    OSSL_SSL_METHOD_ENTRY(TLSv1_1_client),
+#endif
 #if defined(HAVE_SSLV2_METHOD) && defined(HAVE_SSLV2_SERVER_METHOD) && \
         defined(HAVE_SSLV2_CLIENT_METHOD)
     OSSL_SSL_METHOD_ENTRY(SSLv2),
@@ -1505,11 +1517,31 @@ ossl_ssl_get_peer_cert_chain(VALUE self)
 }
 
 /*
- * call-seq:
- *    ssl.cipher => [name, version, bits, alg_bits]
- *
- * The cipher being used for the current connection
- */
+* call-seq:
+*    ssl.version => String
+*
+* Returns a String representing the SSL/TLS version that was negotiated
+* for the connection, for example "TLSv1.2".
+*/
+static VALUE
+ossl_ssl_get_version(VALUE self)
+{
+    SSL *ssl;
+
+    Data_Get_Struct(self, SSL, ssl);
+    if (!ssl) {
+        rb_warning("SSL session is not started yet.");
+        return Qnil;
+    }
+    return rb_str_new2(SSL_get_version(ssl));
+}
+
+/*
+* call-seq:
+*    ssl.cipher => [name, version, bits, alg_bits]
+*
+* The cipher being used for the current connection
+*/
 static VALUE
 ossl_ssl_get_cipher(VALUE self)
 {
@@ -1957,6 +1989,7 @@ Init_ossl_ssl()
     rb_define_method(cSSLSocket, "cert",       ossl_ssl_get_cert, 0);
     rb_define_method(cSSLSocket, "peer_cert",  ossl_ssl_get_peer_cert, 0);
     rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0);
+    rb_define_method(cSSLSocket, "ssl_version",    ossl_ssl_get_version, 0);
     rb_define_method(cSSLSocket, "cipher",     ossl_ssl_get_cipher, 0);
     rb_define_method(cSSLSocket, "state",      ossl_ssl_get_state, 0);
     rb_define_method(cSSLSocket, "pending",    ossl_ssl_pending, 0);
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 033d1e8be5..3ccb3cda87 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -437,6 +437,35 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
       ssl.close
     }
   end
+
+  def test_tls_v_1_1
+    ctx_proc = Proc.new { |ctx|
+      ctx.ssl_version = :TLSv1_1
+    }
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc) { |server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true
+      ssl.connect
+      assert_equal("TLSv1.1", ssl.ssl_version)
+      ssl.close
+    }
+  end if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_1
+
+  def test_tls_v_1_2
+    ctx_proc = Proc.new { |ctx|
+      ctx.ssl_version = :TLSv1_2
+    }
+    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc) { |server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true
+      ssl.connect
+      assert_equal("TLSv1.2", ssl.ssl_version)
+      ssl.close
+    }
+  end if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
+
 end
 
 end