2493 lines · 91.5 KB · Java
RawDownload
     */    private void closeInternal(boolean selfInitiated) throws IOException {        if ((debug != null) && Debug.isOn("ssl")) {            System.out.println(threadName() + ", called closeInternal("                + selfInitiated + ")");        }         int state = getConnectionState();        boolean closeSocketCalled = false;        Throwable cachedThrowable = null;        try {            switch (state) {            /*             * java.net code sometimes closes sockets "early", when             * we can't actually do I/O on them.             */            case cs_START:                break;             /*             * If we're closing down due to error, we already sent (or else             * received) the fatal alert ... no niceties, blow the connection             * away as quickly as possible (even if we didn't allocate the             * socket ourselves; it's unusable, regardless).             */            case cs_ERROR:                closeSocket();                break;             /*             * Sometimes close() gets called more than once.             */            case cs_CLOSED:            case cs_APP_CLOSED:                 break;             /*             * Otherwise we indicate clean termination.             */            // case cs_HANDSHAKE:            // case cs_DATA:            // case cs_RENEGOTIATE:            // case cs_SENT_CLOSE:            default:                synchronized (this) {                    if (((state = getConnectionState()) == cs_CLOSED) ||                       (state == cs_ERROR) || (state == cs_APP_CLOSED)) {                        return;  // connection was closed while we waited                    }                    if (state != cs_SENT_CLOSE) {                        try {                            warning(Alerts.alert_close_notify);                            connectionState = cs_SENT_CLOSE;                        } catch (Throwable th) {                            // we need to ensure socket is closed out                            // if we encounter any errors.                            connectionState = cs_ERROR;                            // cache this for later use                            cachedThrowable = th;                            closeSocketCalled = true;                            closeSocket(selfInitiated);                        }                    }                }                // If state was cs_SENT_CLOSE before, we don't do the actual                // closing since it is already in progress.                if (state == cs_SENT_CLOSE) {                    if (debug != null && Debug.isOn("ssl")) {                        System.out.println(threadName() +                            ", close invoked again; state = " +                            getConnectionState());                    }                    if (selfInitiated == false) {                        // We were called because a close_notify message was                        // received. This may be due to another thread calling                        // read() or due to our call to waitForClose() below.                        // In either case, just return.                        return;                    }                    // Another thread explicitly called close(). We need to                    // wait for the closing to complete before returning.                    synchronized (this) {                        while (connectionState < cs_CLOSED) {                            try {                                this.wait();                            } catch (InterruptedException e) {                                // ignore                            }                        }                    }                    if ((debug != null) && Debug.isOn("ssl")) {                        System.out.println(threadName() +                            ", after primary close; state = " +                            getConnectionState());                    }                    return;                }                 if (!closeSocketCalled)  {                    closeSocketCalled = true;                    closeSocket(selfInitiated);                }                 break;            }        } finally {            synchronized (this) {                // Upon exit from this method, the state is always >= cs_CLOSED                connectionState = (connectionState == cs_APP_CLOSED)                                ? cs_APP_CLOSED : cs_CLOSED;                // notify any threads waiting for the closing to finish                this.notifyAll();            }            if (closeSocketCalled) {                // Dispose of ciphers since we've closed socket                disposeCiphers();            }            if (cachedThrowable != null) {               /*                * Rethrow the error to the calling method                * The Throwable caught can only be an Error or RuntimeException                */                if (cachedThrowable instanceof Error)                    throw (Error) cachedThrowable;                if (cachedThrowable instanceof RuntimeException)                    throw (RuntimeException) cachedThrowable;            }        }    }     /**     * Reads a close_notify or a fatal alert from the input stream.     * Keep reading records until we get a close_notify or until     * the connection is otherwise closed.  The close_notify or alert     * might be read by another reader,     * which will then process the close and set the connection state.     */    void waitForClose(boolean rethrow) throws IOException {        if (debug != null && Debug.isOn("ssl")) {            System.out.println(threadName() +                ", waiting for close_notify or alert: state "                + getConnectionState());        }         try {            int state;             while (((state = getConnectionState()) != cs_CLOSED) &&                   (state != cs_ERROR) && (state != cs_APP_CLOSED)) {                // create the InputRecord if it isn't intialized.                if (inrec == null) {                    inrec = new InputRecord();                }                 // Ask for app data and then throw it away                try {                    readRecord(inrec, true);                } catch (SocketTimeoutException e) {                    // if time out, ignore the exception and continue                }            }            inrec = null;        } catch (IOException e) {            if (debug != null && Debug.isOn("ssl")) {                System.out.println(threadName() +                    ", Exception while waiting for close " +e);            }            if (rethrow) {                throw e; // pass exception up            }        }    }     /**     * Called by closeInternal() only. Be sure to consider the     * synchronization locks carefully before calling it elsewhere.     */    private void disposeCiphers() {        // See comment in changeReadCiphers()        synchronized (readLock) {            readCipher.dispose();        }        // See comment in changeReadCiphers()        writeLock.lock();        try {            writeCipher.dispose();        } finally {            writeLock.unlock();        }    }     //    // EXCEPTION AND ALERT HANDLING    //     /**     * Handle an exception. This method is called by top level exception     * handlers (in read(), write()) to make sure we always shutdown the     * connection correctly and do not pass runtime exception to the     * application.     */    void handleException(Exception e) throws IOException {        handleException(e, true);    }     /**     * Handle an exception. This method is called by top level exception     * handlers (in read(), write(), startHandshake()) to make sure we     * always shutdown the connection correctly and do not pass runtime     * exception to the application.     *     * This method never returns normally, it always throws an IOException.     *     * We first check if the socket has already been shutdown because of an     * error. If so, we just rethrow the exception. If the socket has not     * been shutdown, we sent a fatal alert and remember the exception.     *     * @param e the Exception     * @param resumable indicates the caller process is resumable from the     *          exception. If <code>resumable</code>, the socket will be     *          reserved for exceptions like timeout; otherwise, the socket     *          will be closed, no further communications could be done.     */    synchronized private void handleException(Exception e, boolean resumable)        throws IOException {        if ((debug != null) && Debug.isOn("ssl")) {            System.out.println(threadName()                        + ", handling exception: " + e.toString());        }         // don't close the Socket in case of timeouts or interrupts if        // the process is resumable.        if (e instanceof InterruptedIOException && resumable) {            throw (IOException)e;        }         // if we've already shutdown because of an error,        // there is nothing to do except rethrow the exception        if (closeReason != null) {            if (e instanceof IOException) { // includes SSLException                throw (IOException)e;            } else {                // this is odd, not an IOException.                // normally, this should not happen                // if closeReason has been already been set                throw Alerts.getSSLException(Alerts.alert_internal_error, e,                                      "Unexpected exception");            }        }         // need to perform error shutdown        boolean isSSLException = (e instanceof SSLException);        if ((isSSLException == false) && (e instanceof IOException)) {            // IOException from the socket            // this means the TCP connection is already dead            // we call fatal just to set the error status            try {                fatal(Alerts.alert_unexpected_message, e);            } catch (IOException ee) {                // ignore (IOException wrapped in SSLException)            }            // rethrow original IOException            throw (IOException)e;        }         // must be SSLException or RuntimeException        byte alertType;        if (isSSLException) {            if (e instanceof SSLHandshakeException) {                alertType = Alerts.alert_handshake_failure;            } else {                alertType = Alerts.alert_unexpected_message;            }        } else {            alertType = Alerts.alert_internal_error;        }        fatal(alertType, e);    }     /*     * Send a warning alert.     */    void warning(byte description) {        sendAlert(Alerts.alert_warning, description);    }     synchronized void fatal(byte description, String diagnostic)            throws IOException {        fatal(description, diagnostic, null);    }     synchronized void fatal(byte description, Throwable cause)            throws IOException {        fatal(description, null, cause);    }     /*     * Send a fatal alert, and throw an exception so that callers will     * need to stand on their heads to accidentally continue processing.     */    synchronized void fatal(byte description, String diagnostic,            Throwable cause) throws IOException {        if ((input != null) && (input.r != null)) {            input.r.close();        }        sess.invalidate();        if (handshakeSession != null) {            handshakeSession.invalidate();        }         int oldState = connectionState;        if (connectionState < cs_ERROR) {            connectionState = cs_ERROR;        }         /*         * Has there been an error received yet?  If not, remember it.         * By RFC 2246, we don't bother waiting for a response.         * Fatal errors require immediate shutdown.         */        if (closeReason == null) {            /*             * Try to clear the kernel buffer to avoid TCP connection resets.             */            if (oldState == cs_HANDSHAKE) {                sockInput.skip(sockInput.available());            }             // If the description equals -1, the alert won't be sent to peer.            if (description != -1) {                sendAlert(Alerts.alert_fatal, description);            }            if (cause instanceof SSLException) { // only true if != null                closeReason = (SSLException)cause;            } else {                closeReason =                    Alerts.getSSLException(description, cause, diagnostic);            }        }         /*         * Clean up our side.         */        closeSocket();        // Another thread may have disposed the ciphers during closing        if (connectionState < cs_CLOSED) {            connectionState = (oldState == cs_APP_CLOSED) ? cs_APP_CLOSED                                                              : cs_CLOSED;             // We should lock readLock and writeLock if no deadlock risks.            // See comment in changeReadCiphers()            readCipher.dispose();            writeCipher.dispose();        }         throw closeReason;    }      /*     * Process an incoming alert ... caller must already have synchronized     * access to "this".     */    private void recvAlert(InputRecord r) throws IOException {        byte level = (byte)r.read();        byte description = (byte)r.read();        if (description == -1) { // check for short message            fatal(Alerts.alert_illegal_parameter, "Short alert message");        }         if (debug != null && (Debug.isOn("record") ||                Debug.isOn("handshake"))) {            synchronized (System.out) {                System.out.print(threadName());                System.out.print(", RECV " + protocolVersion + " ALERT:  ");                if (level == Alerts.alert_fatal) {                    System.out.print("fatal, ");                } else if (level == Alerts.alert_warning) {                    System.out.print("warning, ");                } else {                    System.out.print("<level " + (0x0ff & level) + ">, ");                }                System.out.println(Alerts.alertDescription(description));            }        }         if (level == Alerts.alert_warning) {            if (description == Alerts.alert_close_notify) {                if (connectionState == cs_HANDSHAKE) {                    fatal(Alerts.alert_unexpected_message,                                "Received close_notify during handshake");                } else {                    closeInternal(false);  // reply to close                }            } else {                 //                // The other legal warnings relate to certificates,                // e.g. no_certificate, bad_certificate, etc; these                // are important to the handshaking code, which can                // also handle illegal protocol alerts if needed.                //                if (handshaker != null) {                    handshaker.handshakeAlert(description);                }            }        } else { // fatal or unknown level            String reason = "Received fatal alert: "                + Alerts.alertDescription(description);            if (closeReason == null) {                closeReason = Alerts.getSSLException(description, reason);            }            fatal(Alerts.alert_unexpected_message, reason);        }    }      /*     * Emit alerts.  Caller must have synchronized with "this".     */    private void sendAlert(byte level, byte description) {        // the connectionState cannot be cs_START        if (connectionState >= cs_SENT_CLOSE) {            return;        }         // For initial handshaking, don't send alert message to peer if        // handshaker has not started.        if (connectionState == cs_HANDSHAKE &&            (handshaker == null || !handshaker.started())) {            return;        }         OutputRecord r = new OutputRecord(Record.ct_alert);        r.setVersion(protocolVersion);         boolean useDebug = debug != null && Debug.isOn("ssl");        if (useDebug) {            synchronized (System.out) {                System.out.print(threadName());                System.out.print(", SEND " + protocolVersion + " ALERT:  ");                if (level == Alerts.alert_fatal) {                    System.out.print("fatal, ");                } else if (level == Alerts.alert_warning) {                    System.out.print("warning, ");                } else {                    System.out.print("<level = " + (0x0ff & level) + ">, ");                }                System.out.println("description = "                        + Alerts.alertDescription(description));            }        }         r.write(level);        r.write(description);        try {            writeRecord(r);        } catch (IOException e) {            if (useDebug) {                System.out.println(threadName() +                    ", Exception sending alert: " + e);            }        }    }     //    // VARIOUS OTHER METHODS    //     /*     * When a connection finishes handshaking by enabling use of a newly     * negotiated session, each end learns about it in two halves (read,     * and write).  When both read and write ciphers have changed, and the     * last handshake message has been read, the connection has joined     * (rejoined) the new session.     *     * NOTE:  The SSLv3 spec is rather unclear on the concepts here.     * Sessions don't change once they're established (including cipher     * suite and master secret) but connections can join them (and leave     * them).  They're created by handshaking, though sometime handshaking     * causes connections to join up with pre-established sessions.     */    private void changeReadCiphers() throws SSLException {        if (connectionState != cs_HANDSHAKE                && connectionState != cs_RENEGOTIATE) {            throw new SSLProtocolException(                "State error, change cipher specs");        }         // ... create decompressor         CipherBox oldCipher = readCipher;         try {            readCipher = handshaker.newReadCipher();            readMAC = handshaker.newReadMAC();        } catch (GeneralSecurityException e) {            // "can't happen"            throw (SSLException)new SSLException                                ("Algorithm missing:  ").initCause(e);

Lines 1501–2000 of 2493. Previous lines