1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.xnet.provider.jsse;
19
20
21import java.io.IOException;
22import java.nio.BufferUnderflowException;
23import java.nio.ByteBuffer;
24import java.nio.ReadOnlyBufferException;
25import javax.net.ssl.SSLEngine;
26import javax.net.ssl.SSLEngineResult;
27import javax.net.ssl.SSLException;
28import javax.net.ssl.SSLHandshakeException;
29import javax.net.ssl.SSLSession;
30
31/**
32 * Implementation of SSLEngine.
33 * @see javax.net.ssl.SSLEngine class documentation for more information.
34 */
35public class SSLEngineImpl extends SSLEngine {
36
37    // indicates if peer mode was set
38    private boolean peer_mode_was_set = false;
39    // indicates if handshake has been started
40    private boolean handshake_started = false;
41    // indicates if inbound operations finished
42    private boolean isInboundDone = false;
43    // indicates if outbound operations finished
44    private boolean isOutboundDone = false;
45    // indicates if close_notify alert had been sent to another peer
46    private boolean close_notify_was_sent = false;
47    // indicates if close_notify alert had been received from another peer
48    private boolean close_notify_was_received = false;
49    // indicates if engine was closed (it means that
50    // all the works on it are done, except (probably) some finalizing work)
51    private boolean engine_was_closed = false;
52    // indicates if engine was shutted down (it means that
53    // all cleaning work had been done and the engine is not operable)
54    private boolean engine_was_shutteddown = false;
55
56    // record protocol to be used
57    protected SSLRecordProtocol recordProtocol;
58    // input stream for record protocol
59    private SSLBufferedInput recProtIS;
60    // handshake protocol to be used
61    private HandshakeProtocol handshakeProtocol;
62    // alert protocol to be used
63    private AlertProtocol alertProtocol;
64    // place where application data will be stored
65    private SSLEngineAppData appData;
66    // outcoming application data stream
67    private SSLEngineDataStream dataStream = new SSLEngineDataStream();
68    // active session object
69    private SSLSessionImpl session;
70
71    // peer configuration parameters
72    protected SSLParametersImpl sslParameters;
73
74    // in case of emergency situations when data could not be
75    // placed in destination buffers it will be stored in this
76    // fields
77    private byte[] remaining_wrapped_data = null;
78    private byte[] remaining_hsh_data = null;
79
80    // logger
81    private Logger.Stream logger = Logger.getStream("engine");
82
83    protected SSLEngineImpl(SSLParametersImpl sslParameters) {
84        this.sslParameters = sslParameters;
85    }
86
87    protected SSLEngineImpl(String host, int port, SSLParametersImpl sslParameters) {
88        super(host, port);
89        this.sslParameters = sslParameters;
90    }
91
92    /**
93     * Starts the handshake.
94     * @throws  SSLException
95     * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation
96     * for more information
97     */
98    @Override
99    public void beginHandshake() throws SSLException {
100        if (engine_was_closed) {
101            throw new SSLException("Engine has already been closed.");
102        }
103        if (!peer_mode_was_set) {
104            throw new IllegalStateException("Client/Server mode was not set");
105        }
106        if (!handshake_started) {
107            handshake_started = true;
108            if (getUseClientMode()) {
109                handshakeProtocol = new ClientHandshakeImpl(this);
110            } else {
111                handshakeProtocol = new ServerHandshakeImpl(this);
112            }
113            appData = new SSLEngineAppData();
114            alertProtocol = new AlertProtocol();
115            recProtIS = new SSLBufferedInput();
116            recordProtocol = new SSLRecordProtocol(handshakeProtocol,
117                    alertProtocol, recProtIS, appData);
118        }
119        handshakeProtocol.start();
120    }
121
122    /**
123     * Closes inbound operations of this engine
124     * @throws  SSLException
125     * @see javax.net.ssl.SSLEngine#closeInbound() method documentation
126     * for more information
127     */
128    @Override
129    public void closeInbound() throws SSLException {
130        if (logger != null) {
131            logger.println("closeInbound() "+isInboundDone);
132        }
133        if (isInboundDone) {
134            return;
135        }
136        isInboundDone = true;
137        engine_was_closed = true;
138        if (handshake_started) {
139            if (!close_notify_was_received) {
140                if (session != null) {
141                    session.invalidate();
142                }
143                alertProtocol.alert(AlertProtocol.FATAL,
144                        AlertProtocol.INTERNAL_ERROR);
145                throw new SSLException("Inbound is closed before close_notify "
146                        + "alert has been received.");
147            }
148        } else {
149            // engine is closing before initial handshake has been made
150            shutdown();
151        }
152    }
153
154    /**
155     * Closes outbound operations of this engine
156     * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation
157     * for more information
158     */
159    @Override
160    public void closeOutbound() {
161        if (logger != null) {
162            logger.println("closeOutbound() "+isOutboundDone);
163        }
164        if (isOutboundDone) {
165            return;
166        }
167        isOutboundDone = true;
168        if (handshake_started) {
169            // initial handshake had been started
170            alertProtocol.alert(AlertProtocol.WARNING,
171                    AlertProtocol.CLOSE_NOTIFY);
172            close_notify_was_sent = true;
173        } else {
174            // engine is closing before initial handshake has been made
175            shutdown();
176        }
177        engine_was_closed = true;
178    }
179
180    /**
181     * Returns handshake's delegated tasks to be run
182     * @return the delegated task to be executed.
183     * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation
184     * for more information
185     */
186    @Override
187    public Runnable getDelegatedTask() {
188        return handshakeProtocol.getTask();
189    }
190
191    /**
192     * Returns names of supported cipher suites.
193     * @return array of strings containing the names of supported cipher suites
194     * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method
195     * documentation for more information
196     */
197    @Override
198    public String[] getSupportedCipherSuites() {
199        return CipherSuite.getSupportedCipherSuiteNames();
200    }
201
202    // --------------- SSLParameters based methods ---------------------
203
204    /**
205     * This method works according to the specification of implemented class.
206     * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method
207     * documentation for more information
208     */
209    @Override
210    public String[] getEnabledCipherSuites() {
211        return sslParameters.getEnabledCipherSuites();
212    }
213
214    /**
215     * This method works according to the specification of implemented class.
216     * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method
217     * documentation for more information
218     */
219    @Override
220    public void setEnabledCipherSuites(String[] suites) {
221        sslParameters.setEnabledCipherSuites(suites);
222    }
223
224    /**
225     * This method works according to the specification of implemented class.
226     * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method
227     * documentation for more information
228     */
229    @Override
230    public String[] getSupportedProtocols() {
231        return ProtocolVersion.supportedProtocols.clone();
232    }
233
234    /**
235     * This method works according to the specification of implemented class.
236     * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method
237     * documentation for more information
238     */
239    @Override
240    public String[] getEnabledProtocols() {
241        return sslParameters.getEnabledProtocols();
242    }
243
244    /**
245     * This method works according to the specification of implemented class.
246     * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method
247     * documentation for more information
248     */
249    @Override
250    public void setEnabledProtocols(String[] protocols) {
251        sslParameters.setEnabledProtocols(protocols);
252    }
253
254    /**
255     * This method works according to the specification of implemented class.
256     * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method
257     * documentation for more information
258     */
259    @Override
260    public void setUseClientMode(boolean mode) {
261        if (handshake_started) {
262            throw new IllegalArgumentException(
263            "Could not change the mode after the initial handshake has begun.");
264        }
265        sslParameters.setUseClientMode(mode);
266        peer_mode_was_set = true;
267    }
268
269    /**
270     * This method works according to the specification of implemented class.
271     * @see javax.net.ssl.SSLEngine#getUseClientMode() method
272     * documentation for more information
273     */
274    @Override
275    public boolean getUseClientMode() {
276        return sslParameters.getUseClientMode();
277    }
278
279    /**
280     * This method works according to the specification of implemented class.
281     * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method
282     * documentation for more information
283     */
284    @Override
285    public void setNeedClientAuth(boolean need) {
286        sslParameters.setNeedClientAuth(need);
287    }
288
289    /**
290     * This method works according to the specification of implemented class.
291     * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method
292     * documentation for more information
293     */
294    @Override
295    public boolean getNeedClientAuth() {
296        return sslParameters.getNeedClientAuth();
297    }
298
299    /**
300     * This method works according to the specification of implemented class.
301     * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method
302     * documentation for more information
303     */
304    @Override
305    public void setWantClientAuth(boolean want) {
306        sslParameters.setWantClientAuth(want);
307    }
308
309    /**
310     * This method works according to the specification of implemented class.
311     * @see javax.net.ssl.SSLEngine#getWantClientAuth() method
312     * documentation for more information
313     */
314    @Override
315    public boolean getWantClientAuth() {
316        return sslParameters.getWantClientAuth();
317    }
318
319    /**
320     * This method works according to the specification of implemented class.
321     * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method
322     * documentation for more information
323     */
324    @Override
325    public void setEnableSessionCreation(boolean flag) {
326        sslParameters.setEnableSessionCreation(flag);
327    }
328
329    /**
330     * This method works according to the specification of implemented class.
331     * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method
332     * documentation for more information
333     */
334    @Override
335    public boolean getEnableSessionCreation() {
336        return sslParameters.getEnableSessionCreation();
337    }
338
339    // -----------------------------------------------------------------
340
341    /**
342     * This method works according to the specification of implemented class.
343     * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method
344     * documentation for more information
345     */
346    @Override
347    public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
348        if (!handshake_started || engine_was_shutteddown) {
349            // initial handshake has not been started yet
350            return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
351        }
352        if (alertProtocol.hasAlert()) {
353            // need to send an alert
354            return SSLEngineResult.HandshakeStatus.NEED_WRAP;
355        }
356        if (close_notify_was_sent && !close_notify_was_received) {
357            // waiting for "close_notify" response
358            return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
359        }
360        return handshakeProtocol.getStatus();
361    }
362
363    /**
364     * This method works according to the specification of implemented class.
365     * @see javax.net.ssl.SSLEngine#getSession() method
366     * documentation for more information
367     */
368    @Override
369    public SSLSession getSession() {
370        if (session != null) {
371            return session;
372        }
373        return SSLSessionImpl.NULL_SESSION;
374    }
375
376    /**
377     * This method works according to the specification of implemented class.
378     * @see javax.net.ssl.SSLEngine#isInboundDone() method
379     * documentation for more information
380     */
381    @Override
382    public boolean isInboundDone() {
383        return isInboundDone || engine_was_closed;
384    }
385
386    /**
387     * This method works according to the specification of implemented class.
388     * @see javax.net.ssl.SSLEngine#isOutboundDone() method
389     * documentation for more information
390     */
391    @Override
392    public boolean isOutboundDone() {
393        return isOutboundDone;
394    }
395
396    /**
397     * Decodes one complete SSL/TLS record provided in the source buffer.
398     * If decoded record contained application data, this data will
399     * be placed in the destination buffers.
400     * For more information about TLS record fragmentation see
401     * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
402     * @param src source buffer containing SSL/TLS record.
403     * @param dsts destination buffers to place received application data.
404     * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int)
405     * method documentation for more information
406     */
407    @Override
408    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts,
409                                int offset, int length) throws SSLException {
410        if (engine_was_shutteddown) {
411            return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
412                    SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
413        }
414        if ((src == null) || (dsts == null)) {
415            throw new IllegalStateException(
416                    "Some of the input parameters are null");
417        }
418
419        if (!handshake_started) {
420            beginHandshake();
421        }
422
423        SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
424        // If is is initial handshake or connection closure stage,
425        // check if this call was made in spite of handshake status
426        if ((session == null || engine_was_closed) && (
427                    handshakeStatus.equals(
428                        SSLEngineResult.HandshakeStatus.NEED_WRAP) ||
429                    handshakeStatus.equals(
430                        SSLEngineResult.HandshakeStatus.NEED_TASK))) {
431            return new SSLEngineResult(
432                    getEngineStatus(), handshakeStatus, 0, 0);
433        }
434
435        if (src.remaining() < recordProtocol.getMinRecordSize()) {
436            return new SSLEngineResult(
437                    SSLEngineResult.Status.BUFFER_UNDERFLOW,
438                    getHandshakeStatus(), 0, 0);
439        }
440
441        try {
442            src.mark();
443            // check the destination buffers and count their capacity
444            int capacity = 0;
445            for (int i=offset; i<offset+length; i++) {
446                if (dsts[i] == null) {
447                    throw new IllegalStateException(
448                            "Some of the input parameters are null");
449                }
450                if (dsts[i].isReadOnly()) {
451                    throw new ReadOnlyBufferException();
452                }
453                capacity += dsts[i].remaining();
454            }
455            if (capacity < recordProtocol.getDataSize(src.remaining())) {
456                return new SSLEngineResult(
457                        SSLEngineResult.Status.BUFFER_OVERFLOW,
458                        getHandshakeStatus(), 0, 0);
459            }
460            recProtIS.setSourceBuffer(src);
461            // unwrap the record contained in source buffer, pass it
462            // to appropriate client protocol (alert, handshake, or app)
463            // and retrieve the type of unwrapped data
464            int type = recordProtocol.unwrap();
465            // process the data and return the result
466            switch (type) {
467                case ContentType.HANDSHAKE:
468                case ContentType.CHANGE_CIPHER_SPEC:
469                    if (handshakeProtocol.getStatus().equals(
470                            SSLEngineResult.HandshakeStatus.FINISHED)) {
471                        session = recordProtocol.getSession();
472                    }
473                    break;
474                case ContentType.APPLICATION_DATA:
475                    break;
476                case ContentType.ALERT:
477                    if (alertProtocol.isFatalAlert()) {
478                        alertProtocol.setProcessed();
479                        if (session != null) {
480                            session.invalidate();
481                        }
482                        String description = "Fatal alert received "
483                            + alertProtocol.getAlertDescription();
484                        shutdown();
485                        throw new SSLException(description);
486                    } else {
487                        if (logger != null) {
488                            logger.println("Warning allert has been received: "
489                                + alertProtocol.getAlertDescription());
490                        }
491                        switch(alertProtocol.getDescriptionCode()) {
492                            case AlertProtocol.CLOSE_NOTIFY:
493                                alertProtocol.setProcessed();
494                                close_notify_was_received = true;
495                                if (!close_notify_was_sent) {
496                                    closeOutbound();
497                                    closeInbound();
498                                } else {
499                                    closeInbound();
500                                    shutdown();
501                                }
502                                break;
503                            case AlertProtocol.NO_RENEGOTIATION:
504                                alertProtocol.setProcessed();
505                                if (session == null) {
506                                    // message received during the initial
507                                    // handshake
508                                    throw new AlertException(
509                                        AlertProtocol.HANDSHAKE_FAILURE,
510                                        new SSLHandshakeException(
511                                            "Received no_renegotiation "
512                                            + "during the initial handshake"));
513                                } else {
514                                    // just stop the handshake
515                                    handshakeProtocol.stop();
516                                }
517                                break;
518                            default:
519                                alertProtocol.setProcessed();
520                        }
521                    }
522                    break;
523            }
524            return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(),
525                    recProtIS.consumed(),
526                    // place the app. data (if any) into the dest. buffers
527                    // and get the number of produced bytes:
528                    appData.placeTo(dsts, offset, length));
529        } catch (BufferUnderflowException e) {
530            // there was not enought data ource buffer to make complete packet
531            src.reset();
532            return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
533                    getHandshakeStatus(), 0, 0);
534        } catch (AlertException e) {
535            // fatal alert occured
536            alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
537            engine_was_closed = true;
538            src.reset();
539            if (session != null) {
540                session.invalidate();
541            }
542            // shutdown work will be made after the alert will be sent
543            // to another peer (by wrap method)
544            throw e.getReason();
545        } catch (SSLException e) {
546            throw e;
547        } catch (IOException e) {
548            alertProtocol.alert(AlertProtocol.FATAL,
549                    AlertProtocol.INTERNAL_ERROR);
550            engine_was_closed = true;
551            // shutdown work will be made after the alert will be sent
552            // to another peer (by wrap method)
553            throw new SSLException(e.getMessage());
554        }
555    }
556
557    /**
558     * Encodes the application data into SSL/TLS record. If handshake status
559     * of the engine differs from NOT_HANDSHAKING the operation can work
560     * without consuming of the source data.
561     * For more information about TLS record fragmentation see
562     * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
563     * @param srcs the source buffers with application data to be encoded
564     * into SSL/TLS record.
565     * @param offset the offset in the destination buffers array pointing to
566     * the first buffer with the source data.
567     * @param len specifies the maximum number of buffers to be procesed.
568     * @param dst the destination buffer where encoded data will be placed.
569     * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method
570     * documentation for more information
571     */
572    @Override
573    public SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
574                            int len, ByteBuffer dst) throws SSLException {
575        if (engine_was_shutteddown) {
576            return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
577                    SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
578        }
579        if ((srcs == null) || (dst == null)) {
580            throw new IllegalStateException(
581                    "Some of the input parameters are null");
582        }
583        if (dst.isReadOnly()) {
584            throw new ReadOnlyBufferException();
585        }
586
587        if (!handshake_started) {
588            beginHandshake();
589        }
590
591        SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
592        // If it is an initial handshake or connection closure stage,
593        // check if this call was made in spite of handshake status
594        if ((session == null || engine_was_closed) && (
595                handshakeStatus.equals(
596                        SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
597                handshakeStatus.equals(
598                        SSLEngineResult.HandshakeStatus.NEED_TASK))) {
599            return new SSLEngineResult(
600                    getEngineStatus(), handshakeStatus, 0, 0);
601        }
602
603        int capacity = dst.remaining();
604        int produced = 0;
605
606        if (alertProtocol.hasAlert()) {
607            // we have an alert to be sent
608            if (capacity < recordProtocol.getRecordSize(2)) {
609                return new SSLEngineResult(
610                        SSLEngineResult.Status.BUFFER_OVERFLOW,
611                        handshakeStatus, 0, 0);
612            }
613            byte[] alert_data = alertProtocol.wrap();
614            // place the alert record into destination
615            dst.put(alert_data);
616            if (alertProtocol.isFatalAlert()) {
617                alertProtocol.setProcessed();
618                if (session != null) {
619                    session.invalidate();
620                }
621                // fatal alert has been sent, so shut down the engine
622                shutdown();
623                return new SSLEngineResult(
624                        SSLEngineResult.Status.CLOSED,
625                        SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
626                        0, alert_data.length);
627            } else {
628                alertProtocol.setProcessed();
629                // check if the works on this engine have been done
630                if (close_notify_was_sent && close_notify_was_received) {
631                    shutdown();
632                    return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
633                            SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
634                            0, alert_data.length);
635                }
636                return new SSLEngineResult(
637                        getEngineStatus(),
638                        getHandshakeStatus(),
639                        0, alert_data.length);
640            }
641        }
642
643        if (capacity < recordProtocol.getMinRecordSize()) {
644            if (logger != null) {
645                logger.println("Capacity of the destination("
646                        +capacity+") < MIN_PACKET_SIZE("
647                        +recordProtocol.getMinRecordSize()+")");
648            }
649            return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
650                        handshakeStatus, 0, 0);
651        }
652
653        try {
654            if (!handshakeStatus.equals(
655                        SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
656                // so we wraps application data
657                dataStream.setSourceBuffers(srcs, offset, len);
658                if ((capacity < SSLRecordProtocol.MAX_SSL_PACKET_SIZE) &&
659                    (capacity < recordProtocol.getRecordSize(
660                                                 dataStream.available()))) {
661                    if (logger != null) {
662                        logger.println("The destination buffer("
663                                +capacity+") can not take the resulting packet("
664                                + recordProtocol.getRecordSize(
665                                    dataStream.available())+")");
666                    }
667                    return new SSLEngineResult(
668                            SSLEngineResult.Status.BUFFER_OVERFLOW,
669                            handshakeStatus, 0, 0);
670                }
671                if (remaining_wrapped_data == null) {
672                    remaining_wrapped_data =
673                        recordProtocol.wrap(ContentType.APPLICATION_DATA,
674                                dataStream);
675                }
676                if (capacity < remaining_wrapped_data.length) {
677                    // It should newer happen because we checked the destination
678                    // buffer size, but there is a possibility
679                    // (if dest buffer was filled outside)
680                    // so we just remember the data into remaining_wrapped_data
681                    // and will enclose it during the the next call
682                    return new SSLEngineResult(
683                            SSLEngineResult.Status.BUFFER_OVERFLOW,
684                            handshakeStatus, dataStream.consumed(), 0);
685                } else {
686                    dst.put(remaining_wrapped_data);
687                    produced = remaining_wrapped_data.length;
688                    remaining_wrapped_data = null;
689                    return new SSLEngineResult(getEngineStatus(),
690                            handshakeStatus, dataStream.consumed(), produced);
691                }
692            } else {
693                if (remaining_hsh_data == null) {
694                    remaining_hsh_data = handshakeProtocol.wrap();
695                }
696                if (capacity < remaining_hsh_data.length) {
697                    // It should newer happen because we checked the destination
698                    // buffer size, but there is a possibility
699                    // (if dest buffer was filled outside)
700                    // so we just remember the data into remaining_hsh_data
701                    // and will enclose it during the the next call
702                    return new SSLEngineResult(
703                            SSLEngineResult.Status.BUFFER_OVERFLOW,
704                            handshakeStatus, 0, 0);
705                } else {
706                    dst.put(remaining_hsh_data);
707                    produced = remaining_hsh_data.length;
708                    remaining_hsh_data = null;
709
710                    handshakeStatus = handshakeProtocol.getStatus();
711                    if (handshakeStatus.equals(
712                            SSLEngineResult.HandshakeStatus.FINISHED)) {
713                        session = recordProtocol.getSession();
714                    }
715                }
716                return new SSLEngineResult(
717                        getEngineStatus(), getHandshakeStatus(), 0, produced);
718            }
719        } catch (AlertException e) {
720            // fatal alert occured
721            alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
722            engine_was_closed = true;
723            if (session != null) {
724                session.invalidate();
725            }
726            // shutdown work will be made after the alert will be sent
727            // to another peer (by wrap method)
728            throw e.getReason();
729        }
730    }
731
732    // Shutdownes the engine and makes all cleanup work.
733    private void shutdown() {
734        engine_was_closed = true;
735        engine_was_shutteddown = true;
736        isOutboundDone = true;
737        isInboundDone = true;
738        if (handshake_started) {
739            alertProtocol.shutdown();
740            alertProtocol = null;
741            handshakeProtocol.shutdown();
742            handshakeProtocol = null;
743            recordProtocol.shutdown();
744            recordProtocol = null;
745        }
746    }
747
748
749    private SSLEngineResult.Status getEngineStatus() {
750        return (engine_was_closed)
751            ? SSLEngineResult.Status.CLOSED
752            : SSLEngineResult.Status.OK;
753    }
754}
755