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