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