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