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