OpenSSLSocketImpl.java revision e3a187163504f00c98bd75cbd8bcbdde123ae2cd
1/*
2 * Copyright (C) 2007 The Android Open Source Project
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
17package org.apache.harmony.xnet.provider.jsse;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.net.InetAddress;
23import java.net.Socket;
24import java.net.SocketException;
25import java.security.PrivateKey;
26import java.security.SecureRandom;
27import java.security.cert.CertificateEncodingException;
28import java.security.cert.CertificateException;
29import java.security.cert.X509Certificate;
30import java.util.ArrayList;
31import java.util.concurrent.atomic.AtomicInteger;
32import java.util.logging.Logger;
33import javax.net.ssl.HandshakeCompletedEvent;
34import javax.net.ssl.HandshakeCompletedListener;
35import javax.net.ssl.SSLException;
36import javax.net.ssl.SSLPeerUnverifiedException;
37import javax.net.ssl.SSLSession;
38import javax.security.auth.x500.X500Principal;
39import org.apache.harmony.security.provider.cert.X509CertImpl;
40
41/**
42 * Implementation of the class OpenSSLSocketImpl
43 * based on OpenSSL.
44 *
45 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
46 * later, for example in the package.html or a separate reference document.
47 */
48public class OpenSSLSocketImpl
49        extends javax.net.ssl.SSLSocket
50        implements NativeCrypto.SSLHandshakeCallbacks {
51    private int sslNativePointer;
52    private InputStream is;
53    private OutputStream os;
54    private final Object handshakeLock = new Object();
55    private final Object readLock = new Object();
56    private final Object writeLock = new Object();
57    private SSLParameters sslParameters;
58    private String[] enabledProtocols;
59    private String[] enabledCipherSuites;
60    private OpenSSLSessionImpl sslSession;
61    private Socket socket;
62    private boolean autoClose;
63    private boolean handshakeStarted = false;
64
65    /**
66     * Not set to true until the update from native that tells us the
67     * full handshake is complete, since SSL_do_handshake can return
68     * before the handshake is completely done due to
69     * handshake_cutthrough support.
70     */
71    private boolean handshakeCompleted = false;
72
73    private ArrayList<HandshakeCompletedListener> listeners;
74    private int timeout = 0;
75    // BEGIN android-added
76    private int handshakeTimeout = -1;  // -1 = same as timeout; 0 = infinite
77    // END android-added
78    private String wrappedHost;
79    private int wrappedPort;
80
81    private static final AtomicInteger instanceCount = new AtomicInteger(0);
82
83    public static int getInstanceCount() {
84        return instanceCount.get();
85    }
86
87    private static void updateInstanceCount(int amount) {
88        instanceCount.addAndGet(amount);
89    }
90
91    /**
92     * Class constructor with 1 parameter
93     *
94     * @param sslParameters Parameters for the SSL
95     *            context
96     * @throws IOException if network fails
97     */
98    protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException {
99        super();
100        init(sslParameters);
101    }
102
103    /**
104     * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl
105     *
106     * @param sslParameters Parameters for the SSL
107     *            context
108     * @throws IOException if network fails
109     */
110    protected OpenSSLSocketImpl(SSLParameters sslParameters,
111                                String[] enabledProtocols,
112                                String[] enabledCipherSuites) throws IOException {
113        super();
114        init(sslParameters, enabledProtocols, enabledCipherSuites);
115    }
116
117    /**
118     * Class constructor with 3 parameters
119     *
120     * @throws IOException if network fails
121     * @throws java.net.UnknownHostException host not defined
122     */
123    protected OpenSSLSocketImpl(String host, int port, SSLParameters sslParameters)
124        throws IOException {
125        super(host, port);
126        init(sslParameters);
127    }
128
129    /**
130     * Class constructor with 3 parameters: 1st is InetAddress
131     *
132     * @throws IOException if network fails
133     * @throws java.net.UnknownHostException host not defined
134     */
135    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParameters sslParameters)
136        throws IOException {
137        super(address, port);
138        init(sslParameters);
139    }
140
141
142    /**
143     * Class constructor with 5 parameters: 1st is host
144     *
145     * @throws IOException if network fails
146     * @throws java.net.UnknownHostException host not defined
147     */
148    protected OpenSSLSocketImpl(String host, int port,
149                                InetAddress clientAddress, int clientPort,
150                                SSLParameters sslParameters)
151        throws IOException {
152        super(host, port, clientAddress, clientPort);
153        init(sslParameters);
154    }
155
156    /**
157     * Class constructor with 5 parameters: 1st is InetAddress
158     *
159     * @throws IOException if network fails
160     * @throws java.net.UnknownHostException host not defined
161     */
162    protected OpenSSLSocketImpl(InetAddress address, int port,
163                                InetAddress clientAddress, int clientPort,
164                                SSLParameters sslParameters)
165        throws IOException {
166        super(address, port, clientAddress, clientPort);
167        init(sslParameters);
168    }
169
170    /**
171     * Constructor with 5 parameters: 1st is socket. Enhances an existing socket
172     * with SSL functionality. Invoked via OpenSSLSocketImplWrapper constructor.
173     *
174     * @throws IOException if network fails
175     */
176    protected OpenSSLSocketImpl(Socket socket, String host, int port,
177            boolean autoClose, SSLParameters sslParameters) throws IOException {
178        super();
179        this.socket = socket;
180        this.timeout = socket.getSoTimeout();
181        this.wrappedHost = host;
182        this.wrappedPort = port;
183        this.autoClose = autoClose;
184        init(sslParameters);
185    }
186
187    /**
188     * Initialize the SSL socket and set the certificates for the
189     * future handshaking.
190     */
191    private void init(SSLParameters sslParameters) throws IOException {
192        init(sslParameters,
193             NativeCrypto.getSupportedProtocols(),
194             NativeCrypto.getDefaultCipherSuites());
195    }
196
197    /**
198     * Initialize the SSL socket and set the certificates for the
199     * future handshaking.
200     */
201    private void init(SSLParameters sslParameters,
202                      String[] enabledProtocols,
203                      String[] enabledCipherSuites) throws IOException {
204        this.sslParameters = sslParameters;
205        this.enabledProtocols = enabledProtocols;
206        this.enabledCipherSuites = enabledCipherSuites;
207        updateInstanceCount(1);
208    }
209
210    /**
211     * Gets the suitable session reference from the session cache container.
212     *
213     * @return OpenSSLSessionImpl
214     */
215    private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
216        if (super.getInetAddress() == null ||
217                super.getInetAddress().getHostAddress() == null ||
218                super.getInetAddress().getHostName() == null) {
219            return null;
220        }
221        OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(
222                super.getInetAddress().getHostName(),
223                super.getPort());
224        if (session == null) {
225            return null;
226        }
227
228        String protocol = session.getProtocol();
229        boolean protocolFound = false;
230        for (String enabledProtocol : enabledProtocols) {
231            if (protocol.equals(enabledProtocol)) {
232                protocolFound = true;
233                break;
234            }
235        }
236        if (!protocolFound) {
237            return null;
238        }
239
240        String cipherSuite = session.getCipherSuite();
241        boolean cipherSuiteFound = false;
242        for (String enabledCipherSuite : enabledCipherSuites) {
243            if (cipherSuite.equals(enabledCipherSuite)) {
244                cipherSuiteFound = true;
245                break;
246            }
247        }
248        if (!cipherSuiteFound) {
249            return null;
250        }
251
252        return session;
253    }
254
255    /**
256     * Ensures that logger is lazily loaded. The outer class seems to load
257     * before logging is ready.
258     */
259    static class LoggerHolder {
260        static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName());
261    }
262
263    /**
264     * Starts a TLS/SSL handshake on this connection using some native methods
265     * from the OpenSSL library. It can negotiate new encryption keys, change
266     * cipher suites, or initiate a new session. The certificate chain is
267     * verified if the correspondent property in java.Security is set. All
268     * listeners are notified at the end of the TLS/SSL handshake.
269     *
270     * @throws <code>IOException</code> if network fails
271     */
272    public void startHandshake() throws IOException {
273        startHandshake(true);
274    }
275
276    /**
277     * Perform the handshake
278     * @param full If true, disable handshake cutthrough for a fully synchronous handshake
279     */
280    public synchronized void startHandshake(boolean full) throws IOException {
281        synchronized (handshakeLock) {
282            if (!handshakeStarted) {
283                handshakeStarted = true;
284            } else {
285                return;
286            }
287        }
288
289        // note that this modifies the global seed, not something specific to the connection
290        final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
291        final SecureRandom secureRandom = sslParameters.getSecureRandomMember();
292        if (secureRandom == null) {
293            NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
294        } else {
295            NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes));
296        }
297
298        final boolean client = sslParameters.getUseClientMode();
299
300        final int sslCtxNativePointer = (client) ?
301            sslParameters.getClientSessionContext().sslCtxNativePointer :
302            sslParameters.getServerSessionContext().sslCtxNativePointer;
303
304        this.sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
305
306        // setup server certificates and private keys.
307        // clients will receive a call back to request certificates.
308        if (!client) {
309            for (String keyType : NativeCrypto.KEY_TYPES) {
310                setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
311                                                                               null,
312                                                                               this));
313            }
314        }
315
316        NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
317        NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
318
319        boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
320        if (!enableSessionCreation) {
321            NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
322                                                          enableSessionCreation);
323        }
324
325        AbstractSessionContext sessionContext;
326        OpenSSLSessionImpl session;
327        if (client) {
328            // look for client session to reuse
329            ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
330            sessionContext = clientSessionContext;
331            session = getCachedClientSession(clientSessionContext);
332            if (session != null) {
333                NativeCrypto.SSL_set_session(sslNativePointer,  session.sslSessionNativePointer);
334            }
335        } else {
336            sessionContext = sslParameters.getServerSessionContext();
337            session = null;
338        }
339
340        // setup peer certificate verification
341        if (client) {
342            // TODO support for anonymous cipher would require us to
343            // conditionally use SSL_VERIFY_NONE
344        } else {
345            // needing client auth takes priority...
346            if (sslParameters.getNeedClientAuth()) {
347                NativeCrypto.SSL_set_verify(sslNativePointer,
348                                            NativeCrypto.SSL_VERIFY_PEER|
349                                            NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
350            // ... over just wanting it...
351            } else if (sslParameters.getWantClientAuth()) {
352                NativeCrypto.SSL_set_verify(sslNativePointer,
353                                            NativeCrypto.SSL_VERIFY_PEER);
354            }
355            // ... and it defaults properly so we don't need call SSL_set_verify in the common case.
356
357            // TODO Need to call SSL_CTX_set_client_CA_list to notify trusted issuers to client
358            //
359            // From SSL_CTX_load_verify_locations(3SSL)
360            //     In server mode, when requesting a client
361            //     certificate, the server must send the list of CAs
362            //     of which it will accept client certificates. This
363            //     list is not influenced by the contents of CAfile or
364            //     CApath and must explicitly be set using the
365            //     SSL_CTX_set_client_CA_list(3) family of functions.
366            //
367            // We can get the list from sslParameters.getTrustManager().getAcceptedIssuers()
368        }
369
370        if (client && full) {
371            // we want to do a full synchronous handshake, so turn off cutthrough
372            NativeCrypto.SSL_clear_mode(sslNativePointer,
373                                        NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
374        }
375
376        // BEGIN android-added
377        // Temporarily use a different timeout for the handshake process
378        int savedTimeout = timeout;
379        if (handshakeTimeout >= 0) {
380            setSoTimeout(handshakeTimeout);
381        }
382        // END android-added
383
384
385        Socket socket = this.socket != null ? this.socket : this;
386        int sslSessionNativePointer;
387        try {
388            sslSessionNativePointer
389                = NativeCrypto.SSL_do_handshake(sslNativePointer, socket, this, timeout, client);
390        } catch (CertificateException e) {
391            throw new SSLPeerUnverifiedException(e.getMessage());
392        }
393        byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
394        sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId);
395        if (sslSession != null) {
396            sslSession.lastAccessedTime = System.currentTimeMillis();
397            LoggerHolder.logger.fine("Reused cached session for "
398                                     + getInetAddress() + ".");
399            NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
400        } else {
401            if (!enableSessionCreation) {
402                // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
403                throw new IllegalStateException("SSL Session may not be created");
404            }
405            byte[][] localCertificatesBytes = NativeCrypto.SSL_get_certificate(sslNativePointer);
406            X509Certificate[] localCertificates;
407            if (localCertificatesBytes == null) {
408                localCertificates = null;
409            } else {
410                localCertificates = new X509Certificate[localCertificatesBytes.length];
411                for (int i = 0; i < localCertificatesBytes.length; i++) {
412                    localCertificates[i] = new X509CertImpl(localCertificatesBytes[i]);
413                }
414            }
415
416            if (wrappedHost == null) {
417                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
418                                                    super.getInetAddress().getHostName(),
419                                                    super.getPort(), sessionContext);
420            } else  {
421                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
422                                                    wrappedHost, wrappedPort,
423                                                    sessionContext);
424            }
425            // if not, putSession later in handshakeCompleted() callback
426            if (handshakeCompleted) {
427                sessionContext.putSession(sslSession);
428            }
429            LoggerHolder.logger.fine("Created new session for "
430                                     + getInetAddress().getHostName() + ".");
431        }
432
433        // BEGIN android-added
434        // Restore the original timeout now that the handshake is complete
435        if (handshakeTimeout >= 0) {
436            setSoTimeout(savedTimeout);
437        }
438        // END android-added
439
440        // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
441        if (handshakeCompleted) {
442            notifyHandshakeCompletedListeners();
443        }
444
445    }
446
447    private void setCertificate(String alias) throws IOException {
448        if (alias == null) {
449            return;
450        }
451
452        PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
453        byte[] privateKeyBytes = privateKey.getEncoded();
454        NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes);
455
456        X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
457        byte[][] certificateBytes = new byte[certificates.length][];
458        for (int i = 0; i < certificates.length; i++) {
459            try {
460                certificateBytes[i] = certificates[i].getEncoded();
461            } catch (CertificateEncodingException e) {
462                throw new IOException("Problem encoding certificate " + certificates[i], e);
463            }
464        }
465        NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
466
467        // checks the last installed private key and certificate,
468        // so need to do this once per loop iteration
469        NativeCrypto.SSL_check_private_key(sslNativePointer);
470    }
471
472    /**
473     * Implementation of NativeCrypto.SSLHandshakeCallbacks
474     * invoked via JNI from client_cert_cb
475     */
476    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
477            throws IOException {
478
479        String[] keyTypes = new String[keyTypeBytes.length];
480        for (int i = 0; i < keyTypeBytes.length; i++) {
481            keyTypes[i] = NativeCrypto.keyType(keyTypeBytes[i]);
482        }
483
484        X500Principal[] issuers;
485        if (asn1DerEncodedPrincipals == null) {
486            issuers = null;
487        } else {
488            issuers = new X500Principal[asn1DerEncodedPrincipals.length];
489            for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
490                issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
491            }
492        }
493
494        setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this));
495    }
496
497    /**
498     * Implementation of NativeCrypto.SSLHandshakeCallbacks
499     * invoked via JNI from info_callback
500     */
501    public void handshakeCompleted() {
502        handshakeCompleted = true;
503
504        // If sslSession is null, the handshake was completed during
505        // the call to NativeCrypto.SSL_do_handshake and not during a
506        // later read operation. That means we do not need to fixup
507        // the SSLSession and session cache or notify
508        // HandshakeCompletedListeners, it will be done in
509        // startHandshake.
510        if (sslSession == null) {
511            return;
512        }
513
514        // reset session id from the native pointer and update the
515        // appropriate cache.
516        sslSession.resetId();
517        AbstractSessionContext sessionContext =
518            (sslParameters.getUseClientMode())
519            ? sslParameters.getClientSessionContext()
520                : sslParameters.getServerSessionContext();
521        sessionContext.putSession(sslSession);
522
523        // let listeners know we are finally done
524        notifyHandshakeCompletedListeners();
525    }
526
527    private void notifyHandshakeCompletedListeners() {
528        if (listeners != null && !listeners.isEmpty()) {
529            // notify the listeners
530            HandshakeCompletedEvent event =
531                new HandshakeCompletedEvent(this, sslSession);
532            for (HandshakeCompletedListener listener : listeners) {
533                try {
534                    listener.handshakeCompleted(event);
535                } catch (RuntimeException e) {
536                    // The RI runs the handlers in a separate thread,
537                    // which we do not. But we try to preserve their
538                    // behavior of logging a problem and not killing
539                    // the handshaking thread just because a listener
540                    // has a problem.
541                    Thread thread = Thread.currentThread();
542                    thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
543                }
544            }
545        }
546    }
547
548    /**
549     * Implementation of NativeCrypto.SSLHandshakeCallbacks
550     *
551     * @param bytes An array of ASN.1 DER encoded certficates
552     * @param authMethod auth algorithm name
553     *
554     * @throws CertificateException if the certificate is untrusted
555     */
556    @SuppressWarnings("unused")
557    public void verifyCertificateChain(byte[][] bytes, String authMethod)
558            throws CertificateException {
559        try {
560            if (bytes == null || bytes.length == 0) {
561                throw new SSLException("Peer sent no certificate");
562            }
563            X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length];
564            for (int i = 0; i < bytes.length; i++) {
565                peerCertificateChain[i] =
566                    new X509CertImpl(
567                        javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded());
568            }
569            boolean client = sslParameters.getUseClientMode();
570            if (client) {
571                sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain,
572                                                                   authMethod);
573            } else {
574                sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain,
575                                                                   authMethod);
576            }
577
578        } catch (CertificateException e) {
579            throw e;
580        } catch (Exception e) {
581            throw new RuntimeException(e);
582        }
583    }
584
585    /**
586     * Returns an input stream for this SSL socket using native calls to the
587     * OpenSSL library.
588     *
589     * @return: an input stream for reading bytes from this socket.
590     * @throws: <code>IOException</code> if an I/O error occurs when creating
591     *          the input stream, the socket is closed, the socket is not
592     *          connected, or the socket input has been shutdown.
593     */
594    public InputStream getInputStream() throws IOException {
595        synchronized(this) {
596            if (is == null) {
597                is = new SSLInputStream();
598            }
599
600            return is;
601        }
602    }
603
604    /**
605     * Returns an output stream for this SSL socket using native calls to the
606     * OpenSSL library.
607     *
608     * @return an output stream for writing bytes to this socket.
609     * @throws <code>IOException</code> if an I/O error occurs when creating
610     *             the output stream, or no connection to the socket exists.
611     */
612    public OutputStream getOutputStream() throws IOException {
613        synchronized(this) {
614            if (os == null) {
615                os = new SSLOutputStream();
616            }
617
618            return os;
619        }
620    }
621
622    /**
623     * This method is not supported for this SSLSocket implementation
624     * because reading from an SSLSocket may involve writing to the
625     * network.
626     */
627    public void shutdownInput() throws IOException {
628        throw new UnsupportedOperationException();
629    }
630
631    /**
632     * This method is not supported for this SSLSocket implementation
633     * because writing to an SSLSocket may involve reading from the
634     * network.
635     */
636    public void shutdownOutput() throws IOException {
637        throw new UnsupportedOperationException();
638    }
639
640    /**
641     * This inner class provides input data stream functionality
642     * for the OpenSSL native implementation. It is used to
643     * read data received via SSL protocol.
644     */
645    private class SSLInputStream extends InputStream {
646        SSLInputStream() throws IOException {
647            /**
648            /* Note: When startHandshake() throws an exception, no
649             * SSLInputStream object will be created.
650             */
651            OpenSSLSocketImpl.this.startHandshake(false);
652        }
653
654        /**
655         * Reads one byte. If there is no data in the underlying buffer,
656         * this operation can block until the data will be
657         * available.
658         * @return read value.
659         * @throws <code>IOException</code>
660         */
661        public int read() throws IOException {
662            synchronized(readLock) {
663                return NativeCrypto.SSL_read_byte(sslNativePointer, timeout);
664            }
665        }
666
667        /**
668         * Method acts as described in spec for superclass.
669         * @see java.io.InputStream#read(byte[],int,int)
670         */
671        public int read(byte[] b, int off, int len) throws IOException {
672            synchronized(readLock) {
673                return NativeCrypto.SSL_read(sslNativePointer, b, off, len, timeout);
674            }
675        }
676    }
677
678    /**
679     * This inner class provides output data stream functionality
680     * for the OpenSSL native implementation. It is used to
681     * write data according to the encryption parameters given in SSL context.
682     */
683    private class SSLOutputStream extends OutputStream {
684        SSLOutputStream() throws IOException {
685            /**
686            /* Note: When startHandshake() throws an exception, no
687             * SSLOutputStream object will be created.
688             */
689            OpenSSLSocketImpl.this.startHandshake(false);
690        }
691
692        /**
693         * Method acts as described in spec for superclass.
694         * @see java.io.OutputStream#write(int)
695         */
696        public void write(int b) throws IOException {
697            synchronized(writeLock) {
698                NativeCrypto.SSL_write_byte(sslNativePointer, b);
699            }
700        }
701
702        /**
703         * Method acts as described in spec for superclass.
704         * @see java.io.OutputStream#write(byte[],int,int)
705         */
706        public void write(byte[] b, int start, int len) throws IOException {
707            synchronized(writeLock) {
708                NativeCrypto.SSL_write(sslNativePointer, b, start, len);
709            }
710        }
711    }
712
713
714    /**
715     * The SSL session used by this connection is returned. The SSL session
716     * determines which cipher suite should be used by all connections within
717     * that session and which identities have the session's client and server.
718     * This method starts the SSL handshake.
719     * @return the SSLSession.
720     * @throws <code>IOException</code> if the handshake fails
721     */
722    public SSLSession getSession() {
723        try {
724            startHandshake(true);
725        } catch (IOException e) {
726            // return an invalid session with
727            // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
728            return SSLSessionImpl.NULL_SESSION;
729        }
730        return sslSession;
731    }
732
733    /**
734     * Registers a listener to be notified that a SSL handshake
735     * was successfully completed on this connection.
736     * @throws <code>IllegalArgumentException</code> if listener is null.
737     */
738    public void addHandshakeCompletedListener(
739            HandshakeCompletedListener listener) {
740        if (listener == null) {
741            throw new IllegalArgumentException("Provided listener is null");
742        }
743        if (listeners == null) {
744            listeners = new ArrayList();
745        }
746        listeners.add(listener);
747    }
748
749    /**
750     * The method removes a registered listener.
751     * @throws IllegalArgumentException if listener is null or not registered
752     */
753    public void removeHandshakeCompletedListener(
754            HandshakeCompletedListener listener) {
755        if (listener == null) {
756            throw new IllegalArgumentException("Provided listener is null");
757        }
758        if (listeners == null) {
759            throw new IllegalArgumentException(
760                    "Provided listener is not registered");
761        }
762        if (!listeners.remove(listener)) {
763            throw new IllegalArgumentException(
764                    "Provided listener is not registered");
765        }
766    }
767
768    /**
769     * Returns true if new SSL sessions may be established by this socket.
770     *
771     * @return true if the session may be created; false if a session already
772     *         exists and must be resumed.
773     */
774    public boolean getEnableSessionCreation() {
775        return sslParameters.getEnableSessionCreation();
776    }
777
778    /**
779     * Set a flag for the socket to inhibit or to allow the creation of a new
780     * SSL sessions. If the flag is set to false, and there are no actual
781     * sessions to resume, then there will be no successful handshaking.
782     *
783     * @param flag true if session may be created; false
784     *            if a session already exists and must be resumed.
785     */
786    public void setEnableSessionCreation(boolean flag) {
787        sslParameters.setEnableSessionCreation(flag);
788    }
789
790    /**
791     * The names of the cipher suites which could be used by the SSL connection
792     * are returned.
793     * @return an array of cipher suite names
794     */
795    public String[] getSupportedCipherSuites() {
796        return NativeCrypto.getSupportedCipherSuites();
797    }
798
799    /**
800     * The names of the cipher suites that are in use in the actual the SSL
801     * connection are returned.
802     *
803     * @return an array of cipher suite names
804     */
805    public String[] getEnabledCipherSuites() {
806        return enabledCipherSuites.clone();
807    }
808
809    /**
810     * This method enables the cipher suites listed by
811     * getSupportedCipherSuites().
812     *
813     * @param suites names of all the cipher suites to
814     *            put on use
815     * @throws IllegalArgumentException when one or more of the
816     *             ciphers in array suites are not supported, or when the array
817     *             is null.
818     */
819    public void setEnabledCipherSuites(String[] suites) {
820        enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
821    }
822
823    /**
824     * The names of the protocols' versions that may be used on this SSL
825     * connection.
826     * @return an array of protocols names
827     */
828    public String[] getSupportedProtocols() {
829        return NativeCrypto.getSupportedProtocols();
830    }
831
832    /**
833     * The names of the protocols' versions that are in use on this SSL
834     * connection.
835     *
836     * @return an array of protocols names
837     */
838    @Override
839    public String[] getEnabledProtocols() {
840        return enabledProtocols.clone();
841    }
842
843    /**
844     * This method enables the protocols' versions listed by
845     * getSupportedProtocols().
846     *
847     * @param protocols The names of all the protocols to put on use
848     *
849     * @throws IllegalArgumentException when one or more of the names in the
850     *             array are not supported, or when the array is null.
851     */
852    @Override
853    public synchronized void setEnabledProtocols(String[] protocols) {
854        enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
855    }
856
857    /**
858     * This method gives true back if the SSL socket is set to client mode.
859     *
860     * @return true if the socket should do the handshaking as client.
861     */
862    public boolean getUseClientMode() {
863        return sslParameters.getUseClientMode();
864    }
865
866    /**
867     * This method set the actual SSL socket to client mode.
868     *
869     * @param mode true if the socket starts in client
870     *            mode
871     * @throws IllegalArgumentException if mode changes during
872     *             handshake.
873     */
874    public synchronized void setUseClientMode(boolean mode) {
875        if (handshakeStarted) {
876            throw new IllegalArgumentException(
877            "Could not change the mode after the initial handshake has begun.");
878        }
879        sslParameters.setUseClientMode(mode);
880    }
881
882    /**
883     * Returns true if the SSL socket requests client's authentication. Relevant
884     * only for server sockets!
885     *
886     * @return true if client authentication is desired, false if not.
887     */
888    public boolean getWantClientAuth() {
889        return sslParameters.getWantClientAuth();
890    }
891
892    /**
893     * Returns true if the SSL socket needs client's authentication. Relevant
894     * only for server sockets!
895     *
896     * @return true if client authentication is desired, false if not.
897     */
898    public boolean getNeedClientAuth() {
899        return sslParameters.getNeedClientAuth();
900    }
901
902    /**
903     * Sets the SSL socket to use client's authentication. Relevant only for
904     * server sockets!
905     *
906     * @param need true if client authentication is
907     *            desired, false if not.
908     */
909    public void setNeedClientAuth(boolean need) {
910        sslParameters.setNeedClientAuth(need);
911    }
912
913    /**
914     * Sets the SSL socket to use client's authentication. Relevant only for
915     * server sockets! Notice that in contrast to setNeedClientAuth(..) this
916     * method will continue the negotiation if the client decide not to send
917     * authentication credentials.
918     *
919     * @param want true if client authentication is
920     *            desired, false if not.
921     */
922    public void setWantClientAuth(boolean want) {
923        sslParameters.setWantClientAuth(want);
924    }
925
926    /**
927     * This method is not supported for SSLSocket implementation.
928     */
929    public void sendUrgentData(int data) throws IOException {
930        throw new SocketException(
931                "Method sendUrgentData() is not supported.");
932    }
933
934    /**
935     * This method is not supported for SSLSocket implementation.
936     */
937    public void setOOBInline(boolean on) throws SocketException {
938        throw new SocketException(
939                "Methods sendUrgentData, setOOBInline are not supported.");
940    }
941
942    /**
943     * Set the read timeout on this socket. The SO_TIMEOUT option, is specified
944     * in milliseconds. The read operation will block indefinitely for a zero
945     * value.
946     *
947     * @param timeout the read timeout value
948     * @throws SocketException if an error occurs setting the option
949     */
950    public synchronized void setSoTimeout(int timeout) throws SocketException {
951        super.setSoTimeout(timeout);
952        this.timeout = timeout;
953    }
954
955    // BEGIN android-added
956    /**
957     * Set the handshake timeout on this socket.  This timeout is specified in
958     * milliseconds and will be used only during the handshake process.
959     *
960     * @param timeout the handshake timeout value
961     */
962    public synchronized void setHandshakeTimeout(int timeout) throws SocketException {
963        this.handshakeTimeout = timeout;
964    }
965    // END android-added
966
967    /**
968     * Closes the SSL socket. Once closed, a socket is not available for further
969     * use anymore under any circumstance. A new socket must be created.
970     *
971     * @throws <code>IOException</code> if an I/O error happens during the
972     *             socket's closure.
973     */
974    public void close() throws IOException {
975        // TODO: Close SSL sockets using a background thread so they close
976        // gracefully.
977
978        synchronized (handshakeLock) {
979            if (!handshakeStarted) {
980                // prevent further attemps to start handshake
981                handshakeStarted = true;
982
983                synchronized (this) {
984                    free();
985
986                    if (socket != null) {
987                        if (autoClose && !socket.isClosed()) socket.close();
988                    } else {
989                        if (!super.isClosed()) super.close();
990                    }
991                }
992
993                return;
994            }
995        }
996
997        NativeCrypto.SSL_interrupt(sslNativePointer);
998
999        synchronized (this) {
1000            synchronized (writeLock) {
1001                synchronized (readLock) {
1002
1003                    IOException pendingException = null;
1004
1005                    // Shut down the SSL connection, per se.
1006                    try {
1007                        if (handshakeStarted) {
1008                            NativeCrypto.SSL_shutdown(sslNativePointer);
1009                        }
1010                    } catch (IOException ex) {
1011                        /*
1012                         * Note the exception at this point, but try to continue
1013                         * to clean the rest of this all up before rethrowing.
1014                         */
1015                        pendingException = ex;
1016                    }
1017
1018                    /*
1019                     * Even if the above call failed, it is still safe to free
1020                     * the native structs, and we need to do so lest we leak
1021                     * memory.
1022                     */
1023                    free();
1024
1025                    if (socket != null) {
1026                        if (autoClose && !socket.isClosed())
1027                            socket.close();
1028                    } else {
1029                        if (!super.isClosed())
1030                            super.close();
1031                    }
1032
1033                    if (pendingException != null) {
1034                        throw pendingException;
1035                    }
1036                }
1037            }
1038        }
1039    }
1040
1041    private void free() {
1042        if (sslNativePointer == 0) {
1043            return;
1044        }
1045        NativeCrypto.SSL_free(sslNativePointer);
1046        sslNativePointer = 0;
1047    }
1048
1049    protected void finalize() throws IOException {
1050        /*
1051         * Just worry about our own state. Notably we do not try and
1052         * close anything. The SocketImpl, either our own
1053         * PlainSocketImpl, or the Socket we are wrapping, will do
1054         * that. This might mean we do not properly SSL_shutdown, but
1055         * if you want to do that, properly close the socket yourself.
1056         *
1057         * The reason why we don't try to SSL_shutdown, is that there
1058         * can be a race between finalizers where the PlainSocketImpl
1059         * finalizer runs first and closes the socket. However, in the
1060         * meanwhile, the underlying file descriptor could be reused
1061         * for another purpose. If we call SSL_shutdown, the
1062         * underlying socket BIOs still have the old file descriptor
1063         * and will write the close notify to some unsuspecting
1064         * reader.
1065         */
1066        updateInstanceCount(-1);
1067        free();
1068    }
1069}
1070