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.support;
    21
    22  import java.nio.ByteBuffer;
    23  import java.util.LinkedList;
    24  import java.util.Queue;
    25  import java.util.concurrent.ConcurrentLinkedQueue;
    26
    27  import javax.net.ssl.SSLContext;
    28  import javax.net.ssl.SSLEngine;
    29  import javax.net.ssl.SSLEngineResult;
    30  import javax.net.ssl.SSLException;
    31  import javax.net.ssl.SSLHandshakeException;
    32  import javax.net.ssl.SSLSession;
    33
    34  import org.apache.mina.common.IoSession;
    35  import org.apache.mina.common.WriteFuture;
    36  import org.apache.mina.common.IoFilter.NextFilter;
    37  import org.apache.mina.common.IoFilter.WriteRequest;
    38  import org.apache.mina.common.support.DefaultWriteFuture;
    39  import org.apache.mina.filter.SSLFilter;
    40  import org.apache.mina.util.SessionLog;
    41
    42  /**
    43   * A helper class using the SSLEngine API to decrypt/encrypt data.
    44   * <p>
    45   * Each connection has a SSLEngine that is used through the lifetime of the connection.
    46   * We allocate byte buffers for use as the outbound and inbound network buffers.
    47   * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
    48   * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
    49   *
    50   * @author The Apache Directory Project (mina-dev@directory.apache.org)
    51   * @version $Rev: 561232 $, $Date: 2007-07-31 07:19:37 +0200 (Tue, 31 Jul 2007) $
    52   */
    53  public class SSLHandler {
    54      private final SSLFilter parent;
    55
    56      private final SSLContext ctx;
    57
    58      private final IoSession session;
    59
    60      private final Queue<Event> preHandshakeEventQueue = new LinkedList<Event>();
    61
    62      private final Queue<Event> filterWriteEventQueue = new ConcurrentLinkedQueue<Event>();
    63
    64      private final Queue<Event> messageReceivedEventQueue = new ConcurrentLinkedQueue<Event>();
    65
    66      private SSLEngine sslEngine;
    67
    68      /**
    69       * Encrypted data from the net
    70       */
    71      private ByteBuffer inNetBuffer;
    72
    73      /**
    74       * Encrypted data to be written to the net
    75       */
    76      private ByteBuffer outNetBuffer;
    77
    78      /**
    79       * Applicaton cleartext data to be read by application
    80       */
    81      private ByteBuffer appBuffer;
    82
    83      /**
    84       * Empty buffer used during initial handshake and close operations
    85       */
    86      private final ByteBuffer hsBB = ByteBuffer.allocate(0);
    87
    88      /**
    89       * Handshake status
    90       */
    91      private SSLEngineResult.HandshakeStatus handshakeStatus;
    92
    93      private boolean initialHandshakeComplete;
    94
    95      /**
    96       * Handshake complete?
    97       */
    98      private boolean handshakeComplete;
    99
    100     private boolean writingEncryptedData;
    101
    102     /**
    103      * Constuctor.
    104      *
    105      * @param sslc
    106      * @throws SSLException
    107      */
    108     public SSLHandler(SSLFilter parent, SSLContext sslc, IoSession session)
    109             throws SSLException {
    110         this.parent = parent;
    111         this.session = session;
    112         this.ctx = sslc;
    113         init();
    114     }
    115
    116     public void init() throws SSLException {
    117         if (sslEngine != null) {
    118             return;
    119         }
    120
    121         sslEngine = ctx.createSSLEngine();
    122         sslEngine.setUseClientMode(parent.isUseClientMode());
    123
    124         if (parent.isWantClientAuth()) {
    125             sslEngine.setWantClientAuth(true);
    126         }
    127
    128         if (parent.isNeedClientAuth()) {
    129             sslEngine.setNeedClientAuth(true);
    130         }
    131
    132         if (parent.getEnabledCipherSuites() != null) {
    133             sslEngine.setEnabledCipherSuites(parent.getEnabledCipherSuites());
    134         }
    135
    136         if (parent.getEnabledProtocols() != null) {
    137             sslEngine.setEnabledProtocols(parent.getEnabledProtocols());
    138         }
    139
    140         sslEngine.beginHandshake();
    141         handshakeStatus = sslEngine.getHandshakeStatus();//SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
    142         handshakeComplete = false;
    143         initialHandshakeComplete = false;
    144
    145         SSLByteBufferPool.initiate(sslEngine);
    146
    147         appBuffer = SSLByteBufferPool.getApplicationBuffer();
    148
    149         inNetBuffer = SSLByteBufferPool.getPacketBuffer();
    150         outNetBuffer = SSLByteBufferPool.getPacketBuffer();
    151         outNetBuffer.position(0);
    152         outNetBuffer.limit(0);
    153
    154         writingEncryptedData = false;
    155     }
    156
    157     /**
    158      * Release allocated ByteBuffers.
    159      */
    160     public void destroy() {
    161         if (sslEngine == null) {
    162             return;
    163         }
    164
    165         // Close inbound and flush all remaining data if available.
    166         try {
    167             sslEngine.closeInbound();
    168         } catch (SSLException e) {
    169             SessionLog.debug(session,
    170                     "Unexpected exception from SSLEngine.closeInbound().", e);
    171         }
    172
    173         try {
    174             do {
    175                 outNetBuffer.clear();
    176             } while (sslEngine.wrap(hsBB, outNetBuffer).bytesProduced() > 0);
    177         } catch (SSLException e) {
    178             SessionLog.debug(session,
    179                     "Unexpected exception from SSLEngine.wrap().", e);
    180         }
    181         sslEngine.closeOutbound();
    182         sslEngine = null;
    183
    184         SSLByteBufferPool.release(appBuffer);
    185         SSLByteBufferPool.release(inNetBuffer);
    186         SSLByteBufferPool.release(outNetBuffer);
    187         preHandshakeEventQueue.clear();
    188     }
    189
    190     public SSLFilter getParent() {
    191         return parent;
    192     }
    193
    194     public IoSession getSession() {
    195         return session;
    196     }
    197
    198     /**
    199      * Check we are writing encrypted data.
    200      */
    201     public boolean isWritingEncryptedData() {
    202         return writingEncryptedData;
    203     }
    204
    205     /**
    206      * Check if handshake is completed.
    207      */
    208     public boolean isHandshakeComplete() {
    209         return handshakeComplete;
    210     }
    211
    212     public boolean isInboundDone() {
    213         return sslEngine == null || sslEngine.isInboundDone();
    214     }
    215
    216     public boolean isOutboundDone() {
    217         return sslEngine == null || sslEngine.isOutboundDone();
    218     }
    219
    220     /**
    221      * Check if there is any need to complete handshake.
    222      */
    223     public boolean needToCompleteHandshake() {
    224         return (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone());
    225     }
    226
    227     public void schedulePreHandshakeWriteRequest(NextFilter nextFilter,
    228             WriteRequest writeRequest) {
    229         preHandshakeEventQueue.offer(new Event(EventType.FILTER_WRITE,
    230                 nextFilter, writeRequest));
    231     }
    232
    233     public void flushPreHandshakeEvents() throws SSLException {
    234         Event scheduledWrite;
    235
    236         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
    237             if (SessionLog.isDebugEnabled(session)) {
    238                 SessionLog.debug(session, " Flushing buffered write request: "
    239                         + scheduledWrite.data);
    240             }
    241             parent.filterWrite(scheduledWrite.nextFilter, session,
    242                     (WriteRequest) scheduledWrite.data);
    243         }
    244     }
    245
    246     public void scheduleFilterWrite(NextFilter nextFilter,
    247             WriteRequest writeRequest) {
    248         filterWriteEventQueue.offer(new Event(EventType.FILTER_WRITE,
    249                 nextFilter, writeRequest));
    250     }
    251
    252     public void scheduleMessageReceived(NextFilter nextFilter,
    253             Object message) {
    254         messageReceivedEventQueue.offer(new Event(EventType.RECEIVED, nextFilter,
    255                 message));
    256     }
    257
    258     public void flushScheduledEvents() {
    259         // Fire events only when no lock is hold for this handler.
    260         if (Thread.holdsLock(this)) {
    261             return;
    262         }
    263
    264         Event e;
    265
    266         // We need synchronization here inevitably because filterWrite can be
    267         // called simultaneously and cause 'bad record MAC' integrity error.
    268         synchronized (this) {
    269             while ((e = filterWriteEventQueue.poll()) != null) {
    270                 e.nextFilter.filterWrite(session, (WriteRequest) e.data);
    271             }
    272         }
    273
    274         while ((e = messageReceivedEventQueue.poll()) != null) {
    275             e.nextFilter.messageReceived(session, e.data);
    276         }
    277     }
    278
    279     /**
    280      * Call when data read from net. Will perform inial hanshake or decrypt provided
    281      * Buffer.
    282      * Decrytpted data reurned by getAppBuffer(), if any.
    283      *
    284      * @param buf buffer to decrypt
    285      * @throws SSLException on errors
    286      */
    287     public void messageReceived(NextFilter nextFilter, ByteBuffer buf)
    288             throws SSLException {
    289         if (buf.limit() > inNetBuffer.remaining()) {
    290             // We have to expand inNetBuffer
    291             inNetBuffer = SSLByteBufferPool.expandBuffer(inNetBuffer,
    292                     inNetBuffer.capacity() + (buf.limit() * 2));
    293             // We also expand app. buffer (twice the size of in net. buffer)
    294             appBuffer = SSLByteBufferPool.expandBuffer(appBuffer, inNetBuffer
    295                     .capacity() * 2);
    296             if (SessionLog.isDebugEnabled(session)) {
    297                 SessionLog.debug(session, " expanded inNetBuffer:"
    298                         + inNetBuffer);
    299                 SessionLog.debug(session, " expanded appBuffer:" + appBuffer);
    300             }
    301         }
    302
    303         // append buf to inNetBuffer
    304         inNetBuffer.put(buf);
    305         if (!handshakeComplete) {
    306             handshake(nextFilter);
    307         } else {
    308             decrypt(nextFilter);
    309         }
    310
    311         if (isInboundDone()) {
    312             // Rewind the MINA buffer if not all data is processed and inbound is finished.
    313             buf.position(buf.position() - inNetBuffer.position());
    314             inNetBuffer.clear();
    315         }
    316     }
    317
    318     /**
    319      * Get decrypted application data.
    320      *
    321      * @return buffer with data
    322      */
    323     public ByteBuffer getAppBuffer() {
    324         return appBuffer;
    325     }
    326
    327     /**
    328      * Get encrypted data to be sent.
    329      *
    330      * @return buffer with data
    331      */
    332     public ByteBuffer getOutNetBuffer() {
    333         return outNetBuffer;
    334     }
    335
    336     /**
    337      * Encrypt provided buffer. Encytpted data reurned by getOutNetBuffer().
    338      *
    339      * @param src data to encrypt
    340      * @throws SSLException on errors
    341      */
    342     public void encrypt(ByteBuffer src) throws SSLException {
    343         if (!handshakeComplete) {
    344             throw new IllegalStateException();
    345         }
    346
    347         // The data buffer is (must be) empty, we can reuse the entire
    348         // buffer.
    349         outNetBuffer.clear();
    350
    351         // Loop until there is no more data in src
    352         while (src.hasRemaining()) {
    353
    354             if (src.remaining() > ((outNetBuffer.capacity() - outNetBuffer
    355                     .position()) / 2)) {
    356                 // We have to expand outNetBuffer
    357                 // Note: there is no way to know the exact size required, but enrypted data
    358                 // shouln't need to be larger than twice the source data size?
    359                 outNetBuffer = SSLByteBufferPool.expandBuffer(outNetBuffer, src
    360                         .capacity() * 2);
    361                 if (SessionLog.isDebugEnabled(session)) {
    362                     SessionLog.debug(session, " expanded outNetBuffer:"
    363                             + outNetBuffer);
    364                 }
    365             }
    366
    367             SSLEngineResult result = sslEngine.wrap(src, outNetBuffer);
    368             if (SessionLog.isDebugEnabled(session)) {
    369                 SessionLog.debug(session, " Wrap res:" + result);
    370             }
    371
    372             if (result.getStatus() == SSLEngineResult.Status.OK) {
    373                 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
    374                     doTasks();
    375                 }
    376             } else {
    377                 throw new SSLException("SSLEngine error during encrypt: "
    378                         + result.getStatus() + " src: " + src
    379                         + "outNetBuffer: " + outNetBuffer);
    380             }
    381         }
    382
    383         outNetBuffer.flip();
    384     }
    385
    386     /**
    387      * Start SSL shutdown process.
    388      *
    389      * @return <tt>true</tt> if shutdown process is started.
    390      *         <tt>false</tt> if shutdown process is already finished.
    391      *
    392      * @throws SSLException on errors
    393      */
    394     public boolean closeOutbound() throws SSLException {
    395         if (sslEngine == null || sslEngine.isOutboundDone()) {
    396             return false;
    397         }
    398
    399         sslEngine.closeOutbound();
    400
    401         // By RFC 2616, we can "fire and forget" our close_notify
    402         // message, so that's what we'll do here.
    403         outNetBuffer.clear();
    404         SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer);
    405         if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
    406             throw new SSLException("Improper close state: " + result);
    407         }
    408         outNetBuffer.flip();
    409         return true;
    410     }
    411
    412     /**
    413      * Decrypt in net buffer. Result is stored in app buffer.
    414      *
    415      * @throws SSLException
    416      */
    417     private void decrypt(NextFilter nextFilter) throws SSLException {
    418
    419         if (!handshakeComplete) {
    420             throw new IllegalStateException();
    421         }
    422
    423         unwrap(nextFilter);
    424     }
    425
    426     /**
    427      * @param status
    428      * @throws SSLException
    429      */
    430     private void checkStatus(SSLEngineResult res)
    431             throws SSLException {
    432
    433         SSLEngineResult.Status status = res.getStatus();
    434
    435         /*
    436          * The status may be:
    437          * OK - Normal operation
    438          * OVERFLOW - Should never happen since the application buffer is
    439          *      sized to hold the maximum packet size.
    440          * UNDERFLOW - Need to read more data from the socket. It's normal.
    441          * CLOSED - The other peer closed the socket. Also normal.
    442          */
    443         if (status != SSLEngineResult.Status.OK
    444                 && status != SSLEngineResult.Status.CLOSED
    445                 && status != SSLEngineResult.Status.BUFFER_UNDERFLOW) {
    446             throw new SSLException("SSLEngine error during decrypt: " + status
    447                     + " inNetBuffer: " + inNetBuffer + "appBuffer: "
    448                     + appBuffer);
    449         }
    450     }
    451
    452     /**
    453      * Perform any handshaking processing.
    454      */
    455     public void handshake(NextFilter nextFilter) throws SSLException {
    456         if (SessionLog.isDebugEnabled(session)) {
    457             SessionLog.debug(session, " doHandshake()");
    458         }
    459
    460         for (;;) {
    461             if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED) {
    462                 session.setAttribute(SSLFilter.SSL_SESSION, sslEngine
    463                         .getSession());
    464                 if (SessionLog.isDebugEnabled(session)) {
    465                     SSLSession sslSession = sslEngine.getSession();
    466                     SessionLog.debug(session,
    467                             "  handshakeStatus=FINISHED");
    468                     SessionLog.debug(session, "  sslSession CipherSuite used "
    469                             + sslSession.getCipherSuite());
    470                 }
    471                 handshakeComplete = true;
    472                 if (!initialHandshakeComplete
    473                         && session.containsAttribute(SSLFilter.USE_NOTIFICATION)) {
    474                     // SESSION_SECURED is fired only when it's the first handshake.
    475                     // (i.e. renegotiation shouldn't trigger SESSION_SECURED.)
    476                     initialHandshakeComplete = true;
    477                     scheduleMessageReceived(nextFilter,
    478                             SSLFilter.SESSION_SECURED);
    479                 }
    480                 break;
    481             } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
    482                 if (SessionLog.isDebugEnabled(session)) {
    483                     SessionLog.debug(session,
    484                             "  handshakeStatus=NEED_TASK");
    485                 }
    486                 handshakeStatus = doTasks();
    487             } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
    488                 // we need more data read
    489                 if (SessionLog.isDebugEnabled(session)) {
    490                     SessionLog.debug(session,
    491                             "  handshakeStatus=NEED_UNWRAP");
    492                 }
    493                 SSLEngineResult.Status status = unwrapHandshake(nextFilter);
    494                 if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
    495                         || isInboundDone()) {
    496                     // We need more data or the session is closed
    497                     break;
    498                 }
    499             } else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
    500                 if (SessionLog.isDebugEnabled(session)) {
    501                     SessionLog.debug(session,
    502                             "  handshakeStatus=NEED_WRAP");
    503                 }
    504                 // First make sure that the out buffer is completely empty. Since we
    505                 // cannot call wrap with data left on the buffer
    506                 if (outNetBuffer.hasRemaining()) {
    507                     if (SessionLog.isDebugEnabled(session)) {
    508                         SessionLog
    509                                 .debug(session, "  Still data in out buffer!");
    510                     }
    511                     break;
    512                 }
    513                 outNetBuffer.clear();
    514                 SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer);
    515                 if (SessionLog.isDebugEnabled(session)) {
    516                     SessionLog.debug(session, " Wrap res:" + result);
    517                 }
    518
    519                 outNetBuffer.flip();
    520                 handshakeStatus = result.getHandshakeStatus();
    521                 writeNetBuffer(nextFilter);
    522             } else {
    523                 throw new IllegalStateException("Invalid Handshaking State"
    524                         + handshakeStatus);
    525             }
    526         }
    527     }
    528
    529     public WriteFuture writeNetBuffer(NextFilter nextFilter)
    530             throws SSLException {
    531         // Check if any net data needed to be writen
    532         if (!getOutNetBuffer().hasRemaining()) {
    533             // no; bail out
    534             return DefaultWriteFuture.newNotWrittenFuture(session);
    535         }
    536
    537         // set flag that we are writing encrypted data
    538         // (used in SSLFilter.filterWrite())
    539         writingEncryptedData = true;
    540
    541         // write net data
    542         WriteFuture writeFuture = null;
    543
    544         try {
    545             if (SessionLog.isDebugEnabled(session)) {
    546                 SessionLog.debug(session, " write outNetBuffer: "
    547                         + getOutNetBuffer());
    548             }
    549             org.apache.mina.common.ByteBuffer writeBuffer = copy(getOutNetBuffer());
    550             if (SessionLog.isDebugEnabled(session)) {
    551                 SessionLog.debug(session, " session write: " + writeBuffer);
    552             }
    553             //debug("outNetBuffer (after copy): {0}", sslHandler.getOutNetBuffer());
    554
    555             writeFuture = new DefaultWriteFuture(session);
    556             parent.filterWrite(nextFilter, session, new WriteRequest(
    557                     writeBuffer, writeFuture));
    558
    559             // loop while more writes required to complete handshake
    560             while (needToCompleteHandshake()) {
    561                 try {
    562                     handshake(nextFilter);
    563                 } catch (SSLException ssle) {
    564                     SSLException newSSLE = new SSLHandshakeException(
    565                             "SSL handshake failed.");
    566                     newSSLE.initCause(ssle);
    567                     throw newSSLE;
    568                 }
    569                 if (getOutNetBuffer().hasRemaining()) {
    570                     if (SessionLog.isDebugEnabled(session)) {
    571                         SessionLog.debug(session, " write outNetBuffer2: "
    572                                 + getOutNetBuffer());
    573                     }
    574                     org.apache.mina.common.ByteBuffer writeBuffer2 = copy(getOutNetBuffer());
    575                     writeFuture = new DefaultWriteFuture(session);
    576                     parent.filterWrite(nextFilter, session, new WriteRequest(
    577                             writeBuffer2, writeFuture));
    578                 }
    579             }
    580         } finally {
    581             writingEncryptedData = false;
    582         }
    583
    584         return writeFuture;
    585     }
    586
    587     private void unwrap(NextFilter nextFilter) throws SSLException {
    588         if (SessionLog.isDebugEnabled(session)) {
    589             SessionLog.debug(session, " unwrap()");
    590         }
    591
    592         // Prepare the net data for reading.
    593         inNetBuffer.flip();
    594
    595         SSLEngineResult res = unwrap0();
    596
    597         // prepare to be written again
    598         inNetBuffer.compact();
    599
    600         checkStatus(res);
    601
    602         renegotiateIfNeeded(nextFilter, res);
    603     }
    604
    605     private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
    606         if (SessionLog.isDebugEnabled(session)) {
    607             SessionLog.debug(session, " unwrapHandshake()");
    608         }
    609
    610         // Prepare the net data for reading.
    611         inNetBuffer.flip();
    612
    613         SSLEngineResult res = unwrap0();
    614         handshakeStatus = res.getHandshakeStatus();
    615
    616         checkStatus(res);
    617
    618         // If handshake finished, no data was produced, and the status is still ok,
    619         // try to unwrap more
    620         if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
    621                 && res.getStatus() == SSLEngineResult.Status.OK
    622                 && inNetBuffer.hasRemaining()) {
    623             res = unwrap0();
    624
    625             // prepare to be written again
    626             inNetBuffer.compact();
    627
    628             renegotiateIfNeeded(nextFilter, res);
    629         } else {
    630             // prepare to be written again
    631             inNetBuffer.compact();
    632         }
    633
    634         return res.getStatus();
    635     }
    636
    637     private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res)
    638             throws SSLException {
    639         if (res.getStatus() != SSLEngineResult.Status.CLOSED
    640                 && res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW
    641                 && res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
    642             // Renegotiation required.
    643             SessionLog.debug(session, " Renegotiating...");
    644             handshakeComplete = false;
    645             handshakeStatus = res.getHandshakeStatus();
    646             handshake(nextFilter);
    647         }
    648     }
    649
    650     private SSLEngineResult unwrap0() throws SSLException {
    651         SSLEngineResult res;
    652         do {
    653             if (SessionLog.isDebugEnabled(session)) {
    654                 SessionLog.debug(session, "   inNetBuffer: " + inNetBuffer);
    655                 SessionLog.debug(session, "   appBuffer: " + appBuffer);
    656             }
    657             res = sslEngine.unwrap(inNetBuffer, appBuffer);
    658             if (SessionLog.isDebugEnabled(session)) {
    659                 SessionLog.debug(session, " Unwrap res:" + res);
    660             }
    661         } while (res.getStatus() == SSLEngineResult.Status.OK
    662                 && (handshakeComplete && res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING
    663                         || res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP));
    664
    665         return res;
    666     }
    667
    668     /**
    669      * Do all the outstanding handshake tasks in the current Thread.
    670      */
    671     private SSLEngineResult.HandshakeStatus doTasks() {
    672         if (SessionLog.isDebugEnabled(session)) {
    673             SessionLog.debug(session, "   doTasks()");
    674         }
    675
    676         /*
    677          * We could run this in a separate thread, but I don't see the need
    678          * for this when used from SSLFilter. Use thread filters in MINA instead?
    679          */
    680         Runnable runnable;
    681         while ((runnable = sslEngine.getDelegatedTask()) != null) {
    682             if (SessionLog.isDebugEnabled(session)) {
    683                 SessionLog.debug(session, "    doTask: " + runnable);
    684             }
    685             runnable.run();
    686         }
    687         if (SessionLog.isDebugEnabled(session)) {
    688             SessionLog.debug(session, "   doTasks(): "
    689                     + sslEngine.getHandshakeStatus());
    690         }
    691         return sslEngine.getHandshakeStatus();
    692     }
    693
    694     /**
    695      * Creates a new Mina byte buffer that is a deep copy of the remaining bytes
    696      * in the given buffer (between index buf.position() and buf.limit())
    697      *
    698      * @param src the buffer to copy
    699      * @return the new buffer, ready to read from
    700      */
    701     public static org.apache.mina.common.ByteBuffer copy(java.nio.ByteBuffer src) {
    702         org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer
    703                 .allocate(src.remaining());
    704         copy.put(src);
    705         copy.flip();
    706         return copy;
    707     }
    708
    709     private static class EventType {
    710         public static final EventType RECEIVED = new EventType("RECEIVED");
    711
    712         public static final EventType FILTER_WRITE = new EventType(
    713                 "FILTER_WRITE");
    714
    715         private final String value;
    716
    717         private EventType(String value) {
    718             this.value = value;
    719         }
    720
    721         public String toString() {
    722             return value;
    723         }
    724     }
    725
    726     private static class Event {
    727         private final EventType type;
    728
    729         private final NextFilter nextFilter;
    730
    731         private final Object data;
    732
    733         Event(EventType type, NextFilter nextFilter, Object data) {
    734             this.type = type;
    735             this.nextFilter = nextFilter;
    736             this.data = data;
    737         }
    738
    739         public Object getData() {
    740             return data;
    741         }
    742
    743         public NextFilter getNextFilter() {
    744             return nextFilter;
    745         }
    746
    747         public EventType getType() {
    748             return type;
    749         }
    750     }
    751 }