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.util.ArrayList;
24  import java.util.List;
25  
26  import javax.net.ssl.SSLContext;
27  import javax.net.ssl.SSLEngine;
28  import javax.net.ssl.SSLException;
29  import javax.net.ssl.SSLHandshakeException;
30  import javax.net.ssl.SSLSession;
31  
32  import org.apache.mina.core.buffer.IoBuffer;
33  import org.apache.mina.core.filterchain.IoFilterAdapter;
34  import org.apache.mina.core.filterchain.IoFilterChain;
35  import org.apache.mina.core.future.DefaultWriteFuture;
36  import org.apache.mina.core.future.IoFuture;
37  import org.apache.mina.core.future.IoFutureListener;
38  import org.apache.mina.core.future.WriteFuture;
39  import org.apache.mina.core.service.IoAcceptor;
40  import org.apache.mina.core.service.IoHandler;
41  import org.apache.mina.core.session.AttributeKey;
42  import org.apache.mina.core.session.IoSession;
43  import org.apache.mina.core.write.WriteRequest;
44  import org.apache.mina.core.write.WriteRequestWrapper;
45  import org.apache.mina.core.write.WriteToClosedSessionException;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * An SSL filter that encrypts and decrypts the data exchanged in the session.
51   * Adding this filter triggers SSL handshake procedure immediately by sending
52   * a SSL 'hello' message, so you don't need to call
53   * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS
54   * (see below).  If you don't want the handshake procedure to start
55   * immediately, please specify {@code false} as {@code autoStart} parameter in
56   * the constructor.
57   * <p>
58   * This filter uses an {@link SSLEngine} which was introduced in Java 5, so
59   * Java version 5 or above is mandatory to use this filter. And please note that
60   * this filter only works for TCP/IP connections.
61   * <p>
62   *
63   * <h2>Implementing StartTLS</h2>
64   * <p>
65   * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
66   * <pre>
67   * public void messageReceived(IoSession session, Object message) {
68   *    if (message instanceof MyStartTLSRequest) {
69   *        // Insert SSLFilter to get ready for handshaking
70   *        session.getFilterChain().addFirst(sslFilter);
71   *
72   *        // Disable encryption temporarilly.
73   *        // This attribute will be removed by SSLFilter
74   *        // inside the Session.write() call below.
75   *        session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
76   *
77   *        // Write StartTLSResponse which won't be encrypted.
78   *        session.write(new MyStartTLSResponse(OK));
79   *
80   *        // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
81   *        assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
82   *    }
83   * }
84   * </pre>
85   *
86   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
87   * @org.apache.xbean.XBean
88   */
89  public class SslFilter extends IoFilterAdapter {
90      /** The logger */
91      private static final Logger LOGGER = LoggerFactory.getLogger( SslFilter.class );
92  
93      /**
94       * A session attribute key that stores underlying {@link SSLSession}
95       * for each session.
96       */
97      public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
98  
99      /**
100      * A session attribute key that makes next one write request bypass
101      * this filter (not encrypting the data).  This is a marker attribute,
102      * which means that you can put whatever as its value. ({@link Boolean#TRUE}
103      * is preferred.)  The attribute is automatically removed from the session
104      * attribute map as soon as {@link IoSession#write(Object)} is invoked,
105      * and therefore should be put again if you want to make more messages
106      * bypass this filter.  This is especially useful when you implement
107      * StartTLS.
108      */
109     public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
110 
111     /**
112      * A session attribute key that makes this filter to emit a
113      * {@link IoHandler#messageReceived(IoSession, Object)} event with a
114      * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}).
115      * This is a marker attribute, which means that you can put whatever as its
116      * value. ({@link Boolean#TRUE} is preferred.)  By default, this filter
117      * doesn't emit any events related with SSL session flow control.
118      */
119     public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
120 
121     /**
122      * A session attribute key that should be set to an {@link InetSocketAddress}.
123      * Setting this attribute causes
124      * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
125      * hostname and port of the {@link InetSocketAddress} to get an
126      * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
127      * will be called.<br/>
128      * Using this feature {@link SSLSession} objects may be cached and reused
129      * when in client mode.
130      *
131      * @see SSLContext#createSSLEngine(String, int)
132      */
133     public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
134 
135     /**
136      * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)}
137      * event when the session is secured and its {@link #USE_NOTIFICATION}
138      * attribute is set.
139      */
140     public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage(
141             "SESSION_SECURED");
142 
143     /**
144      * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)}
145      * event when the session is not secure anymore and its {@link #USE_NOTIFICATION}
146      * attribute is set.
147      */
148     public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage(
149             "SESSION_UNSECURED");
150 
151     private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
152     private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
153 
154     /** The SslContext used */
155     /* No qualifier */ final SSLContext sslContext;
156 
157     /** A flag used to tell the filter to start the handshake immediately */
158     private final boolean autoStart;
159     
160     /** A flag used to determinate if the handshake should start immediately */
161     private static final boolean START_HANDSHAKE = true;
162 
163     private boolean client;
164 
165     private boolean needClientAuth;
166 
167     private boolean wantClientAuth;
168 
169     private String[] enabledCipherSuites;
170 
171     private String[] enabledProtocols;
172 
173     /**
174      * Creates a new SSL filter using the specified {@link SSLContext}.
175      * The handshake will start immediately.
176      */
177     public SslFilter(SSLContext sslContext) {
178         this(sslContext, START_HANDSHAKE);
179     }
180 
181     /**
182      * Creates a new SSL filter using the specified {@link SSLContext}.
183      * If the <code>autostart</code> flag is set to <code>true</code>, the
184      * handshake will start immediately.
185      */
186     public SslFilter(SSLContext sslContext, boolean autoStart) {
187         if (sslContext == null) {
188             throw new IllegalArgumentException("sslContext");
189         }
190 
191         this.sslContext = sslContext;
192         this.autoStart = autoStart;
193     }
194 
195     /**
196      * Returns the underlying {@link SSLSession} for the specified session.
197      *
198      * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
199      */
200     public SSLSession getSslSession(IoSession session) {
201         return (SSLSession) session.getAttribute(SSL_SESSION);
202     }
203 
204     /**
205      * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
206      * Please note that SSL session is automatically started by default, and therefore
207      * you don't need to call this method unless you've used TLS closure.
208      *
209      * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
210      * @throws SSLException if failed to start the SSL session
211      */
212     public boolean startSsl(IoSession session) throws SSLException {
213         SslHandler handler = getSslSessionHandler(session);
214         boolean started;
215         synchronized (handler) {
216             if (handler.isOutboundDone()) {
217                 NextFilter nextFilter = (NextFilter) session
218                         .getAttribute(NEXT_FILTER);
219                 handler.destroy();
220                 handler.init();
221                 handler.handshake(nextFilter);
222                 started = true;
223             } else {
224                 started = false;
225             }
226         }
227 
228         handler.flushScheduledEvents();
229         return started;
230     }
231     
232     
233     /**
234      * An extended toString() method for sessions. If the SSL handshake
235      * is not yet completed, we will print (ssl) in small caps. Once it's
236      * completed, we will use SSL capitalized.
237      */
238     /* no qualifier */ String getSessionInfo(IoSession session) {
239         StringBuilder sb = new StringBuilder();
240 
241         if (session.getService() instanceof IoAcceptor) {
242             sb.append("Session Server");
243             
244         } else {
245             sb.append("Session Client");
246         }
247         
248         sb.append('[').append(session.getId()).append(']');
249 
250         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
251 
252         if (handler == null) {
253             sb.append("(no sslEngine)");
254         } else if (isSslStarted(session)) {
255             if ( handler.isHandshakeComplete()) {
256                 sb.append("(SSL)");
257             } else {
258                 sb.append( "(ssl...)" );
259             }
260         }
261         
262         return sb.toString();
263     }
264 
265     /**
266      * Returns <tt>true</tt> if and only if the specified <tt>session</tt> is
267      * encrypted/decrypted over SSL/TLS currently. This method will start
268      * to return <tt>false</tt> after TLS <tt>close_notify</tt> message
269      * is sent and any messages written after then is not going to get encrypted.
270      */
271     public boolean isSslStarted(IoSession session) {
272         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
273         
274         if (handler == null) {
275             return false;
276         }
277 
278         synchronized (handler) {
279             return !handler.isOutboundDone();
280         }
281     }
282 
283     /**
284      * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
285      * initiate TLS closure.
286      *
287      * @param session the {@link IoSession} to initiate TLS closure
288      * @throws SSLException if failed to initiate TLS closure
289      * @throws IllegalArgumentException if this filter is not managing the specified session
290      */
291     public WriteFuture stopSsl(IoSession session) throws SSLException {
292         SslHandler handler = getSslSessionHandler(session);
293         NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
294         WriteFuture future;
295         synchronized (handler) {
296             future = initiateClosure(nextFilter, session);
297         }
298 
299         handler.flushScheduledEvents();
300 
301         return future;
302     }
303 
304     /**
305      * Returns <tt>true</tt> if the engine is set to use client mode
306      * when handshaking.
307      */
308     public boolean isUseClientMode() {
309         return client;
310     }
311 
312     /**
313      * Configures the engine to use client (or server) mode when handshaking.
314      */
315     public void setUseClientMode(boolean clientMode) {
316         this.client = clientMode;
317     }
318 
319     /**
320      * Returns <tt>true</tt> if the engine will <em>require</em> client authentication.
321      * This option is only useful to engines in the server mode.
322      */
323     public boolean isNeedClientAuth() {
324         return needClientAuth;
325     }
326 
327     /**
328      * Configures the engine to <em>require</em> client authentication.
329      * This option is only useful for engines in the server mode.
330      */
331     public void setNeedClientAuth(boolean needClientAuth) {
332         this.needClientAuth = needClientAuth;
333     }
334 
335     /**
336      * Returns <tt>true</tt> if the engine will <em>request</em> client authentication.
337      * This option is only useful to engines in the server mode.
338      */
339     public boolean isWantClientAuth() {
340         return wantClientAuth;
341     }
342 
343     /**
344      * Configures the engine to <em>request</em> client authentication.
345      * This option is only useful for engines in the server mode.
346      */
347     public void setWantClientAuth(boolean wantClientAuth) {
348         this.wantClientAuth = wantClientAuth;
349     }
350 
351     /**
352      * Returns the list of cipher suites to be enabled when {@link SSLEngine}
353      * is initialized.
354      *
355      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
356      */
357     public String[] getEnabledCipherSuites() {
358         return enabledCipherSuites;
359     }
360 
361     /**
362      * Sets the list of cipher suites to be enabled when {@link SSLEngine}
363      * is initialized.
364      *
365      * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
366      */
367     public void setEnabledCipherSuites(String[] cipherSuites) {
368         this.enabledCipherSuites = cipherSuites;
369     }
370 
371     /**
372      * Returns the list of protocols to be enabled when {@link SSLEngine}
373      * is initialized.
374      *
375      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
376      */
377     public String[] getEnabledProtocols() {
378         return enabledProtocols;
379     }
380 
381     /**
382      * Sets the list of protocols to be enabled when {@link SSLEngine}
383      * is initialized.
384      *
385      * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
386      */
387     public void setEnabledProtocols(String[] protocols) {
388         this.enabledProtocols = protocols;
389     }
390 
391     /**
392      * Executed just before the filter is added into the chain, we do :
393      * <ul>
394      * <li>check that we don't have a SSL filter already present
395      * <li>we update the next filter
396      * <li>we create the SSL handler helper class
397      * <li>and we store it into the session's Attributes
398      * </ul>
399      */
400     @Override
401     public void onPreAdd(IoFilterChain parent, String name,
402             NextFilter nextFilter) throws SSLException {
403         // Check that we don't have a SSL filter already present in the chain
404         if (parent.contains(SslFilter.class)) {
405             String msg = "Only one SSL filter is permitted in a chain.";
406             LOGGER.error(msg);
407             throw new IllegalStateException(msg);
408         }
409 
410         LOGGER.debug("Adding the SSL Filter {} to the chain", name);
411 
412         IoSession session = parent.getSession();
413         session.setAttribute(NEXT_FILTER, nextFilter);
414 
415         // Create a SSL handler and start handshake.
416         SslHandler handler = new SslHandler(this, session);
417         handler.init();
418         session.setAttribute(SSL_HANDLER, handler);
419     }
420 
421     @Override
422     public void onPostAdd(IoFilterChain parent, String name,
423             NextFilter nextFilter) throws SSLException {
424         if (autoStart == START_HANDSHAKE) {
425             initiateHandshake(nextFilter, parent.getSession());
426         }
427     }
428 
429     @Override
430     public void onPreRemove(IoFilterChain parent, String name,
431             NextFilter nextFilter) throws SSLException {
432         IoSession session = parent.getSession();
433         stopSsl(session);
434         session.removeAttribute(NEXT_FILTER);
435         session.removeAttribute(SSL_HANDLER);
436     }
437 
438     // IoFilter impl.
439     @Override
440     public void sessionClosed(NextFilter nextFilter, IoSession session)
441             throws SSLException {
442         SslHandler handler = getSslSessionHandler(session);
443         try {
444             synchronized (handler) {
445                 // release resources
446                 handler.destroy();
447             }
448 
449             handler.flushScheduledEvents();
450         } finally {
451             // notify closed session
452             nextFilter.sessionClosed(session);
453         }
454     }
455 
456     @Override
457     public void messageReceived(NextFilter nextFilter, IoSession session,
458             Object message) throws SSLException {
459         if ( LOGGER.isDebugEnabled()) {
460             LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
461         }
462         
463         SslHandler handler = getSslSessionHandler(session);
464         
465         synchronized (handler) {
466             if (!isSslStarted(session) && handler.isInboundDone()) {
467                 // The SSL session must be established first before we 
468                 // can push data to the application. Store the incoming
469                 // data into a queue for a later processing
470                 handler.scheduleMessageReceived(nextFilter, message);
471             } else {
472                 IoBuffer buf = (IoBuffer) message;
473                 
474                 try {
475                     // forward read encrypted data to SSL handler
476                     handler.messageReceived(nextFilter, buf.buf());
477 
478                     // Handle data to be forwarded to application or written to net
479                     handleSslData(nextFilter, handler);
480 
481                     if (handler.isInboundDone()) {
482                         if (handler.isOutboundDone()) {
483                             handler.destroy();
484                         } else {
485                             initiateClosure(nextFilter, session);
486                         }
487 
488                         if (buf.hasRemaining()) {
489                             // Forward the data received after closure.
490                             handler.scheduleMessageReceived(nextFilter, buf);
491                         }
492                     }
493                 } catch (SSLException ssle) {
494                     if (!handler.isHandshakeComplete()) {
495                         SSLException newSsle = new SSLHandshakeException(
496                                 "SSL handshake failed.");
497                         newSsle.initCause(ssle);
498                         ssle = newSsle;
499                     }
500 
501                     throw ssle;
502                 }
503             }
504         }
505 
506         handler.flushScheduledEvents();
507     }
508 
509     @Override
510     public void messageSent(NextFilter nextFilter, IoSession session,
511             WriteRequest writeRequest) {
512         if (writeRequest instanceof EncryptedWriteRequest) {
513             EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
514             nextFilter.messageSent(session, wrappedRequest.getParentRequest());
515         } else {
516             // ignore extra buffers used for handshaking
517         }
518     }
519 
520     @Override
521     public void exceptionCaught(NextFilter nextFilter, IoSession session,
522             Throwable cause) throws Exception {
523 
524         if (cause instanceof WriteToClosedSessionException) {
525             // Filter out SSL close notify, which is likely to fail to flush
526             // due to disconnection.
527             WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
528             List<WriteRequest> failedRequests = e.getRequests();
529             boolean containsCloseNotify = false;
530             for (WriteRequest r: failedRequests) {
531                 if (isCloseNotify(r.getMessage())) {
532                     containsCloseNotify = true;
533                     break;
534                 }
535             }
536             
537             if (containsCloseNotify) {
538                 if (failedRequests.size() == 1) {
539                     // close notify is the only failed request; bail out.
540                     return;
541                 }
542                 
543                 List<WriteRequest> newFailedRequests =
544                     new ArrayList<WriteRequest>(failedRequests.size() - 1);
545                 for (WriteRequest r: failedRequests) {
546                     if (!isCloseNotify(r.getMessage())) {
547                         newFailedRequests.add(r);
548                     }
549                 }
550                 
551                 if (newFailedRequests.isEmpty()) {
552                     // the failedRequests were full with close notify; bail out.
553                     return;
554                 }
555                 
556                 cause = new WriteToClosedSessionException(
557                         newFailedRequests, cause.getMessage(), cause.getCause());
558             }
559         }
560         
561         nextFilter.exceptionCaught(session, cause);
562     }
563         
564     private boolean isCloseNotify(Object message) {
565         if (!(message instanceof IoBuffer)) {
566             return false;
567         }
568         
569         IoBuffer buf = (IoBuffer) message;
570         int offset = buf.position();
571         return buf.remaining() == 23 &&
572                buf.get(offset + 0) == 0x15 && buf.get(offset + 1) == 0x03 &&
573                buf.get(offset + 2) == 0x01 && buf.get(offset + 3) == 0x00 &&
574                buf.get(offset + 4) == 0x12;
575     }
576 
577     @Override
578     public void filterWrite(NextFilter nextFilter, IoSession session,
579             WriteRequest writeRequest) throws SSLException {
580         if ( LOGGER.isDebugEnabled()) {
581             LOGGER.debug("{}: Writing Message : {}",  getSessionInfo(session), writeRequest);
582         }
583 
584         boolean needsFlush = true;
585         SslHandler handler = getSslSessionHandler(session);
586         synchronized (handler) {
587             if (!isSslStarted(session)) {
588                 handler.scheduleFilterWrite(nextFilter,
589                         writeRequest);
590             }
591             // Don't encrypt the data if encryption is disabled.
592             else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
593                 // Remove the marker attribute because it is temporary.
594                 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
595                 handler.scheduleFilterWrite(nextFilter,
596                         writeRequest);
597             } else {
598                 // Otherwise, encrypt the buffer.
599                 IoBuffer buf = (IoBuffer) writeRequest.getMessage();
600 
601                 if (handler.isWritingEncryptedData()) {
602                     // data already encrypted; simply return buffer
603                     handler.scheduleFilterWrite(nextFilter, writeRequest);
604                 } else if (handler.isHandshakeComplete()) {
605                     // SSL encrypt
606                     int pos = buf.position();
607                     handler.encrypt(buf.buf());
608                     buf.position(pos);
609                     IoBuffer encryptedBuffer = handler.fetchOutNetBuffer();
610                     handler.scheduleFilterWrite(
611                             nextFilter,
612                             new EncryptedWriteRequest(
613                                     writeRequest, encryptedBuffer));
614                 } else {
615                     if (session.isConnected()) {
616                         // Handshake not complete yet.
617                         handler.schedulePreHandshakeWriteRequest(nextFilter,
618                                 writeRequest);
619                     }
620                     needsFlush = false;
621                 }
622             }
623         }
624 
625         if (needsFlush) {
626             handler.flushScheduledEvents();
627         }
628     }
629 
630     @Override
631     public void filterClose(final NextFilter nextFilter, final IoSession session)
632             throws SSLException {
633         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
634         if (handler == null) {
635             // The connection might already have closed, or
636             // SSL might have not started yet.
637             nextFilter.filterClose(session);
638             return;
639         }
640 
641         WriteFuture future = null;
642         try {
643             synchronized (handler) {
644                 if (isSslStarted(session)) {
645                     future = initiateClosure(nextFilter, session);
646                     future.addListener(new IoFutureListener<IoFuture>() {
647                         public void operationComplete(IoFuture future) {
648                             nextFilter.filterClose(session);
649                         }
650                     });
651                 }
652             }
653 
654             handler.flushScheduledEvents();
655         } finally {
656             if (future == null) {
657                 nextFilter.filterClose(session);
658             }
659         }
660     }
661 
662     private void initiateHandshake(NextFilter nextFilter, IoSession session)
663             throws SSLException {
664         LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
665         SslHandler handler = getSslSessionHandler(session);
666         
667         synchronized (handler) {
668             handler.handshake(nextFilter);
669         }
670         
671         handler.flushScheduledEvents();
672     }
673 
674     private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session)
675             throws SSLException {
676         SslHandler handler = getSslSessionHandler(session);
677         
678         // if already shut down
679         if (!handler.closeOutbound()) {
680             return DefaultWriteFuture.newNotWrittenFuture(
681                     session, new IllegalStateException("SSL session is shut down already."));
682         }
683 
684         // there might be data to write out here?
685         WriteFuture future = handler.writeNetBuffer(nextFilter);
686         
687         if (future == null) {
688             future = DefaultWriteFuture.newWrittenFuture(session);
689         }
690 
691         if (handler.isInboundDone()) {
692             handler.destroy();
693         }
694 
695         if (session.containsAttribute(USE_NOTIFICATION)) {
696             handler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
697         }
698 
699         return future;
700     }
701 
702     // Utilities
703     private void handleSslData(NextFilter nextFilter, SslHandler handler)
704             throws SSLException {
705         if ( LOGGER.isDebugEnabled()) {
706             LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(handler.getSession()));
707         }
708 
709         // Flush any buffered write requests occurred before handshaking.
710         if (handler.isHandshakeComplete()) {
711             handler.flushPreHandshakeEvents();
712         }
713 
714         // Write encrypted data to be written (if any)
715         handler.writeNetBuffer(nextFilter);
716 
717         // handle app. data read (if any)
718         handleAppDataRead(nextFilter, handler);
719     }
720 
721     private void handleAppDataRead(NextFilter nextFilter, SslHandler handler) {
722         // forward read app data
723         IoBuffer readBuffer = handler.fetchAppBuffer();
724         
725         if (readBuffer.hasRemaining()) {
726             handler.scheduleMessageReceived(nextFilter, readBuffer);
727         }
728     }
729 
730     private SslHandler getSslSessionHandler(IoSession session) {
731         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
732         
733         if (handler == null) {
734             throw new IllegalStateException();
735         }
736         
737         if (handler.getSslFilter() != this) {
738             throw new IllegalArgumentException("Not managed by this filter.");
739         }
740         
741         return handler;
742     }
743 
744     /**
745      * A message that is sent from {@link SslFilter} when the connection became
746      * secure or is not secure anymore.
747      *
748      * @author <a href="http://mina.apache.org">Apache MINA Project</a>
749      */
750     public static class SslFilterMessage {
751         private final String name;
752 
753         private SslFilterMessage(String name) {
754             this.name = name;
755         }
756 
757         @Override
758         public String toString() {
759             return name;
760         }
761     }
762 
763     private static class EncryptedWriteRequest extends WriteRequestWrapper {
764         private final IoBuffer encryptedMessage;
765 
766         private EncryptedWriteRequest(WriteRequest writeRequest,
767                 IoBuffer encryptedMessage) {
768             super(writeRequest);
769             this.encryptedMessage = encryptedMessage;
770         }
771 
772         @Override
773         public Object getMessage() {
774             return encryptedMessage;
775         }
776     }
777 }