Home | History | Annotate | Download | only in conscrypt
      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 
     18 package org.conscrypt;
     19 
     20 import java.io.IOException;
     21 import java.nio.BufferUnderflowException;
     22 import java.nio.ByteBuffer;
     23 import java.nio.ReadOnlyBufferException;
     24 import javax.net.ssl.SSLEngine;
     25 import javax.net.ssl.SSLEngineResult;
     26 import javax.net.ssl.SSLException;
     27 import javax.net.ssl.SSLHandshakeException;
     28 import javax.net.ssl.SSLSession;
     29 
     30 /**
     31  * Implementation of SSLEngine.
     32  * @see javax.net.ssl.SSLEngine class documentation for more information.
     33  */
     34 public 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