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