1 /*
2  * Copyright 2014 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  *   http://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 static io.netty.handler.ssl.ApplicationProtocolUtil.toList;
19 import static io.netty.util.internal.ObjectUtil.checkNotNull;
20
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Set;
24
25 import javax.net.ssl.SSLEngine;
26 import javax.net.ssl.SSLHandshakeException;
27
28 /**
29  * Common base class for {@link JdkApplicationProtocolNegotiator} classes to inherit from.
30  */

31 class JdkBaseApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator {
32     private final List<String> protocols;
33     private final ProtocolSelectorFactory selectorFactory;
34     private final ProtocolSelectionListenerFactory listenerFactory;
35     private final SslEngineWrapperFactory wrapperFactory;
36
37     /**
38      * Create a new instance.
39      * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
40      * @param selectorFactory How the peer selecting the protocol should behave.
41      * @param listenerFactory How the peer being notified of the selected protocol should behave.
42      * @param protocols The order of iteration determines the preference of support for protocols.
43      */

44     JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
45             ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
46             Iterable<String> protocols) {
47         this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
48     }
49
50     /**
51      * Create a new instance.
52      * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
53      * @param selectorFactory How the peer selecting the protocol should behave.
54      * @param listenerFactory How the peer being notified of the selected protocol should behave.
55      * @param protocols The order of iteration determines the preference of support for protocols.
56      */

57     JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
58             ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
59             String... protocols) {
60         this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
61     }
62
63     /**
64      * Create a new instance.
65      * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
66      * @param selectorFactory How the peer selecting the protocol should behave.
67      * @param listenerFactory How the peer being notified of the selected protocol should behave.
68      * @param protocols The order of iteration determines the preference of support for protocols.
69      */

70     private JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
71             ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
72             List<String> protocols) {
73         this.wrapperFactory = checkNotNull(wrapperFactory, "wrapperFactory");
74         this.selectorFactory = checkNotNull(selectorFactory, "selectorFactory");
75         this.listenerFactory = checkNotNull(listenerFactory, "listenerFactory");
76         this.protocols = Collections.unmodifiableList(checkNotNull(protocols, "protocols"));
77     }
78
79     @Override
80     public List<String> protocols() {
81         return protocols;
82     }
83
84     @Override
85     public ProtocolSelectorFactory protocolSelectorFactory() {
86         return selectorFactory;
87     }
88
89     @Override
90     public ProtocolSelectionListenerFactory protocolListenerFactory() {
91         return listenerFactory;
92     }
93
94     @Override
95     public SslEngineWrapperFactory wrapperFactory() {
96         return wrapperFactory;
97     }
98
99     static final ProtocolSelectorFactory FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
100         @Override
101         public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
102             return new FailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
103         }
104     };
105
106     static final ProtocolSelectorFactory NO_FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
107         @Override
108         public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
109             return new NoFailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
110         }
111     };
112
113     static final ProtocolSelectionListenerFactory FAIL_SELECTION_LISTENER_FACTORY =
114             new ProtocolSelectionListenerFactory() {
115         @Override
116         public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
117             return new FailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
118         }
119     };
120
121     static final ProtocolSelectionListenerFactory NO_FAIL_SELECTION_LISTENER_FACTORY =
122             new ProtocolSelectionListenerFactory() {
123         @Override
124         public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
125             return new NoFailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
126         }
127     };
128
129     static class NoFailProtocolSelector implements ProtocolSelector {
130         private final JdkSslEngine engineWrapper;
131         private final Set<String> supportedProtocols;
132
133         NoFailProtocolSelector(JdkSslEngine engineWrapper, Set<String> supportedProtocols) {
134             this.engineWrapper = engineWrapper;
135             this.supportedProtocols = supportedProtocols;
136         }
137
138         @Override
139         public void unsupported() {
140             engineWrapper.setNegotiatedApplicationProtocol(null);
141         }
142
143         @Override
144         public String select(List<String> protocols) throws Exception {
145             for (String p : supportedProtocols) {
146                 if (protocols.contains(p)) {
147                     engineWrapper.setNegotiatedApplicationProtocol(p);
148                     return p;
149                 }
150             }
151             return noSelectMatchFound();
152         }
153
154         public String noSelectMatchFound() throws Exception {
155             engineWrapper.setNegotiatedApplicationProtocol(null);
156             return null;
157         }
158     }
159
160     private static final class FailProtocolSelector extends NoFailProtocolSelector {
161         FailProtocolSelector(JdkSslEngine engineWrapper, Set<String> supportedProtocols) {
162             super(engineWrapper, supportedProtocols);
163         }
164
165         @Override
166         public String noSelectMatchFound() throws Exception {
167             throw new SSLHandshakeException("Selected protocol is not supported");
168         }
169     }
170
171     private static class NoFailProtocolSelectionListener implements ProtocolSelectionListener {
172         private final JdkSslEngine engineWrapper;
173         private final List<String> supportedProtocols;
174
175         NoFailProtocolSelectionListener(JdkSslEngine engineWrapper, List<String> supportedProtocols) {
176             this.engineWrapper = engineWrapper;
177             this.supportedProtocols = supportedProtocols;
178         }
179
180         @Override
181         public void unsupported() {
182             engineWrapper.setNegotiatedApplicationProtocol(null);
183         }
184
185         @Override
186         public void selected(String protocol) throws Exception {
187             if (supportedProtocols.contains(protocol)) {
188                 engineWrapper.setNegotiatedApplicationProtocol(protocol);
189             } else {
190                 noSelectedMatchFound(protocol);
191             }
192         }
193
194         protected void noSelectedMatchFound(String protocol) throws Exception {
195             // Will never be called.
196         }
197     }
198
199     private static final class FailProtocolSelectionListener extends NoFailProtocolSelectionListener {
200         FailProtocolSelectionListener(JdkSslEngine engineWrapper, List<String> supportedProtocols) {
201             super(engineWrapper, supportedProtocols);
202         }
203
204         @Override
205         protected void noSelectedMatchFound(String protocol) throws Exception {
206             throw new SSLHandshakeException("No compatible protocols found");
207         }
208     }
209 }
210