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: 557169 $, $Date: 2007-07-18 15:26:04 +0900 (수, 18  7월 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 initialHandshakeStatus;
92  
93      /**
94       * Initial handshake complete?
95       */
96      private boolean initialHandshakeComplete;
97  
98      private boolean writingEncryptedData;
99  
100     /**
101      * Constuctor.
102      *
103      * @param sslc
104      * @throws SSLException
105      */
106     public SSLHandler(SSLFilter parent, SSLContext sslc, IoSession session)
107             throws SSLException {
108         this.parent = parent;
109         this.session = session;
110         this.ctx = sslc;
111         init();
112     }
113 
114     public void init() throws SSLException {
115         if (sslEngine != null) {
116             return;
117         }
118 
119         sslEngine = ctx.createSSLEngine();
120         sslEngine.setUseClientMode(parent.isUseClientMode());
121 
122         if (parent.isWantClientAuth()) {
123             sslEngine.setWantClientAuth(true);
124         }
125 
126         if (parent.isNeedClientAuth()) {
127             sslEngine.setNeedClientAuth(true);
128         }
129 
130         if (parent.getEnabledCipherSuites() != null) {
131             sslEngine.setEnabledCipherSuites(parent.getEnabledCipherSuites());
132         }
133 
134         if (parent.getEnabledProtocols() != null) {
135             sslEngine.setEnabledProtocols(parent.getEnabledProtocols());
136         }
137 
138         sslEngine.beginHandshake();
139         initialHandshakeStatus = sslEngine.getHandshakeStatus();//SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
140         initialHandshakeComplete = false;
141 
142         SSLByteBufferPool.initiate(sslEngine);
143 
144         appBuffer = SSLByteBufferPool.getApplicationBuffer();
145 
146         inNetBuffer = SSLByteBufferPool.getPacketBuffer();
147         outNetBuffer = SSLByteBufferPool.getPacketBuffer();
148         outNetBuffer.position(0);
149         outNetBuffer.limit(0);
150 
151         writingEncryptedData = false;
152     }
153 
154     /**
155      * Release allocated ByteBuffers.
156      */
157     public void destroy() {
158         if (sslEngine == null) {
159             return;
160         }
161 
162         // Close inbound and flush all remaining data if available.
163         try {
164             sslEngine.closeInbound();
165         } catch (SSLException e) {
166             SessionLog.debug(session,
167                     "Unexpected exception from SSLEngine.closeInbound().", e);
168         }
169 
170         try {
171             do {
172                 outNetBuffer.clear();
173             } while (sslEngine.wrap(hsBB, outNetBuffer).bytesProduced() > 0);
174         } catch (SSLException e) {
175             SessionLog.debug(session,
176                     "Unexpected exception from SSLEngine.wrap().", e);
177         }
178         sslEngine.closeOutbound();
179         sslEngine = null;
180 
181         SSLByteBufferPool.release(appBuffer);
182         SSLByteBufferPool.release(inNetBuffer);
183         SSLByteBufferPool.release(outNetBuffer);
184         preHandshakeEventQueue.clear();
185         //postHandshakeEventQueue.clear();
186     }
187 
188     public SSLFilter getParent() {
189         return parent;
190     }
191 
192     public IoSession getSession() {
193         return session;
194     }
195 
196     /**
197      * Check we are writing encrypted data.
198      */
199     public boolean isWritingEncryptedData() {
200         return writingEncryptedData;
201     }
202 
203     /**
204      * Check if initial handshake is completed.
205      */
206     public boolean isInitialHandshakeComplete() {
207         return initialHandshakeComplete;
208     }
209 
210     public boolean isInboundDone() {
211         return sslEngine == null || sslEngine.isInboundDone();
212     }
213 
214     public boolean isOutboundDone() {
215         return sslEngine == null || sslEngine.isOutboundDone();
216     }
217 
218     /**
219      * Check if there is any need to complete initial handshake.
220      */
221     public boolean needToCompleteInitialHandshake() {
222         return (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone());
223     }
224 
225     public void schedulePreHandshakeWriteRequest(NextFilter nextFilter,
226             WriteRequest writeRequest) {
227         preHandshakeEventQueue.offer(new Event(EventType.FILTER_WRITE,
228                 nextFilter, writeRequest));
229     }
230 
231     public void flushPreHandshakeEvents() throws SSLException {
232         Event scheduledWrite;
233 
234         while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
235             if (SessionLog.isDebugEnabled(session)) {
236                 SessionLog.debug(session, " Flushing buffered write request: "
237                         + scheduledWrite.data);
238             }
239             parent.filterWrite(scheduledWrite.nextFilter, session,
240                     (WriteRequest) scheduledWrite.data);
241         }
242     }
243 
244     public void scheduleFilterWrite(NextFilter nextFilter,
245             WriteRequest writeRequest) {
246         filterWriteEventQueue.offer(new Event(EventType.FILTER_WRITE,
247                 nextFilter, writeRequest));
248     }
249 
250     public void scheduleMessageReceived(NextFilter nextFilter,
251             Object message) {
252         messageReceivedEventQueue.offer(new Event(EventType.RECEIVED, nextFilter,
253                 message));
254     }
255     
256     public void flushScheduledEvents() {
257         // Fire events only when no lock is hold for this handler.
258         if (Thread.holdsLock(this)) {
259             return;
260         }
261 
262         Event e;
263         
264         // We need synchronization here inevitably because filterWrite can be
265         // called simultaneously and cause 'bad record MAC' integrity error.
266         synchronized (this) {
267             while ((e = filterWriteEventQueue.poll()) != null) {
268                 e.nextFilter.filterWrite(session, (WriteRequest) e.data);
269             }
270         }
271 
272         while ((e = messageReceivedEventQueue.poll()) != null) {
273             e.nextFilter.messageReceived(session, e.data);
274         }
275     }
276 
277     /**
278      * Call when data read from net. Will perform inial hanshake or decrypt provided
279      * Buffer.
280      * Decrytpted data reurned by getAppBuffer(), if any.
281      *
282      * @param buf buffer to decrypt
283      * @throws SSLException on errors
284      */
285     public void messageReceived(NextFilter nextFilter, ByteBuffer buf)
286             throws SSLException {
287         if (buf.limit() > inNetBuffer.remaining()) {
288             // We have to expand inNetBuffer
289             inNetBuffer = SSLByteBufferPool.expandBuffer(inNetBuffer,
290                     inNetBuffer.capacity() + (buf.limit() * 2));
291             // We also expand app. buffer (twice the size of in net. buffer)
292             appBuffer = SSLByteBufferPool.expandBuffer(appBuffer, inNetBuffer
293                     .capacity() * 2);
294             appBuffer.position(0);
295             appBuffer.limit(0);
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 (!initialHandshakeComplete) {
306             handshake(nextFilter);
307         } else {
308             decrypt();
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 (!initialHandshakeComplete) {
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() throws SSLException {
418 
419         if (!initialHandshakeComplete) {
420             throw new IllegalStateException();
421         }
422 
423         if (appBuffer.hasRemaining()) {
424             if (SessionLog.isDebugEnabled(session)) {
425                 SessionLog.debug(session, " Error: appBuffer not empty!");
426             }
427             //still app data in buffer!?
428             throw new IllegalStateException();
429         }
430 
431         unwrap();
432     }
433 
434     /**
435      * @param status
436      * @throws SSLException
437      */
438     private SSLEngineResult.Status checkStatus(SSLEngineResult.Status status)
439             throws SSLException {
440         if (status != SSLEngineResult.Status.OK
441                 && status != SSLEngineResult.Status.CLOSED
442                 && status != SSLEngineResult.Status.BUFFER_UNDERFLOW) {
443             throw new SSLException("SSLEngine error during decrypt: " + status
444                     + " inNetBuffer: " + inNetBuffer + "appBuffer: "
445                     + appBuffer);
446         }
447 
448         return status;
449     }
450 
451     /**
452      * Perform any handshaking processing.
453      */
454     public void handshake(NextFilter nextFilter) throws SSLException {
455         if (SessionLog.isDebugEnabled(session)) {
456             SessionLog.debug(session, " doHandshake()");
457         }
458 
459         while (!initialHandshakeComplete) {
460             if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED) {
461                 session.setAttribute(SSLFilter.SSL_SESSION, sslEngine
462                         .getSession());
463                 if (SessionLog.isDebugEnabled(session)) {
464                     SSLSession sslSession = sslEngine.getSession();
465                     SessionLog.debug(session,
466                             "  initialHandshakeStatus=FINISHED");
467                     SessionLog.debug(session, "  sslSession CipherSuite used "
468                             + sslSession.getCipherSuite());
469                 }
470                 initialHandshakeComplete = true;
471                 if (session.containsAttribute(SSLFilter.USE_NOTIFICATION)) {
472                     scheduleMessageReceived(nextFilter,
473                             SSLFilter.SESSION_SECURED);
474                 }
475                 break;
476             } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
477                 if (SessionLog.isDebugEnabled(session)) {
478                     SessionLog.debug(session,
479                             "  initialHandshakeStatus=NEED_TASK");
480                 }
481                 initialHandshakeStatus = doTasks();
482             } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
483                 // we need more data read
484                 if (SessionLog.isDebugEnabled(session)) {
485                     SessionLog.debug(session,
486                             "  initialHandshakeStatus=NEED_UNWRAP");
487                 }
488                 SSLEngineResult.Status status = unwrapHandshake();
489                 if ((initialHandshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED && status == SSLEngineResult.Status.BUFFER_UNDERFLOW)
490                         || isInboundDone()) {
491                     // We need more data or the session is closed
492                     break;
493                 }
494             } else if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
495                 if (SessionLog.isDebugEnabled(session)) {
496                     SessionLog.debug(session,
497                             "  initialHandshakeStatus=NEED_WRAP");
498                 }
499                 // First make sure that the out buffer is completely empty. Since we
500                 // cannot call wrap with data left on the buffer
501                 if (outNetBuffer.hasRemaining()) {
502                     if (SessionLog.isDebugEnabled(session)) {
503                         SessionLog
504                                 .debug(session, "  Still data in out buffer!");
505                     }
506                     break;
507                 }
508                 outNetBuffer.clear();
509                 SSLEngineResult result = sslEngine.wrap(hsBB, outNetBuffer);
510                 if (SessionLog.isDebugEnabled(session)) {
511                     SessionLog.debug(session, " Wrap res:" + result);
512                 }
513 
514                 outNetBuffer.flip();
515                 initialHandshakeStatus = result.getHandshakeStatus();
516                 writeNetBuffer(nextFilter);
517             } else {
518                 throw new IllegalStateException("Invalid Handshaking State"
519                         + initialHandshakeStatus);
520             }
521         }
522     }
523 
524     public WriteFuture writeNetBuffer(NextFilter nextFilter)
525             throws SSLException {
526         // Check if any net data needed to be writen
527         if (!getOutNetBuffer().hasRemaining()) {
528             // no; bail out
529             return DefaultWriteFuture.newNotWrittenFuture(session);
530         }
531 
532         // set flag that we are writing encrypted data
533         // (used in SSLFilter.filterWrite())
534         writingEncryptedData = true;
535 
536         // write net data
537         WriteFuture writeFuture = null;
538 
539         try {
540             if (SessionLog.isDebugEnabled(session)) {
541                 SessionLog.debug(session, " write outNetBuffer: "
542                         + getOutNetBuffer());
543             }
544             org.apache.mina.common.ByteBuffer writeBuffer = copy(getOutNetBuffer());
545             if (SessionLog.isDebugEnabled(session)) {
546                 SessionLog.debug(session, " session write: " + writeBuffer);
547             }
548             //debug("outNetBuffer (after copy): {0}", sslHandler.getOutNetBuffer());
549 
550             writeFuture = new DefaultWriteFuture(session);
551             parent.filterWrite(nextFilter, session, new WriteRequest(
552                     writeBuffer, writeFuture));
553 
554             // loop while more writes required to complete handshake
555             while (needToCompleteInitialHandshake()) {
556                 try {
557                     handshake(nextFilter);
558                 } catch (SSLException ssle) {
559                     SSLException newSSLE = new SSLHandshakeException(
560                             "Initial SSL handshake failed.");
561                     newSSLE.initCause(ssle);
562                     throw newSSLE;
563                 }
564                 if (getOutNetBuffer().hasRemaining()) {
565                     if (SessionLog.isDebugEnabled(session)) {
566                         SessionLog.debug(session, " write outNetBuffer2: "
567                                 + getOutNetBuffer());
568                     }
569                     org.apache.mina.common.ByteBuffer writeBuffer2 = copy(getOutNetBuffer());
570                     writeFuture = new DefaultWriteFuture(session);
571                     parent.filterWrite(nextFilter, session, new WriteRequest(
572                             writeBuffer2, writeFuture));
573                 }
574             }
575         } finally {
576             writingEncryptedData = false;
577         }
578 
579         return writeFuture;
580     }
581 
582     private SSLEngineResult.Status unwrap() throws SSLException {
583         if (SessionLog.isDebugEnabled(session)) {
584             SessionLog.debug(session, " unwrap()");
585         }
586         // Prepare the application buffer to receive decrypted data
587         appBuffer.clear();
588 
589         // Prepare the net data for reading.
590         inNetBuffer.flip();
591 
592         SSLEngineResult res;
593         do {
594             if (SessionLog.isDebugEnabled(session)) {
595                 SessionLog.debug(session, "   inNetBuffer: " + inNetBuffer);
596                 SessionLog.debug(session, "   appBuffer: " + appBuffer);
597             }
598             res = sslEngine.unwrap(inNetBuffer, appBuffer);
599             if (SessionLog.isDebugEnabled(session)) {
600                 SessionLog.debug(session, " Unwrap res:" + res);
601             }
602         } while (res.getStatus() == SSLEngineResult.Status.OK);
603 
604         // prepare to be written again
605         inNetBuffer.compact();
606         // prepare app data to be read
607         appBuffer.flip();
608 
609         /*
610          * The status may be:
611          * OK - Normal operation
612          * OVERFLOW - Should never happen since the application buffer is
613          *      sized to hold the maximum packet size.
614          * UNDERFLOW - Need to read more data from the socket. It's normal.
615          * CLOSED - The other peer closed the socket. Also normal.
616          */
617         return checkStatus(res.getStatus());
618     }
619 
620     private SSLEngineResult.Status unwrapHandshake() throws SSLException {
621         if (SessionLog.isDebugEnabled(session)) {
622             SessionLog.debug(session, " unwrapHandshake()");
623         }
624         // Prepare the application buffer to receive decrypted data
625         appBuffer.clear();
626 
627         // Prepare the net data for reading.
628         inNetBuffer.flip();
629 
630         SSLEngineResult res;
631         do {
632             if (SessionLog.isDebugEnabled(session)) {
633                 SessionLog.debug(session, "   inNetBuffer: " + inNetBuffer);
634                 SessionLog.debug(session, "   appBuffer: " + appBuffer);
635             }
636             res = sslEngine.unwrap(inNetBuffer, appBuffer);
637             if (SessionLog.isDebugEnabled(session)) {
638                 SessionLog.debug(session, " Unwrap res:" + res);
639             }
640 
641         } while (res.getStatus() == SSLEngineResult.Status.OK
642                 && res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP);
643 
644         initialHandshakeStatus = res.getHandshakeStatus();
645 
646         // If handshake finished, no data was produced, and the status is still ok,
647         // try to unwrap more
648         if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
649                 && appBuffer.position() == 0
650                 && res.getStatus() == SSLEngineResult.Status.OK
651                 && inNetBuffer.hasRemaining()) {
652             do {
653                 if (SessionLog.isDebugEnabled(session)) {
654                     SessionLog.debug(session, "  extra handshake unwrap");
655                     SessionLog.debug(session, "   inNetBuffer: " + inNetBuffer);
656                     SessionLog.debug(session, "   appBuffer: " + appBuffer);
657                 }
658                 res = sslEngine.unwrap(inNetBuffer, appBuffer);
659                 if (SessionLog.isDebugEnabled(session)) {
660                     SessionLog.debug(session, " Unwrap res:" + res);
661                 }
662             } while (res.getStatus() == SSLEngineResult.Status.OK);
663         }
664 
665         // prepare to be written again
666         inNetBuffer.compact();
667 
668         // prepare app data to be read
669         appBuffer.flip();
670 
671         /*
672          * The status may be:
673          * OK - Normal operation
674          * OVERFLOW - Should never happen since the application buffer is
675          *      sized to hold the maximum packet size.
676          * UNDERFLOW - Need to read more data from the socket. It's normal.
677          * CLOSED - The other peer closed the socket. Also normal.
678          */
679         //initialHandshakeStatus = res.getHandshakeStatus();
680         return checkStatus(res.getStatus());
681     }
682 
683     /**
684      * Do all the outstanding handshake tasks in the current Thread.
685      */
686     private SSLEngineResult.HandshakeStatus doTasks() {
687         if (SessionLog.isDebugEnabled(session)) {
688             SessionLog.debug(session, "   doTasks()");
689         }
690 
691         /*
692          * We could run this in a separate thread, but I don't see the need
693          * for this when used from SSLFilter. Use thread filters in MINA instead?
694          */
695         Runnable runnable;
696         while ((runnable = sslEngine.getDelegatedTask()) != null) {
697             if (SessionLog.isDebugEnabled(session)) {
698                 SessionLog.debug(session, "    doTask: " + runnable);
699             }
700             runnable.run();
701         }
702         if (SessionLog.isDebugEnabled(session)) {
703             SessionLog.debug(session, "   doTasks(): "
704                     + sslEngine.getHandshakeStatus());
705         }
706         return sslEngine.getHandshakeStatus();
707     }
708 
709     /**
710      * Creates a new Mina byte buffer that is a deep copy of the remaining bytes
711      * in the given buffer (between index buf.position() and buf.limit())
712      *
713      * @param src the buffer to copy
714      * @return the new buffer, ready to read from
715      */
716     public static org.apache.mina.common.ByteBuffer copy(java.nio.ByteBuffer src) {
717         org.apache.mina.common.ByteBuffer copy = org.apache.mina.common.ByteBuffer
718                 .allocate(src.remaining());
719         copy.put(src);
720         copy.flip();
721         return copy;
722     }
723 
724     private static class EventType {
725         public static final EventType RECEIVED = new EventType("RECEIVED");
726 
727         public static final EventType FILTER_WRITE = new EventType(
728                 "FILTER_WRITE");
729 
730         private final String value;
731 
732         private EventType(String value) {
733             this.value = value;
734         }
735 
736         public String toString() {
737             return value;
738         }
739     }
740 
741     private static class Event {
742         private final EventType type;
743 
744         private final NextFilter nextFilter;
745 
746         private final Object data;
747 
748         Event(EventType type, NextFilter nextFilter, Object data) {
749             this.type = type;
750             this.nextFilter = nextFilter;
751             this.data = data;
752         }
753 
754         public Object getData() {
755             return data;
756         }
757 
758         public NextFilter getNextFilter() {
759             return nextFilter;
760         }
761 
762         public EventType getType() {
763             return type;
764         }
765     }
766 }