View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.filter.ssl;
    21
    22  import java.net.InetSocketAddress;
    23  import java.nio.ByteBuffer;
    24  import java.util.Queue;
    25  import java.util.concurrent.ConcurrentLinkedQueue;
    26
    27  import javax.net.ssl.SSLEngine;
    28  import javax.net.ssl.SSLEngineResult;
    29  import javax.net.ssl.SSLException;
    30  import javax.net.ssl.SSLHandshakeException;
    31  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
    32  import javax.net.ssl.SSLEngineResult.Status;
    33
    34  import org.apache.mina.core.buffer.IoBuffer;
    35  import org.apache.mina.core.filterchain.IoFilterEvent;
    36  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
    37  import org.apache.mina.core.future.DefaultWriteFuture;
    38  import org.apache.mina.core.future.WriteFuture;
    39  import org.apache.mina.core.session.IoEventType;
    40  import org.apache.mina.core.session.IoSession;
    41  import org.apache.mina.core.write.DefaultWriteRequest;
    42  import org.apache.mina.core.write.WriteRequest;
    43  import org.slf4j.Logger;
    44  import org.slf4j.LoggerFactory;
    45
    46  /**
    47   * A helper class using the SSLEngine API to decrypt/encrypt data.
    48   * <p/>
    49   * Each connection has a SSLEngine that is used through the lifetime of the connection.
    50   * We allocate buffers for use as the outbound and inbound network buffers.
    51   * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
    52   * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
    53   * <p/>
    54   * This class is not to be used by any client, it's closely associated with the SSL Filter.
    55   * None of its methods are public as they should not be used by any other class but from
    56   * the SslFilter class, in the same package
    57   *
    58   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    59   */
    60  /** No qualifier*/
    61  class SslHandler {
    62      /** A logger for this class */
    63      private final static Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
    64
    65      /** The SSL Filter which has created this handler */
    66      private final SslFilter sslFilter;
    67
    68      /** The current session */
    69      private final IoSession session;
    70
    71      private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
    72
    73      private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
    74
    75      /** A queue used to stack all the incoming data until the SSL session is established */
    76      private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<IoFilterEvent>();
    77
    78      private SSLEngine sslEngine;
    79
    80      /**
    81       * Encrypted data from the net
    82       */
    83      private IoBuffer inNetBuffer;
    84
    85      /**
    86       * Encrypted data to be written to the net
    87       */
    88      private IoBuffer outNetBuffer;
    89
    90      /**
    91       * Application cleartext data to be read by application
    92       */
    93      private IoBuffer appBuffer;
    94
    95      /**
    96       * Empty buffer used during initial handshake and close operations
    97       */
    98      private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
    99
    100     private SSLEngineResult.HandshakeStatus handshakeStatus;
    101
    102     /**
    103      * A flag set to true when the first SSL handshake has been completed
    104      * This is used to avoid sending a notification to the application handler
    105      * when we switch to a SECURE or UNSECURE session.
    106      */
    107     private boolean firstSSLNegociation;
    108
    109     /** A flag set to true when a SSL Handshake has been completed */
    110     private boolean handshakeComplete;
    111
    112     /** A flag used to indicate to the SslFilter that the buffer
    113      * it will write is already encrypted (this will be the case
    114      * for data being produced during the handshake). */
    115     private boolean writingEncryptedData;
    116
    117     /**
    118      * Create a new SSL Handler, and initialize it.
    119      *
    120      * @param sslContext
    121      * @throws SSLException
    122      */
    123     /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) throws SSLException {
    124         this.sslFilter = sslFilter;
    125         this.session = session;
    126     }
    127
    128     /**
    129      * Initialize the SSL handshake.
    130      *
    131      * @throws SSLException If the underlying SSLEngine handshake initialization failed
    132      */
    133     /* no qualifier */void init() throws SSLException {
    134         if (sslEngine != null) {
    135             // We already have a SSL engine created, no need to create a new one
    136             return;
    137         }
    138
    139         LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
    140
    141         InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
    142
    143         // Create the SSL engine here
    144         if (peer == null) {
    145             sslEngine = sslFilter.sslContext.createSSLEngine();
    146         } else {
    147             sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
    148         }
    149
    150         // Initialize the engine in client mode if necessary
    151         sslEngine.setUseClientMode(sslFilter.isUseClientMode());
    152
    153         // Initialize the different SslEngine modes
    154         if (!sslEngine.getUseClientMode()) {
    155             // Those parameters are only valid when in server mode
    156             if (sslFilter.isWantClientAuth()) {
    157                 sslEngine.setWantClientAuth(true);
    158             }
    159
    160             if (sslFilter.isNeedClientAuth()) {
    161                 sslEngine.setNeedClientAuth(true);
    162             }
    163         }
    164
    165         // Set the cipher suite to use by this SslEngine instance
    166         if (sslFilter.getEnabledCipherSuites() != null) {
    167             sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
    168         }
    169
    170         // Set the list of enabled protocols
    171         if (sslFilter.getEnabledProtocols() != null) {
    172             sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
    173         }
    174
    175         // TODO : we may not need to call this method...
    176         // However, if we don't call it here, the tests are failing. Why?
    177         sslEngine.beginHandshake();
    178
    179         handshakeStatus = sslEngine.getHandshakeStatus();
    180
    181         // Default value
    182         writingEncryptedData = false;
    183
    184         // We haven't yet started a SSL negotiation
    185         // set the flags accordingly
    186         firstSSLNegociation = true;
    187         handshakeComplete = false;
    188
    189         if (LOGGER.isDebugEnabled()) {
    190             LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
    191         }
    192     }
    193
    194     /**
    195      * Release allocated buffers.
    196      */
    197     /* no qualifier */void destroy() {
    198         if (sslEngine == null) {
    199             return;
    200         }
    201
    202         // Close inbound and flush all remaining data if available.
    203         try {
    204             sslEngine.closeInbound();
    205         } catch (SSLException e) {
    206             LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
    207         }
    208
    209         if (outNetBuffer != null) {
    210             outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
    211         } else {
    212             createOutNetBuffer(0);
    213         }
    214         try {
    215             do {
    216                 outNetBuffer.clear();
    217             } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
    218         } catch (SSLException e) {
    219             // Ignore.
    220         } finally {
    221             destroyOutNetBuffer();
    222         }
    223
    224         sslEngine.closeOutbound();
    225         sslEngine = null;
    226
    227         preHandshakeEventQueue.clear();
    228     }
    229
    230     private void destroyOutNetBuffer() {
    231         outNetBuffer.free();
    232         outNetBuffer = null;
    233     }
    234
    235     /**
    236      * @return The SSL filter which has created this handler
    237      */
    238     /* no qualifier */SslFilter getSslFilter() {
    239         return sslFilter;
    240     }
    241
    242     /* no qualifier */IoSession getSession() {
    243         return session;
    244     }
    245
    246     /**
    247      * Check if we are writing encrypted data.
    248      */
    249     /* no qualifier */boolean isWritingEncryptedData() {
    250         return writingEncryptedData;
    251     }
    252
    253     /**
    254      * Check if handshake is completed.
    255      */
    256     /* no qualifier */boolean isHandshakeComplete() {
    257         return handshakeComplete;
    258     }
    259
    260     /* no qualifier */boolean isInboundDone() {
    261         return sslEngine == null || sslEngine.isInboundDone();
    262     }
    263
    264     /* no qualifier */boolean isOutboundDone() {
    265         return sslEngine == null || sslEngine.isOutboundDone();
    266     }
    267
    268     /**
    269      * Check if there is any need to complete handshake.
    270      */
    271     /* no qualifier */boolean needToCompleteHandshake() {
    272         return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
    273     }
    274
    275     /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
    276         preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
    277     }
    278
    279     /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
    280         IoFilterEvent scheduledWrite;
    281
    282         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
    283             sslFilter
    284                     .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
    285         }
    286     }
    287
    288     /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
    289         filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
    290     }
    291
    292     /**
    293      * Push the newly received data into a queue, waiting for the SSL session
    294      * to be fully established
    295      *
    296      * @param nextFilter The next filter to call
    297      * @param message The incoming data
    298      */
    299     /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
    300         messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
    301     }
    302
    303     /* no qualifier */void flushScheduledEvents() {
    304         // Fire events only when no lock is hold for this handler.
    305         if (Thread.holdsLock(this)) {
    306             return;
    307         }
    308
    309         IoFilterEvent event;
    310
    311         // We need synchronization here inevitably because filterWrite can be
    312         // called simultaneously and cause 'bad record MAC' integrity error.
    313         synchronized (this) {
    314             while ((event = filterWriteEventQueue.poll()) != null) {
    315                 NextFilter nextFilter = event.getNextFilter();
    316                 nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
    317             }
    318         }
    319
    320         while ((event = messageReceivedEventQueue.poll()) != null) {
    321             NextFilter nextFilter = event.getNextFilter();
    322             nextFilter.messageReceived(session, event.getParameter());
    323         }
    324     }
    325
    326     /**
    327      * Call when data are read from net. It will perform the initial hanshake or decrypt
    328      * the data if SSL has been initialiaed.
    329      *
    330      * @param buf buffer to decrypt
    331      * @param nextFilter Next filter in chain
    332      * @throws SSLException on errors
    333      */
    334     /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
    335         if (LOGGER.isDebugEnabled()) {
    336             if (!isOutboundDone()) {
    337                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
    338             } else {
    339                 LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
    340             }
    341         }
    342
    343         // append buf to inNetBuffer
    344         if (inNetBuffer == null) {
    345             inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
    346         }
    347
    348         inNetBuffer.put(buf);
    349
    350         if (!handshakeComplete) {
    351             handshake(nextFilter);
    352         } else {
    353             // Prepare the net data for reading.
    354             inNetBuffer.flip();
    355
    356             if (!inNetBuffer.hasRemaining()) {
    357                 return;
    358             }
    359
    360             SSLEngineResult res = unwrap();
    361
    362             // prepare to be written again
    363             if (inNetBuffer.hasRemaining()) {
    364                 inNetBuffer.compact();
    365             } else {
    366                 inNetBuffer = null;
    367             }
    368
    369             checkStatus(res);
    370
    371             renegotiateIfNeeded(nextFilter, res);
    372         }
    373
    374         if (isInboundDone()) {
    375             // Rewind the MINA buffer if not all data is processed and inbound
    376             // is finished.
    377             int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
    378             buf.position(buf.position() - inNetBufferPosition);
    379             inNetBuffer = null;
    380         }
    381     }
    382
    383     /**
    384      * Get decrypted application data.
    385      *
    386      * @return buffer with data
    387      */
    388     /* no qualifier */IoBuffer fetchAppBuffer() {
    389         IoBuffer appBuffer = this.appBuffer.flip();
    390         this.appBuffer = null;
    391         return appBuffer;
    392     }
    393
    394     /**
    395      * Get encrypted data to be sent.
    396      *
    397      * @return buffer with data
    398      */
    399     /* no qualifier */IoBuffer fetchOutNetBuffer() {
    400         IoBuffer answer = outNetBuffer;
    401         if (answer == null) {
    402             return emptyBuffer;
    403         }
    404
    405         outNetBuffer = null;
    406         return answer.shrink();
    407     }
    408
    409     /**
    410      * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
    411      *
    412      * @param src
    413      *            data to encrypt
    414      * @throws SSLException
    415      *             on errors
    416      */
    417     /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
    418         if (!handshakeComplete) {
    419             throw new IllegalStateException();
    420         }
    421
    422         if (!src.hasRemaining()) {
    423             if (outNetBuffer == null) {
    424                 outNetBuffer = emptyBuffer;
    425             }
    426             return;
    427         }
    428
    429         createOutNetBuffer(src.remaining());
    430
    431         // Loop until there is no more data in src
    432         while (src.hasRemaining()) {
    433
    434             SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
    435             if (result.getStatus() == SSLEngineResult.Status.OK) {
    436                 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
    437                     doTasks();
    438                 }
    439             } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
    440                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
    441                 outNetBuffer.limit(outNetBuffer.capacity());
    442             } else {
    443                 throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
    444                         + "outNetBuffer: " + outNetBuffer);
    445             }
    446         }
    447
    448         outNetBuffer.flip();
    449     }
    450
    451     /**
    452      * Start SSL shutdown process.
    453      *
    454      * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
    455      *         shutdown process is already finished.
    456      * @throws SSLException
    457      *             on errors
    458      */
    459     /* no qualifier */boolean closeOutbound() throws SSLException {
    460         if (sslEngine == null || sslEngine.isOutboundDone()) {
    461             return false;
    462         }
    463
    464         sslEngine.closeOutbound();
    465
    466         createOutNetBuffer(0);
    467         SSLEngineResult result;
    468         for (;;) {
    469             result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
    470             if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
    471                 outNetBuffer.capacity(outNetBuffer.capacity() << 1);
    472                 outNetBuffer.limit(outNetBuffer.capacity());
    473             } else {
    474                 break;
    475             }
    476         }
    477
    478         if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
    479             throw new SSLException("Improper close state: " + result);
    480         }
    481         outNetBuffer.flip();
    482         return true;
    483     }
    484
    485     /**
    486      * @param res
    487      * @throws SSLException
    488      */
    489     private void checkStatus(SSLEngineResult res) throws SSLException {
    490
    491         SSLEngineResult.Status status = res.getStatus();
    492
    493         /*
    494          * The status may be:
    495          * OK - Normal operation
    496          * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
    497          * packet size.
    498          * UNDERFLOW - Need to read more data from the socket. It's normal.
    499          * CLOSED - The other peer closed the socket. Also normal.
    500          */
    501         if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
    502             throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
    503                     + "appBuffer: " + appBuffer);
    504         }
    505     }
    506
    507     /**
    508      * Perform any handshaking processing.
    509      */
    510     /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
    511         for (;;) {
    512             switch (handshakeStatus) {
    513             case FINISHED:
    514             case NOT_HANDSHAKING:
    515                 if (LOGGER.isDebugEnabled()) {
    516                     LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
    517                 }
    518
    519                 session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
    520                 handshakeComplete = true;
    521
    522                 // Send the SECURE message only if it's the first SSL handshake
    523                 if (firstSSLNegociation && session.containsAttribute(SslFilter.USE_NOTIFICATION)) {
    524                     // SESSION_SECURED is fired only when it's the first handshake
    525                     firstSSLNegociation = false;
    526                     scheduleMessageReceived(nextFilter, SslFilter.SESSION_SECURED);
    527                 }
    528
    529                 if (LOGGER.isDebugEnabled()) {
    530                     if (!isOutboundDone()) {
    531                         LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
    532                     } else {
    533                         LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
    534                     }
    535                 }
    536
    537                 return;
    538
    539             case NEED_TASK:
    540                 if (LOGGER.isDebugEnabled()) {
    541                     LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
    542                 }
    543
    544                 handshakeStatus = doTasks();
    545                 break;
    546
    547             case NEED_UNWRAP:
    548                 if (LOGGER.isDebugEnabled()) {
    549                     LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
    550                 }
    551                 // we need more data read
    552                 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
    553
    554                 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
    555                         && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
    556                     // We need more data or the session is closed
    557                     return;
    558                 }
    559
    560                 break;
    561
    562             case NEED_WRAP:
    563                 if (LOGGER.isDebugEnabled()) {
    564                     LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
    565                 }
    566
    567                 // First make sure that the out buffer is completely empty.
    568                 // Since we
    569                 // cannot call wrap with data left on the buffer
    570                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
    571                     return;
    572                 }
    573
    574                 SSLEngineResult result;
    575                 createOutNetBuffer(0);
    576
    577                 for (;;) {
    578                     result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
    579                     if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
    580                         outNetBuffer.capacity(outNetBuffer.capacity() << 1);
    581                         outNetBuffer.limit(outNetBuffer.capacity());
    582                     } else {
    583                         break;
    584                     }
    585                 }
    586
    587                 outNetBuffer.flip();
    588                 handshakeStatus = result.getHandshakeStatus();
    589                 writeNetBuffer(nextFilter);
    590                 break;
    591
    592             default:
    593                 String msg = "Invalid Handshaking State" + handshakeStatus
    594                         + " while processing the Handshake for session " + session.getId();
    595                 LOGGER.error(msg);
    596                 throw new IllegalStateException(msg);
    597             }
    598         }
    599     }
    600
    601     private void createOutNetBuffer(int expectedRemaining) {
    602         // SSLEngine requires us to allocate unnecessarily big buffer
    603         // even for small data. *Shrug*
    604         int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
    605
    606         if (outNetBuffer != null) {
    607             outNetBuffer.capacity(capacity);
    608         } else {
    609             outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
    610         }
    611     }
    612
    613     /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
    614         // Check if any net data needed to be writen
    615         if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
    616             // no; bail out
    617             return null;
    618         }
    619
    620         // set flag that we are writing encrypted data
    621         // (used in SSLFilter.filterWrite())
    622         writingEncryptedData = true;
    623
    624         // write net data
    625         WriteFuture writeFuture = null;
    626
    627         try {
    628             IoBuffer writeBuffer = fetchOutNetBuffer();
    629             writeFuture = new DefaultWriteFuture(session);
    630             sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
    631
    632             // loop while more writes required to complete handshake
    633             while (needToCompleteHandshake()) {
    634                 try {
    635                     handshake(nextFilter);
    636                 } catch (SSLException ssle) {
    637                     SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
    638                     newSsle.initCause(ssle);
    639                     throw newSsle;
    640                 }
    641
    642                 IoBuffer outNetBuffer = fetchOutNetBuffer();
    643                 if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
    644                     writeFuture = new DefaultWriteFuture(session);
    645                     sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(outNetBuffer, writeFuture));
    646                 }
    647             }
    648         } finally {
    649             writingEncryptedData = false;
    650         }
    651
    652         return writeFuture;
    653     }
    654
    655     private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
    656         // Prepare the net data for reading.
    657         if (inNetBuffer != null) {
    658             inNetBuffer.flip();
    659         }
    660
    661         if (inNetBuffer == null || !inNetBuffer.hasRemaining()) {
    662             // Need more data.
    663             return SSLEngineResult.Status.BUFFER_UNDERFLOW;
    664         }
    665
    666         SSLEngineResult res = unwrap();
    667         handshakeStatus = res.getHandshakeStatus();
    668
    669         checkStatus(res);
    670
    671         // If handshake finished, no data was produced, and the status is still
    672         // ok, try to unwrap more
    673         if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED && res.getStatus() == SSLEngineResult.Status.OK
    674                 && inNetBuffer.hasRemaining()) {
    675             res = unwrap();
    676
    677             // prepare to be written again
    678             if (inNetBuffer.hasRemaining()) {
    679                 inNetBuffer.compact();
    680             } else {
    681                 inNetBuffer = null;
    682             }
    683
    684             renegotiateIfNeeded(nextFilter, res);
    685         } else {
    686             // prepare to be written again
    687             if (inNetBuffer.hasRemaining()) {
    688                 inNetBuffer.compact();
    689             } else {
    690                 inNetBuffer = null;
    691             }
    692         }
    693
    694         return res.getStatus();
    695     }
    696
    697     private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
    698         if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
    699                 && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
    700                 && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
    701             // Renegotiation required.
    702             handshakeComplete = false;
    703             handshakeStatus = res.getHandshakeStatus();
    704             handshake(nextFilter);
    705         }
    706     }
    707
    708     /**
    709      * Decrypt the incoming buffer and move the decrypted data to an
    710      * application buffer.
    711      */
    712     private SSLEngineResult unwrap() throws SSLException {
    713         // We first have to create the application buffer if it does not exist
    714         if (appBuffer == null) {
    715             appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
    716         } else {
    717             // We already have one, just add the new data into it
    718             appBuffer.expand(inNetBuffer.remaining());
    719         }
    720
    721         SSLEngineResult res;
    722
    723         Status status = null;
    724         HandshakeStatus handshakeStatus = null;
    725
    726         do {
    727             // Decode the incoming data
    728             res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
    729             status = res.getStatus();
    730
    731             // We can be processing the Handshake
    732             handshakeStatus = res.getHandshakeStatus();
    733
    734             if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
    735                 // We have to grow the target buffer, it's too small.
    736                 // Then we can call the unwrap method again
    737                 appBuffer.capacity(appBuffer.capacity() << 1);
    738                 appBuffer.limit(appBuffer.capacity());
    739                 continue;
    740             }
    741         } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
    742                 && ((handshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
    743
    744         return res;
    745     }
    746
    747     /**
    748      * Do all the outstanding handshake tasks in the current Thread.
    749      */
    750     private SSLEngineResult.HandshakeStatus doTasks() {
    751         /*
    752          * We could run this in a separate thread, but I don't see the need for
    753          * this when used from SSLFilter. Use thread filters in MINA instead?
    754          */
    755         Runnable runnable;
    756         while ((runnable = sslEngine.getDelegatedTask()) != null) {
    757             // TODO : we may have to use a thread pool here to improve the
    758             // performances
    759             runnable.run();
    760         }
    761         return sslEngine.getHandshakeStatus();
    762     }
    763
    764     /**
    765      * Creates a new MINA buffer that is a deep copy of the remaining bytes in
    766      * the given buffer (between index buf.position() and buf.limit())
    767      *
    768      * @param src
    769      *            the buffer to copy
    770      * @return the new buffer, ready to read from
    771      */
    772     /* no qualifier */static IoBuffer copy(ByteBuffer src) {
    773         IoBuffer copy = IoBuffer.allocate(src.remaining());
    774         copy.put(src);
    775         copy.flip();
    776         return copy;
    777     }
    778
    779     public String toString() {
    780         StringBuilder sb = new StringBuilder();
    781
    782         sb.append("SSLStatus <");
    783
    784         if (handshakeComplete) {
    785             sb.append("SSL established");
    786         } else {
    787             sb.append("Processing Handshake").append("; ");
    788             sb.append("Status : ").append(handshakeStatus).append("; ");
    789         }
    790
    791         sb.append(", ");
    792         sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
    793         sb.append(">");
    794         return sb.toString();
    795     }
    796
    797 }