| /* |
| * Copyright (C) 2012 Square, Inc. |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed 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 com.squareup.okhttp.internal; |
| |
| import dalvik.system.SocketTagger; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.InetSocketAddress; |
| import java.net.Socket; |
| import java.net.SocketException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.List; |
| import java.util.zip.Deflater; |
| import java.util.zip.DeflaterOutputStream; |
| import javax.net.ssl.SSLSocket; |
| |
| import com.android.org.conscrypt.OpenSSLSocketImpl; |
| import com.squareup.okhttp.Protocol; |
| import okio.ByteString; |
| |
| /** |
| * Access to proprietary Android APIs. Doesn't use reflection. |
| */ |
| public final class Platform { |
| private static final Platform PLATFORM = new Platform(); |
| |
| public static Platform get() { |
| return PLATFORM; |
| } |
| |
| public void logW(String warning) { |
| System.logW(warning); |
| } |
| |
| public void tagSocket(Socket socket) throws SocketException { |
| SocketTagger.get().tag(socket); |
| } |
| |
| public void untagSocket(Socket socket) throws SocketException { |
| SocketTagger.get().untag(socket); |
| } |
| |
| public URI toUriLenient(URL url) throws URISyntaxException { |
| return url.toURILenient(); |
| } |
| |
| public void enableTlsExtensions(SSLSocket socket, String uriHost) { |
| if (socket instanceof OpenSSLSocketImpl) { |
| OpenSSLSocketImpl openSSLSocket = (OpenSSLSocketImpl) socket; |
| openSSLSocket.setUseSessionTickets(true); |
| openSSLSocket.setHostname(uriHost); |
| } |
| } |
| |
| public void supportTlsIntolerantServer(SSLSocket socket) { |
| // In accordance with https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 |
| // the SCSV cipher is added to signal that a protocol fallback has taken place. |
| final String fallbackScsv = "TLS_FALLBACK_SCSV"; |
| boolean socketSupportsFallbackScsv = false; |
| String[] supportedCipherSuites = socket.getSupportedCipherSuites(); |
| for (int i = supportedCipherSuites.length - 1; i >= 0; i--) { |
| String supportedCipherSuite = supportedCipherSuites[i]; |
| if (fallbackScsv.equals(supportedCipherSuite)) { |
| socketSupportsFallbackScsv = true; |
| break; |
| } |
| } |
| if (socketSupportsFallbackScsv) { |
| // Add the SCSV cipher to the set of enabled ciphers. |
| String[] enabledCipherSuites = socket.getEnabledCipherSuites(); |
| String[] newEnabledCipherSuites = new String[enabledCipherSuites.length + 1]; |
| System.arraycopy(enabledCipherSuites, 0, |
| newEnabledCipherSuites, 0, enabledCipherSuites.length); |
| newEnabledCipherSuites[newEnabledCipherSuites.length - 1] = fallbackScsv; |
| socket.setEnabledCipherSuites(newEnabledCipherSuites); |
| } |
| socket.setEnabledProtocols(new String[]{"SSLv3"}); |
| } |
| |
| /** |
| * Returns the negotiated protocol, or null if no protocol was negotiated. |
| */ |
| public ByteString getNpnSelectedProtocol(SSLSocket socket) { |
| if (!(socket instanceof OpenSSLSocketImpl)) { |
| return null; |
| } |
| |
| OpenSSLSocketImpl socketImpl = (OpenSSLSocketImpl) socket; |
| // Prefer ALPN's result if it is present. |
| byte[] alpnResult = socketImpl.getAlpnSelectedProtocol(); |
| if (alpnResult != null) { |
| return ByteString.of(alpnResult); |
| } |
| byte[] npnResult = socketImpl.getNpnSelectedProtocol(); |
| return npnResult == null ? null : ByteString.of(npnResult); |
| } |
| |
| /** |
| * Sets client-supported protocols on a socket to send to a server. The |
| * protocols are only sent if the socket implementation supports NPN. |
| */ |
| public void setNpnProtocols(SSLSocket socket, List<Protocol> npnProtocols) { |
| if (socket instanceof OpenSSLSocketImpl) { |
| OpenSSLSocketImpl socketImpl = (OpenSSLSocketImpl) socket; |
| byte[] protocols = concatLengthPrefixed(npnProtocols); |
| socketImpl.setAlpnProtocols(protocols); |
| socketImpl.setNpnProtocols(protocols); |
| } |
| } |
| |
| /** |
| * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name |
| * value blocks. This throws an {@link UnsupportedOperationException} on |
| * Java 6 and earlier where there is no built-in API to do SYNC_FLUSH. |
| */ |
| public OutputStream newDeflaterOutputStream( |
| OutputStream out, Deflater deflater, boolean syncFlush) { |
| return new DeflaterOutputStream(out, deflater, syncFlush); |
| } |
| |
| public void connectSocket(Socket socket, InetSocketAddress address, |
| int connectTimeout) throws IOException { |
| socket.connect(address, connectTimeout); |
| } |
| |
| /** Prefix used on custom headers. */ |
| public String getPrefix() { |
| return "X-Android"; |
| } |
| |
| /** |
| * Concatenation of 8-bit, length prefixed protocol names. |
| * |
| * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 |
| */ |
| static byte[] concatLengthPrefixed(List<Protocol> protocols) { |
| int size = 0; |
| for (Protocol protocol : protocols) { |
| size += protocol.name.size() + 1; // add a byte for 8-bit length prefix. |
| } |
| byte[] result = new byte[size]; |
| int pos = 0; |
| for (Protocol protocol : protocols) { |
| int nameSize = protocol.name.size(); |
| result[pos++] = (byte) nameSize; |
| // toByteArray allocates an array, but this is only called on new connections. |
| System.arraycopy(protocol.name.toByteArray(), 0, result, pos, nameSize); |
| pos += nameSize; |
| } |
| return result; |
| } |
| } |