View Javadoc
1   /*
2    * Copyright 2017 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.util.internal.StringUtil;
19  import io.netty.util.internal.SuppressJava6Requirement;
20  
21  import javax.net.ssl.SSLEngine;
22  import javax.net.ssl.SSLEngineResult;
23  import javax.net.ssl.SSLException;
24  
25  import java.nio.ByteBuffer;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.function.BiConsumer;
29  import java.util.function.BiFunction;
30  
31  import static io.netty.handler.ssl.SslUtils.toSSLHandshakeException;
32  import static io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
33  import static io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
34  
35  @SuppressJava6Requirement(reason = "Usage guarded by java version check")
36  class JdkAlpnSslEngine extends JdkSslEngine {
37      private final ProtocolSelectionListener selectionListener;
38      private final AlpnSelector alpnSelector;
39  
40      final class AlpnSelector implements BiFunction<SSLEngine, List<String>, String> {
41          private final ProtocolSelector selector;
42          private boolean called;
43  
44          AlpnSelector(ProtocolSelector selector) {
45              this.selector = selector;
46          }
47  
48          @Override
49          public String apply(SSLEngine sslEngine, List<String> strings) {
50              assert !called;
51              called = true;
52  
53              try {
54                  String selected = selector.select(strings);
55                  return selected == null ? StringUtil.EMPTY_STRING : selected;
56              } catch (Exception cause) {
57                  // Returning null means we want to fail the handshake.
58                  //
59                  // See https://download.java.net/java/jdk9/docs/api/javax/net/ssl/
60                  // SSLEngine.html#setHandshakeApplicationProtocolSelector-java.util.function.BiFunction-
61                  return null;
62              }
63          }
64  
65          void checkUnsupported() {
66              if (called) {
67                  // ALPN message was received by peer and so apply(...) was called.
68                  // See:
69                  // https://hg.openjdk.java.net/jdk9/dev/jdk/file/65464a307408/src/
70                  // java.base/share/classes/sun/security/ssl/ServerHandshaker.java#l933
71                  return;
72              }
73              String protocol = getApplicationProtocol();
74              assert protocol != null;
75  
76              if (protocol.isEmpty()) {
77                  // ALPN is not supported
78                  selector.unsupported();
79              }
80          }
81      }
82  
83      JdkAlpnSslEngine(SSLEngine engine,
84                       @SuppressWarnings("deprecation") JdkApplicationProtocolNegotiator applicationNegotiator,
85                       boolean isServer, BiConsumer<SSLEngine, AlpnSelector> setHandshakeApplicationProtocolSelector,
86                       BiConsumer<SSLEngine, List<String>> setApplicationProtocols) {
87          super(engine);
88          if (isServer) {
89              selectionListener = null;
90              alpnSelector = new AlpnSelector(applicationNegotiator.protocolSelectorFactory().
91                      newSelector(this, new LinkedHashSet<String>(applicationNegotiator.protocols())));
92              setHandshakeApplicationProtocolSelector.accept(engine, alpnSelector);
93          } else {
94              selectionListener = applicationNegotiator.protocolListenerFactory()
95                      .newListener(this, applicationNegotiator.protocols());
96              alpnSelector = null;
97              setApplicationProtocols.accept(engine, applicationNegotiator.protocols());
98          }
99      }
100 
101     JdkAlpnSslEngine(SSLEngine engine,
102                      @SuppressWarnings("deprecation") JdkApplicationProtocolNegotiator applicationNegotiator,
103                      boolean isServer) {
104        this(engine, applicationNegotiator, isServer,
105                new BiConsumer<SSLEngine, AlpnSelector>() {
106                    @Override
107                    public void accept(SSLEngine e, AlpnSelector s) {
108                        JdkAlpnSslUtils.setHandshakeApplicationProtocolSelector(e, s);
109                    }
110                },
111                new BiConsumer<SSLEngine, List<String>>() {
112                    @Override
113                    public void accept(SSLEngine e, List<String> p) {
114                        JdkAlpnSslUtils.setApplicationProtocols(e, p);
115                    }
116                });
117     }
118 
119     private SSLEngineResult verifyProtocolSelection(SSLEngineResult result) throws SSLException {
120         if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
121             if (alpnSelector == null) {
122                 // This means we are using client-side and
123                 try {
124                     String protocol = getApplicationProtocol();
125                     assert protocol != null;
126                     if (protocol.isEmpty()) {
127                         // If empty the server did not announce ALPN:
128                         // See:
129                         // https://hg.openjdk.java.net/jdk9/dev/jdk/file/65464a307408/src/java.base/
130                         // share/classes/sun/security/ssl/ClientHandshaker.java#l741
131                         selectionListener.unsupported();
132                     } else {
133                         selectionListener.selected(protocol);
134                     }
135                 } catch (Throwable e) {
136                     throw toSSLHandshakeException(e);
137                 }
138             } else {
139                 assert selectionListener == null;
140                 alpnSelector.checkUnsupported();
141             }
142         }
143         return result;
144     }
145 
146     @Override
147     public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
148         return verifyProtocolSelection(super.wrap(src, dst));
149     }
150 
151     @Override
152     public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException {
153         return verifyProtocolSelection(super.wrap(srcs, dst));
154     }
155 
156     @Override
157     public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int len, ByteBuffer dst) throws SSLException {
158         return verifyProtocolSelection(super.wrap(srcs, offset, len, dst));
159     }
160 
161     @Override
162     public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
163         return verifyProtocolSelection(super.unwrap(src, dst));
164     }
165 
166     @Override
167     public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException {
168         return verifyProtocolSelection(super.unwrap(src, dsts));
169     }
170 
171     @Override
172     public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dst, int offset, int len) throws SSLException {
173         return verifyProtocolSelection(super.unwrap(src, dst, offset, len));
174     }
175 
176     @Override
177     void setNegotiatedApplicationProtocol(String applicationProtocol) {
178         // Do nothing as this is handled internally by the Java8u251+ implementation of SSLEngine.
179     }
180 
181     @Override
182     public String getNegotiatedApplicationProtocol() {
183         String protocol = getApplicationProtocol();
184         if (protocol != null) {
185             return protocol.isEmpty() ? null : protocol;
186         }
187         return null;
188     }
189 
190     // These methods will override the methods defined by Java 8u251 and later. As we may compile with an earlier
191     // java8 version we don't use @Override annotations here.
192     public String getApplicationProtocol() {
193         return JdkAlpnSslUtils.getApplicationProtocol(getWrappedEngine());
194     }
195 
196     public String getHandshakeApplicationProtocol() {
197         return JdkAlpnSslUtils.getHandshakeApplicationProtocol(getWrappedEngine());
198     }
199 
200     public void setHandshakeApplicationProtocolSelector(BiFunction<SSLEngine, List<String>, String> selector) {
201         JdkAlpnSslUtils.setHandshakeApplicationProtocolSelector(getWrappedEngine(), selector);
202     }
203 
204     public BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector() {
205         return JdkAlpnSslUtils.getHandshakeApplicationProtocolSelector(getWrappedEngine());
206     }
207 }