1 /* 2 * Copyright (C) 2012 Square, Inc. 3 * Copyright (C) 2012 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.squareup.okhttp.internal; 18 19 import dalvik.system.SocketTagger; 20 import java.io.IOException; 21 import java.io.OutputStream; 22 import java.net.InetSocketAddress; 23 import java.net.Socket; 24 import java.net.SocketException; 25 import java.net.URI; 26 import java.net.URISyntaxException; 27 import java.net.URL; 28 import java.util.List; 29 import java.util.zip.Deflater; 30 import java.util.zip.DeflaterOutputStream; 31 import javax.net.ssl.SSLSocket; 32 33 import com.android.org.conscrypt.OpenSSLSocketImpl; 34 import com.squareup.okhttp.Protocol; 35 import okio.ByteString; 36 37 /** 38 * Access to proprietary Android APIs. Doesn't use reflection. 39 */ 40 public final class Platform { 41 private static final Platform PLATFORM = new Platform(); 42 get()43 public static Platform get() { 44 return PLATFORM; 45 } 46 logW(String warning)47 public void logW(String warning) { 48 System.logW(warning); 49 } 50 tagSocket(Socket socket)51 public void tagSocket(Socket socket) throws SocketException { 52 SocketTagger.get().tag(socket); 53 } 54 untagSocket(Socket socket)55 public void untagSocket(Socket socket) throws SocketException { 56 SocketTagger.get().untag(socket); 57 } 58 toUriLenient(URL url)59 public URI toUriLenient(URL url) throws URISyntaxException { 60 return url.toURILenient(); 61 } 62 enableTlsExtensions(SSLSocket socket, String uriHost)63 public void enableTlsExtensions(SSLSocket socket, String uriHost) { 64 if (socket instanceof OpenSSLSocketImpl) { 65 OpenSSLSocketImpl openSSLSocket = (OpenSSLSocketImpl) socket; 66 openSSLSocket.setUseSessionTickets(true); 67 openSSLSocket.setHostname(uriHost); 68 } 69 } 70 supportTlsIntolerantServer(SSLSocket socket)71 public void supportTlsIntolerantServer(SSLSocket socket) { 72 // In accordance with https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 73 // the SCSV cipher is added to signal that a protocol fallback has taken place. 74 final String fallbackScsv = "TLS_FALLBACK_SCSV"; 75 boolean socketSupportsFallbackScsv = false; 76 String[] supportedCipherSuites = socket.getSupportedCipherSuites(); 77 for (int i = supportedCipherSuites.length - 1; i >= 0; i--) { 78 String supportedCipherSuite = supportedCipherSuites[i]; 79 if (fallbackScsv.equals(supportedCipherSuite)) { 80 socketSupportsFallbackScsv = true; 81 break; 82 } 83 } 84 if (socketSupportsFallbackScsv) { 85 // Add the SCSV cipher to the set of enabled ciphers. 86 String[] enabledCipherSuites = socket.getEnabledCipherSuites(); 87 String[] newEnabledCipherSuites = new String[enabledCipherSuites.length + 1]; 88 System.arraycopy(enabledCipherSuites, 0, 89 newEnabledCipherSuites, 0, enabledCipherSuites.length); 90 newEnabledCipherSuites[newEnabledCipherSuites.length - 1] = fallbackScsv; 91 socket.setEnabledCipherSuites(newEnabledCipherSuites); 92 } 93 socket.setEnabledProtocols(new String[]{"SSLv3"}); 94 } 95 96 /** 97 * Returns the negotiated protocol, or null if no protocol was negotiated. 98 */ getNpnSelectedProtocol(SSLSocket socket)99 public ByteString getNpnSelectedProtocol(SSLSocket socket) { 100 if (!(socket instanceof OpenSSLSocketImpl)) { 101 return null; 102 } 103 104 OpenSSLSocketImpl socketImpl = (OpenSSLSocketImpl) socket; 105 // Prefer ALPN's result if it is present. 106 byte[] alpnResult = socketImpl.getAlpnSelectedProtocol(); 107 if (alpnResult != null) { 108 return ByteString.of(alpnResult); 109 } 110 byte[] npnResult = socketImpl.getNpnSelectedProtocol(); 111 return npnResult == null ? null : ByteString.of(npnResult); 112 } 113 114 /** 115 * Sets client-supported protocols on a socket to send to a server. The 116 * protocols are only sent if the socket implementation supports NPN. 117 */ setNpnProtocols(SSLSocket socket, List<Protocol> npnProtocols)118 public void setNpnProtocols(SSLSocket socket, List<Protocol> npnProtocols) { 119 if (socket instanceof OpenSSLSocketImpl) { 120 OpenSSLSocketImpl socketImpl = (OpenSSLSocketImpl) socket; 121 byte[] protocols = concatLengthPrefixed(npnProtocols); 122 socketImpl.setAlpnProtocols(protocols); 123 socketImpl.setNpnProtocols(protocols); 124 } 125 } 126 127 /** 128 * Returns a deflater output stream that supports SYNC_FLUSH for SPDY name 129 * value blocks. This throws an {@link UnsupportedOperationException} on 130 * Java 6 and earlier where there is no built-in API to do SYNC_FLUSH. 131 */ newDeflaterOutputStream( OutputStream out, Deflater deflater, boolean syncFlush)132 public OutputStream newDeflaterOutputStream( 133 OutputStream out, Deflater deflater, boolean syncFlush) { 134 return new DeflaterOutputStream(out, deflater, syncFlush); 135 } 136 connectSocket(Socket socket, InetSocketAddress address, int connectTimeout)137 public void connectSocket(Socket socket, InetSocketAddress address, 138 int connectTimeout) throws IOException { 139 socket.connect(address, connectTimeout); 140 } 141 142 /** Prefix used on custom headers. */ getPrefix()143 public String getPrefix() { 144 return "X-Android"; 145 } 146 147 /** 148 * Concatenation of 8-bit, length prefixed protocol names. 149 * 150 * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 151 */ concatLengthPrefixed(List<Protocol> protocols)152 static byte[] concatLengthPrefixed(List<Protocol> protocols) { 153 int size = 0; 154 for (Protocol protocol : protocols) { 155 size += protocol.name.size() + 1; // add a byte for 8-bit length prefix. 156 } 157 byte[] result = new byte[size]; 158 int pos = 0; 159 for (Protocol protocol : protocols) { 160 int nameSize = protocol.name.size(); 161 result[pos++] = (byte) nameSize; 162 // toByteArray allocates an array, but this is only called on new connections. 163 System.arraycopy(protocol.name.toByteArray(), 0, result, pos, nameSize); 164 pos += nameSize; 165 } 166 return result; 167 } 168 } 169