1 /*
2  * Copyright (C) 2016 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package okhttp3.internal.platform;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.List;
21 import javax.annotation.Nullable;
22 import javax.net.ssl.SSLParameters;
23 import javax.net.ssl.SSLSocket;
24 import javax.net.ssl.SSLSocketFactory;
25 import javax.net.ssl.X509TrustManager;
26 import okhttp3.Protocol;
27
28 /** OpenJDK 9+. */
29 final class Jdk9Platform extends Platform {
30   final Method setProtocolMethod;
31   final Method getProtocolMethod;
32
33   Jdk9Platform(Method setProtocolMethod, Method getProtocolMethod) {
34     this.setProtocolMethod = setProtocolMethod;
35     this.getProtocolMethod = getProtocolMethod;
36   }
37
38   @Override
39   public void configureTlsExtensions(SSLSocket sslSocket, String hostname,
40       List<Protocol> protocols) {
41     try {
42       SSLParameters sslParameters = sslSocket.getSSLParameters();
43
44       List<String> names = alpnProtocolNames(protocols);
45
46       setProtocolMethod.invoke(sslParameters,
47           new Object[] {names.toArray(new String[names.size()])});
48
49       sslSocket.setSSLParameters(sslParameters);
50     } catch (IllegalAccessException | InvocationTargetException e) {
51       throw new AssertionError("failed to set SSL parameters", e);
52     }
53   }
54
55   @Override
56   public @Nullable String getSelectedProtocol(SSLSocket socket) {
57     try {
58       String protocol = (String) getProtocolMethod.invoke(socket);
59
60       // SSLSocket.getApplicationProtocol returns "" if application protocols values will not
61       // be used. Observed if you didn't specify SSLParameters.setApplicationProtocols
62       if (protocol == null || protocol.equals("")) {
63         return null;
64       }
65
66       return protocol;
67     } catch (InvocationTargetException e) {
68       if (e.getCause() instanceof UnsupportedOperationException) {
69         // Handle UnsupportedOperationException as it is defined in the getApplicationProtocol API.
70         // https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLSocket.html
71         return null;
72       }
73
74       throw new AssertionError("failed to get ALPN selected protocol", e);
75     } catch (IllegalAccessException e) {
76       throw new AssertionError("failed to get ALPN selected protocol", e);
77     }
78   }
79
80   @Override public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) {
81     // Not supported due to access checks on JDK 9+:
82     // java.lang.reflect.InaccessibleObjectException: Unable to make member of class
83     // sun.security.ssl.SSLSocketFactoryImpl accessible:  module java.base does not export
84     // sun.security.ssl to unnamed module @xxx
85     throw new UnsupportedOperationException(
86         "clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 9+");
87   }
88
89   public static Jdk9Platform buildIfSupported() {
90     // Find JDK 9 new methods, also present on JDK8 after build 252.
91     try {
92       Method setProtocolMethod =
93           SSLParameters.class.getMethod("setApplicationProtocols", String[].class);
94       Method getProtocolMethod = SSLSocket.class.getMethod("getApplicationProtocol");
95
96       return new Jdk9Platform(setProtocolMethod, getProtocolMethod);
97     } catch (NoSuchMethodException ignored) {
98       // pre JDK 9
99     }
100
101     return null;
102   }
103 }
104