From c630c1f79553db8f36b528d324e930216b2800f8 Mon Sep 17 00:00:00 2001
From: Kazuki Yamaguchi <k@rhe.jp>
Date: Wed, 16 Mar 2016 23:58:02 +0900
Subject: [PATCH] openssl: fix build when NPN is disabled by OpenSSL

* ext/openssl/extconf.rb: check SSL_CTX_set_next_proto_select_cb
  function rather than OPENSSL_NPN_NEGOTIATED macro. it exists even if
  it is disabled by OpenSSL configuration.
* ext/openssl/ossl_ssl.c: update #ifdef(s) as above.
* test/openssl/test_ssl.rb: skip NPN tests if NPN is disabled.
---
 ext/openssl/extconf.rb   |  2 +-
 ext/openssl/ossl_ssl.c   | 39 ++++++++++++++++++++-------------------
 test/openssl/test_ssl.rb |  4 +++-
 3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 7bb6cd8..0b7fa2a 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -110,7 +110,7 @@
 have_func("TLSv1_2_server_method")
 have_func("TLSv1_2_client_method")
 have_func("SSL_CTX_set_alpn_select_cb")
-have_macro("OPENSSL_NPN_NEGOTIATED", ['openssl/ssl.h']) && $defs.push("-DHAVE_OPENSSL_NPN_NEGOTIATED")
+have_func("SSL_CTX_set_next_proto_select_cb")
 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 90ce762..0e01d7c 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -544,7 +544,7 @@ ssl_renegotiation_cb(const SSL *ssl)
     (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj);
 }
 
-#ifdef HAVE_OPENSSL_NPN_NEGOTIATED
+#if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
 static VALUE
 ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded)
 {
@@ -569,18 +569,6 @@ ssl_encode_npn_protocols(VALUE protocols)
 }
 
 static int
-ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
-{
-    VALUE sslctx_obj = (VALUE) arg;
-    VALUE protocols = rb_iv_get(sslctx_obj, "@_protocols");
-
-    *out = (const unsigned char *) RSTRING_PTR(protocols);
-    *outlen = RSTRING_LENINT(protocols);
-
-    return SSL_TLSEXT_ERR_OK;
-}
-
-static int
 ssl_npn_select_cb_common(VALUE cb, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen)
 {
     VALUE selected;
@@ -609,6 +597,19 @@ ssl_npn_select_cb_common(VALUE cb, const unsigned char **out, unsigned char *out
     return SSL_TLSEXT_ERR_OK;
 }
 
+#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
+static int
+ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
+{
+    VALUE sslctx_obj = (VALUE) arg;
+    VALUE protocols = rb_iv_get(sslctx_obj, "@_protocols");
+
+    *out = (const unsigned char *) RSTRING_PTR(protocols);
+    *outlen = RSTRING_LENINT(protocols);
+
+    return SSL_TLSEXT_ERR_OK;
+}
+
 static int
 ssl_npn_select_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
 {
@@ -619,6 +620,7 @@ ssl_npn_select_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsi
 
     return ssl_npn_select_cb_common(cb, (const unsigned char **)out, outlen, in, inlen);
 }
+#endif
 
 #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
 static int
@@ -632,8 +634,7 @@ ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, c
     return ssl_npn_select_cb_common(cb, out, outlen, in, inlen);
 }
 #endif
-
-#endif
+#endif /* HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB || HAVE_SSL_CTX_SET_ALPN_SELECT_CB */
 
 /* This function may serve as the entry point to support further
  * callbacks. */
@@ -793,7 +794,7 @@ ossl_sslctx_setup(VALUE self)
     val = ossl_sslctx_get_verify_dep(self);
     if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
 
-#ifdef HAVE_OPENSSL_NPN_NEGOTIATED
+#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
     val = rb_iv_get(self, "@npn_protocols");
     if (!NIL_P(val)) {
 	rb_iv_set(self, "@_protocols", ssl_encode_npn_protocols(val));
@@ -1861,7 +1862,7 @@ ossl_ssl_get_client_ca_list(VALUE self)
     return ossl_x509name_sk2ary(ca);
 }
 
-# ifdef HAVE_OPENSSL_NPN_NEGOTIATED
+# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
 /*
  * call-seq:
  *    ssl.npn_protocol => String
@@ -2132,7 +2133,7 @@ Init_ossl_ssl(void)
      *   end
      */
     rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse);
-#ifdef HAVE_OPENSSL_NPN_NEGOTIATED
+#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
     /*
      * An Enumerable of Strings. Each String represents a protocol to be
      * advertised as the list of supported protocols for Next Protocol
@@ -2307,7 +2308,7 @@ Init_ossl_ssl(void)
 # ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
     rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
 # endif
-# ifdef HAVE_OPENSSL_NPN_NEGOTIATED
+# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB
     rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
 # endif
 #endif
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 0f74085..f0bedbc 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -1064,7 +1064,9 @@ def test_alpn_protocol_selection_cancel
   end
 end
 
-if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 &&
+	OpenSSL::SSL::SSLContext.instance_methods.include?(:npn_select_cb)
+  # NPN may be disabled by OpenSSL configure option
 
   def test_npn_protocol_selection_ary
     advertised = ["http/1.1", "spdy/2"]
-- 
2.7.3

