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