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