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