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