MailTransport.java revision 51c653646d14d841fbe527aee9fab7a1886338f8
196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/* 296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License. 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License. 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.mail.transport; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 19b203b2b1196bfd5507c83a4fe81d362de840ec0aMarc Blankimport android.content.Context; 20b203b2b1196bfd5507c83a4fe81d362de840ec0aMarc Blank 2151c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdonimport com.android.email.DebugUtils; 2231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.Logging; 232193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.CertificateValidationException; 242193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.MessagingException; 257d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blankimport com.android.emailcommon.provider.HostAuth; 263a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blankimport com.android.emailcommon.utility.SSLUtils; 27560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils; 2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.BufferedInputStream; 3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.BufferedOutputStream; 3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException; 3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream; 3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.OutputStream; 34f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onukiimport java.net.InetAddress; 3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.InetSocketAddress; 3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.Socket; 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.SocketAddress; 3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.SocketException; 3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 40fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.HostnameVerifier; 41fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.HttpsURLConnection; 4296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport javax.net.ssl.SSLException; 43fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLPeerUnverifiedException; 44fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLSession; 45fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadlerimport javax.net.ssl.SSLSocket; 4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 47b203b2b1196bfd5507c83a4fe81d362de840ec0aMarc Blankpublic class MailTransport { 488d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // TODO protected eventually 5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /*protected*/ public static final int SOCKET_CONNECT_TIMEOUT = 10000; 5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /*protected*/ public static final int SOCKET_READ_TIMEOUT = 60000; 5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 53fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler private static final HostnameVerifier HOSTNAME_VERIFIER = 54fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler HttpsURLConnection.getDefaultHostnameVerifier(); 55fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 567d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank private final String mDebugLabel; 577d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank private final Context mContext; 5817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie protected final HostAuth mHostAuth; 5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 6096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private Socket mSocket; 6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private InputStream mIn; 6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project private OutputStream mOut; 6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 647d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank public MailTransport(Context context, String debugLabel, HostAuth hostAuth) { 6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project super(); 667d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank mContext = context; 6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mDebugLabel = debugLabel; 687d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank mHostAuth = hostAuth; 6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 708d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 717d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank /** 72ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * Returns a new transport, using the current transport as a model. The new transport is 73ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * configured identically (as if {@link #setSecurity(int, boolean)}, {@link #setPort(int)} 74ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy * and {@link #setHost(String)} were invoked), but not opened or connected in any way. 7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 76ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy @Override 77b203b2b1196bfd5507c83a4fe81d362de840ec0aMarc Blank public MailTransport clone() { 787d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return new MailTransport(mContext, mDebugLabel, mHostAuth); 79a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy } 80a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy 8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public String getHost() { 827d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return mHostAuth.mAddress; 8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public int getPort() { 867d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return mHostAuth.mPort; 8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean canTrySslSecurity() { 907d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return (mHostAuth.mFlags & HostAuth.FLAG_SSL) != 0; 9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 928d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean canTryTlsSecurity() { 947d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return (mHostAuth.mFlags & HostAuth.FLAG_TLS) != 0; 9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 968d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 97e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler public boolean canTrustAllCertificates() { 987d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank return (mHostAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0; 99e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } 100e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler 10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Attempts to open a connection using the Uri supplied for connection parameters. Will attempt 10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * an SSL connection if indicated. 10496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void open() throws MessagingException, CertificateValidationException { 10651c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 107560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "*** " + mDebugLabel + " open " + 10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project getHost() + ":" + String.valueOf(getPort())); 10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project SocketAddress socketAddress = new InetSocketAddress(getHost(), getPort()); 11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (canTrySslSecurity()) { 1147d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank mSocket = SSLUtils.getSSLSocketFactory( 1157d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank mContext, mHostAuth, canTrustAllCertificates()).createSocket(); 11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket = new Socket(); 11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT); 120fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // After the socket connects to an SSL server, confirm that the hostname is as expected 121fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (canTrySslSecurity() && !canTrustAllCertificates()) { 122fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler verifyHostname(mSocket, getHost()); 123fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); 12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); 1269a2686afa2e02c62a8e5f9c42c82bd2da70b96afYu Ping Hu mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); 12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (SSLException e) { 12851c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 129560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, e.toString()); 13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new CertificateValidationException(e.getMessage(), e); 13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (IOException ioe) { 13351c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 134560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, ioe.toString()); 13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new MessagingException(MessagingException.IOERROR, ioe.toString()); 137532bcf4a522f91258c74604b74096c9b1f7c0510Paul Westbrook } catch (IllegalArgumentException iae) { 13851c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 139560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, iae.toString()); 140532bcf4a522f91258c74604b74096c9b1f7c0510Paul Westbrook } 141532bcf4a522f91258c74604b74096c9b1f7c0510Paul Westbrook throw new MessagingException(MessagingException.UNSPECIFIED_EXCEPTION, iae.toString()); 14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Attempts to reopen a TLS connection using the Uri supplied for connection parameters. 14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * 148fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * NOTE: No explicit hostname verification is required here, because it's handled automatically 149fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * by the call to createSocket(). 150fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * TODO should we explicitly close the old socket? This seems funky to abandon it. 15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void reopenTls() throws MessagingException { 15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 1557d5e2a7c08966ffd4a9e8c78f504cc4fd5be4216Marc Blank mSocket = SSLUtils.getSSLSocketFactory(mContext, mHostAuth, canTrustAllCertificates()) 156c5912e4920bb4fa1979d63d47e7f430e87e3820fMakoto Onuki .createSocket(mSocket, getHost(), getPort(), true); 15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); 15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); 15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); 16096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 161e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } catch (SSLException e) { 16251c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 163560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, e.toString()); 164e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler } 165e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler throw new CertificateValidationException(e.getMessage(), e); 16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (IOException ioe) { 16751c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 168560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, ioe.toString()); 16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project throw new MessagingException(MessagingException.IOERROR, ioe.toString()); 17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 173fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 174fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler /** 175fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * Lightweight version of SSLCertificateSocketFactory.verifyHostname, which provides this 176fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * service but is not in the public API. 177fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 178fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * Verify the hostname of the certificate used by the other end of a 179fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * connected socket. You MUST call this if you did not supply a hostname 180fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * to SSLCertificateSocketFactory.createSocket(). It is harmless to call this method 181fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * redundantly if the hostname has already been verified. 182fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 183fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * <p>Wildcard certificates are allowed to verify any matching hostname, 184fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * so "foo.bar.example.com" is verified if the peer has a certificate 185fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * for "*.example.com". 186fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * 187fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @param socket An SSL socket which has been connected to a server 188fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @param hostname The expected hostname of the remote server 189fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @throws IOException if something goes wrong handshaking with the server 190fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler * @throws SSLPeerUnverifiedException if the server cannot prove its identity 191fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler */ 1921b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy private static void verifyHostname(Socket socket, String hostname) throws IOException { 193fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // The code at the start of OpenSSLSocketImpl.startHandshake() 194fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // ensures that the call is idempotent, so we can safely call it. 195fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler SSLSocket ssl = (SSLSocket) socket; 196fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler ssl.startHandshake(); 197fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 198fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler SSLSession session = ssl.getSession(); 199fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (session == null) { 200fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler throw new SSLException("Cannot verify SSL socket without session"); 201fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 202fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // TODO: Instead of reporting the name of the server we think we're connecting to, 203fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // we should be reporting the bad name in the certificate. Unfortunately this is buried 204fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // in the verifier code and is not available in the verifier API, and extracting the 205fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler // CN & alts is beyond the scope of this patch. 206fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler if (!HOSTNAME_VERIFIER.verify(hostname, session)) { 207fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler throw new SSLPeerUnverifiedException( 208fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler "Certificate hostname not useable for server: " + hostname); 209fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 210fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler } 211fb060de65db57607748cbf8bc5b93939281a443fAndrew Stadler 21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 2130c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler * Get the socket timeout. 2140c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler * @return the read timeout value in milliseconds 2150c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler * @throws SocketException 2160c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler */ 2170c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler public int getSoTimeout() throws SocketException { 2180c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler return mSocket.getSoTimeout(); 2190c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler } 2200c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler 2210c8696c2ebd52c7f2a92fa7b6b8d5d2005c19d1cTony Mantler /** 22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Set the socket timeout. 22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @param timeoutMilliseconds the read timeout value if greater than {@code 0}, or 22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * {@code 0} for an infinite timeout. 22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void setSoTimeout(int timeoutMilliseconds) throws SocketException { 22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.setSoTimeout(timeoutMilliseconds); 22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public boolean isOpen() { 2318d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy return (mIn != null && mOut != null && 23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket != null && mSocket.isConnected() && !mSocket.isClosed()); 23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Close the connection. MUST NOT return any exceptions - must be "best effort" and safe. 23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void close() { 23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn.close(); 24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut.close(); 24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project try { 25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket.close(); 25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } catch (Exception e) { 25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project // May fail if the connection is already closed. 25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mIn = null; 25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mOut = null; 25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project mSocket = null; 25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public InputStream getInputStream() { 26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return mIn; 26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public OutputStream getOutputStream() { 26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return mOut; 26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2668d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Writes a single line to the server using \r\n termination. 26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project public void writeLine(String s, String sensitiveReplacement) throws IOException { 27151c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (DebugUtils.DEBUG) { 272bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onuki if (sensitiveReplacement != null && !Logging.DEBUG_SENSITIVE) { 273560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, ">>> " + sensitiveReplacement); 27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 275560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, ">>> " + s); 27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2788d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project OutputStream out = getOutputStream(); 28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write(s.getBytes()); 28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write('\r'); 28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.write('\n'); 28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project out.flush(); 28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 2858d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy 28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project /** 28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Reads a single line from the server, using either \r\n or \n as the delimiter. The 28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * delimiter char(s) are not included in the result. 28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */ 290b203b2b1196bfd5507c83a4fe81d362de840ec0aMarc Blank public String readLine(boolean loggable) throws IOException { 29196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project StringBuffer sb = new StringBuffer(); 29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project InputStream in = getInputStream(); 29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project int d; 29496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project while ((d = in.read()) != -1) { 29596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project if (((char)d) == '\r') { 29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project continue; 29796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else if (((char)d) == '\n') { 29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project break; 29996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } else { 30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project sb.append((char)d); 30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30351c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (d == -1 && DebugUtils.DEBUG) { 304560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "End of stream reached while trying to read line."); 30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project String ret = sb.toString(); 30751c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon if (loggable && DebugUtils.DEBUG) { 308560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy LogUtils.d(Logging.LOG_TAG, "<<< " + ret); 30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return ret; 31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 313f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki public InetAddress getLocalAddress() { 314f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki if (isOpen()) { 315f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki return mSocket.getLocalAddress(); 316f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } else { 317f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki return null; 318f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } 319f4dac9f266906a84f4710d8af5d4a24f2290b1baMakoto Onuki } 32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 321