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