Repository: netty/netty
Issue: Enabling SNI, OpenSSL client from netty does not work 
Labels: defect

Netty version : 4.1 - ff11fe8 also tried with netty-4.1.0.Beta8
When trying to connect to a host with SNI enabled from the netty openssl client, handshake fails as :

```
OpenSSL error: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
```

same issue when you use the openssl s_client :

```
openssl s_client -connect sni-enabled-host:443
140133327337120:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:770:
```

and works well for : 

```
openssl s_client -connect sni-enabled-host:443 -servername sni-enabled-host
```

The following code fails with Openssl and gives the same error but works fine with Openssl disabled (removed netty-tcnative) 

```
SSLEngine sslEngine = sslCtx.newEngine(ctx.channel().alloc(), peerHost,peerPort));
```

```
java -version 
java version "1.8.0_66"

uname -a
Linux linuxbox 4.2.1-040201-generic #201509211431 SMP Mon Sep 21 18:34:44 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

openssl version
OpenSSL 1.0.1f 6 Jan 2014
```


Issue comments:
@jknair I think this is the same issue as :

https://github.com/netty/netty/issues/4722

Looking into it atm.

@jknair can you share a "full" reproducer ?

sorry for the delay, I will try to make one. Problem is I cant find any SNI enabled public host that will fail the handshake 

@jknair maybe there is no need at all. Let me check if I can write one myself

@jknair can you show me a basic client setup that works with JDK version ?

Do you do the same as here:
https://www-01.ibm.com/support/knowledgecenter/#!/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/security-component/jsse2Docs/sni_extension.html
 ?

the following code does **NOT** work with Openssl client 

``` java
SSLEngine sslEngine = sslCtx.newEngine(ctx.channel().alloc(), peerHost, peerPort);
 SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setServerNames(new ArrayList<SNIServerName>(1) {{
     add(new SNIHostName(peerHost));
}});
sslEngine.setSSLParameters(sslParameters);
final SslHandler ssllHandler = new SslHandler(sslEngine);
```

and for the JDK version this works perfectly dont need to add any `SNIServerName` : 

``` java
SSLEngine sslEngine = sslCtx.newEngine(ctx.channel().alloc(), peerHost, peerPort);
final SslHandler ssllHandler = new SslHandler(sslEngine);
```

@jknair and peerHost and peerPort are anything public that I can hit ?

@jknair ok I think I know whats wrong.. working on a fix.

Fixed by https://github.com/netty/netty/pull/4791

sorry for the delay but this worked perfectly. Thanks @normanmaurer 


Pull request review comments:

Patch that resolved the issue:
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
index d70c9525443..0644023ee7b 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
@@ -222,6 +222,12 @@ public OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
         // Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the
         // needed JNI methods.
         setClientAuth(clientMode ? ClientAuth.NONE : checkNotNull(clientAuth, "clientAuth"));
+
+        // Use SNI if peerHost was specified
+        // See https://github.com/netty/netty/issues/4746
+        if (clientMode && peerHost != null) {
+            SSL.setTlsExtHostName(ssl, peerHost);
+        }
     }
 
     @Override
diff --git a/pom.xml b/pom.xml
index e5ae5d4f92a..be65203dbbf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -216,7 +216,7 @@
     <!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
     <os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
     <tcnative.artifactId>netty-tcnative</tcnative.artifactId>
-    <tcnative.version>1.1.33.Fork11</tcnative.version>
+    <tcnative.version>1.1.33.Fork12</tcnative.version>
     <tcnative.classifier>${os.detected.classifier}</tcnative.classifier>
     <epoll.classifier>${os.detected.name}-${os.detected.arch}</epoll.classifier>
   </properties>


Test patch:
diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java
new file mode 100644
index 00000000000..2af2da48b12
--- /dev/null
+++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.ssl;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.DefaultEventLoopGroup;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.channel.local.LocalServerChannel;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import io.netty.util.Mapping;
+import io.netty.util.concurrent.Promise;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class SniClientTest {
+
+    @Test
+    public void testSniClientJdkSslServerJdkSsl() throws Exception {
+        testSniClient(SslProvider.JDK, SslProvider.JDK);
+    }
+
+    @Test
+    public void testSniClientOpenSslServerOpenSsl() throws Exception {
+        Assume.assumeTrue(OpenSsl.isAvailable());
+        testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL);
+    }
+
+    @Test
+    public void testSniClientJdkSslServerOpenSsl() throws Exception {
+        Assume.assumeTrue(OpenSsl.isAvailable());
+        testSniClient(SslProvider.JDK, SslProvider.OPENSSL);
+    }
+
+    @Test
+    public void testSniClientOpenSslServerJdkSsl() throws Exception {
+        Assume.assumeTrue(OpenSsl.isAvailable());
+        testSniClient(SslProvider.OPENSSL, SslProvider.JDK);
+    }
+
+    private static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider) throws Exception {
+        final String sniHost = "sni.netty.io";
+        LocalAddress address = new LocalAddress("test");
+        EventLoopGroup group = new DefaultEventLoopGroup(1);
+        Channel sc = null;
+        Channel cc = null;
+        try {
+            SelfSignedCertificate cert = new SelfSignedCertificate();
+            final SslContext sslServerContext = SslContextBuilder.forServer(cert.key(), cert.cert())
+                    .sslProvider(sslServerProvider).build();
+
+            final Promise<String> promise = group.next().newPromise();
+            ServerBootstrap sb = new ServerBootstrap();
+            sc = sb.group(group).channel(LocalServerChannel.class).childHandler(new ChannelInitializer<Channel>() {
+                @Override
+                protected void initChannel(Channel ch) throws Exception {
+                    ch.pipeline().addFirst(new SniHandler(new Mapping<String, SslContext>() {
+                        @Override
+                        public SslContext map(String input) {
+                            promise.setSuccess(input);
+                            return sslServerContext;
+                        }
+                    }));
+                }
+            }).bind(address).syncUninterruptibly().channel();
+
+            SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
+                    .sslProvider(sslClientProvider).build();
+            Bootstrap cb = new Bootstrap();
+            cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler(
+                    sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)))
+                    .connect(address).syncUninterruptibly().channel();
+            Assert.assertEquals(sniHost, promise.syncUninterruptibly().getNow());
+        } finally {
+            if (cc != null) {
+                cc.close();
+            }
+            if (sc != null) {
+                sc.close();
+            }
+            group.shutdownGracefully();
+        }
+    }
+}