001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.filter.ssl;
021
022import java.net.InetSocketAddress;
023import java.util.ArrayList;
024import java.util.List;
025
026import javax.net.ssl.SSLContext;
027import javax.net.ssl.SSLEngine;
028import javax.net.ssl.SSLException;
029import javax.net.ssl.SSLHandshakeException;
030import javax.net.ssl.SSLSession;
031
032import org.apache.mina.core.buffer.IoBuffer;
033import org.apache.mina.core.filterchain.IoFilterAdapter;
034import org.apache.mina.core.filterchain.IoFilterChain;
035import org.apache.mina.core.future.DefaultWriteFuture;
036import org.apache.mina.core.future.IoFuture;
037import org.apache.mina.core.future.IoFutureListener;
038import org.apache.mina.core.future.WriteFuture;
039import org.apache.mina.core.service.IoAcceptor;
040import org.apache.mina.core.service.IoHandler;
041import org.apache.mina.core.session.AttributeKey;
042import org.apache.mina.core.session.IoSession;
043import org.apache.mina.core.write.WriteRequest;
044import org.apache.mina.core.write.WriteRequestWrapper;
045import org.apache.mina.core.write.WriteToClosedSessionException;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049/**
050 * An SSL filter that encrypts and decrypts the data exchanged in the session.
051 * Adding this filter triggers SSL handshake procedure immediately by sending
052 * a SSL 'hello' message, so you don't need to call
053 * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS
054 * (see below).  If you don't want the handshake procedure to start
055 * immediately, please specify {@code false} as {@code autoStart} parameter in
056 * the constructor.
057 * <p>
058 * This filter uses an {@link SSLEngine} which was introduced in Java 5, so
059 * Java version 5 or above is mandatory to use this filter. And please note that
060 * this filter only works for TCP/IP connections.
061 *
062 * <h2>Implementing StartTLS</h2>
063 * <p>
064 * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
065 * <pre>
066 * public void messageReceived(IoSession session, Object message) {
067 *    if (message instanceof MyStartTLSRequest) {
068 *        // Insert SSLFilter to get ready for handshaking
069 *        session.getFilterChain().addFirst(sslFilter);
070 *
071 *        // Disable encryption temporarilly.
072 *        // This attribute will be removed by SSLFilter
073 *        // inside the Session.write() call below.
074 *        session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
075 *
076 *        // Write StartTLSResponse which won't be encrypted.
077 *        session.write(new MyStartTLSResponse(OK));
078 *
079 *        // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
080 *        assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
081 *    }
082 * }
083 * </pre>
084 *
085 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
086 * @org.apache.xbean.XBean
087 */
088public class SslFilter extends IoFilterAdapter {
089    /** The logger */
090    private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
091
092    /**
093     * A session attribute key that stores underlying {@link SSLSession}
094     * for each session.
095     */
096    public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
097
098    /**
099     * A session attribute key that makes next one write request bypass
100     * this filter (not encrypting the data).  This is a marker attribute,
101     * which means that you can put whatever as its value. ({@link Boolean#TRUE}
102     * is preferred.)  The attribute is automatically removed from the session
103     * attribute map as soon as {@link IoSession#write(Object)} is invoked,
104     * and therefore should be put again if you want to make more messages
105     * bypass this filter.  This is especially useful when you implement
106     * StartTLS.
107     */
108    public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
109
110    /**
111     * A session attribute key that makes this filter to emit a
112     * {@link IoHandler#messageReceived(IoSession, Object)} event with a
113     * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}).
114     * This is a marker attribute, which means that you can put whatever as its
115     * value. ({@link Boolean#TRUE} is preferred.)  By default, this filter
116     * doesn't emit any events related with SSL session flow control.
117     */
118    public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
119
120    /**
121     * A session attribute key that should be set to an {@link InetSocketAddress}.
122     * Setting this attribute causes
123     * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
124     * hostname and port of the {@link InetSocketAddress} to get an
125     * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
126     * will be called.
127     * <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     * @param sslContext The SSLContext to use
177     */
178    public SslFilter(SSLContext sslContext) {
179        this(sslContext, START_HANDSHAKE);
180    }
181
182    /**
183     * Creates a new SSL filter using the specified {@link SSLContext}.
184     * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the
185     * handshake will start immediately.
186     * 
187     * @param sslContext The SSLContext to use
188     * @param autoStart The flag used to tell the filter to start the handshake immediately
189     */
190    public SslFilter(SSLContext sslContext, boolean autoStart) {
191        if (sslContext == null) {
192            throw new IllegalArgumentException("sslContext");
193        }
194
195        this.sslContext = sslContext;
196        this.autoStart = autoStart;
197    }
198
199    /**
200     * Returns the underlying {@link SSLSession} for the specified session.
201     *
202     * @param session The current session 
203     * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
204     */
205    public SSLSession getSslSession(IoSession session) {
206        return (SSLSession) session.getAttribute(SSL_SESSION);
207    }
208
209    /**
210     * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
211     * Please note that SSL session is automatically started by default, and therefore
212     * you don't need to call this method unless you've used TLS closure.
213     *
214     * @param session The session that will be switched to SSL mode
215     * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
216     * @throws SSLException if failed to start the SSL session
217     */
218    public boolean startSsl(IoSession session) throws SSLException {
219        SslHandler sslHandler = getSslSessionHandler(session);
220        boolean started;
221
222        try {
223            synchronized (sslHandler) {
224                if (sslHandler.isOutboundDone()) {
225                    NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
226                    sslHandler.destroy();
227                    sslHandler.init();
228                    sslHandler.handshake(nextFilter);
229                    started = true;
230                } else {
231                    started = false;
232                }
233            }
234
235            sslHandler.flushScheduledEvents();
236        } catch (SSLException se) {
237            sslHandler.release();
238            throw se;
239        }
240
241        return started;
242    }
243
244    /**
245     * An extended toString() method for sessions. If the SSL handshake
246     * is not yet completed, we will print (ssl) in small caps. Once it's
247     * completed, we will use SSL capitalized.
248     */
249    /* no qualifier */String getSessionInfo(IoSession session) {
250        StringBuilder sb = new StringBuilder();
251
252        if (session.getService() instanceof IoAcceptor) {
253            sb.append("Session Server");
254
255        } else {
256            sb.append("Session Client");
257        }
258
259        sb.append('[').append(session.getId()).append(']');
260
261        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
262
263        if (sslHandler == null) {
264            sb.append("(no sslEngine)");
265        } else if (isSslStarted(session)) {
266            if (sslHandler.isHandshakeComplete()) {
267                sb.append("(SSL)");
268            } else {
269                sb.append("(ssl...)");
270            }
271        }
272
273        return sb.toString();
274    }
275
276    /**
277     * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
278     * encrypted/decrypted over SSL/TLS currently. This method will start
279     * to return <tt>false</tt> after TLS <tt>close_notify</tt> message
280     * is sent and any messages written after then is not going to get encrypted.
281     * 
282     * @param session the session we want to check
283     */
284    public boolean isSslStarted(IoSession session) {
285        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
286
287        if (sslHandler == null) {
288            return false;
289        }
290
291        synchronized (sslHandler) {
292            return !sslHandler.isOutboundDone();
293        }
294    }
295
296    /**
297     * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
298     * initiate TLS closure.
299     *
300     * @param session the {@link IoSession} to initiate TLS closure
301     * @return The Future for the initiated closure
302     * @throws SSLException if failed to initiate TLS closure
303     */
304    public WriteFuture stopSsl(IoSession session) throws SSLException {
305        SslHandler sslHandler = getSslSessionHandler(session);
306        NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
307        WriteFuture future;
308
309        try {
310            synchronized (sslHandler) {
311                future = initiateClosure(nextFilter, session);
312            }
313
314            sslHandler.flushScheduledEvents();
315        } catch (SSLException se) {
316            sslHandler.release();
317            throw se;
318        }
319
320        return future;
321    }
322
323    /**
324     * @return <tt>true</tt> if the engine is set to use client mode
325     * when handshaking.
326     */
327    public boolean isUseClientMode() {
328        return client;
329    }
330
331    /**
332     * Configures the engine to use client (or server) mode when handshaking.
333     * 
334     * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode
335     */
336    public void setUseClientMode(boolean clientMode) {
337        this.client = clientMode;
338    }
339
340    /**
341     * @return <tt>true</tt> if the engine will <em>require</em> client authentication.
342     * This option is only useful to engines in the server mode.
343     */
344    public boolean isNeedClientAuth() {
345        return needClientAuth;
346    }
347
348    /**
349     * Configures the engine to <em>require</em> client authentication.
350     * This option is only useful for engines in the server mode.
351     * 
352     * @param needClientAuth A flag set when we need to authenticate the client
353     */
354    public void setNeedClientAuth(boolean needClientAuth) {
355        this.needClientAuth = needClientAuth;
356    }
357
358    /**
359     * @return <tt>true</tt> if the engine will <em>request</em> client authentication.
360     * This option is only useful to engines in the server mode.
361     */
362    public boolean isWantClientAuth() {
363        return wantClientAuth;
364    }
365
366    /**
367     * Configures the engine to <em>request</em> client authentication.
368     * This option is only useful for engines in the server mode.
369     * 
370     * @param wantClientAuth A flag set when we want to check the client authentication
371     */
372    public void setWantClientAuth(boolean wantClientAuth) {
373        this.wantClientAuth = wantClientAuth;
374    }
375
376    /**
377     * @return the list of cipher suites to be enabled when {@link SSLEngine}
378     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
379     */
380    public String[] getEnabledCipherSuites() {
381        return enabledCipherSuites;
382    }
383
384    /**
385     * Sets the list of cipher suites to be enabled when {@link SSLEngine}
386     * is initialized.
387     *
388     * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
389     */
390    public void setEnabledCipherSuites(String[] cipherSuites) {
391        this.enabledCipherSuites = cipherSuites;
392    }
393
394    /**
395     * @return the list of protocols to be enabled when {@link SSLEngine}
396     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
397     */
398    public String[] getEnabledProtocols() {
399        return enabledProtocols;
400    }
401
402    /**
403     * Sets the list of protocols to be enabled when {@link SSLEngine}
404     * is initialized.
405     *
406     * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
407     */
408    public void setEnabledProtocols(String[] protocols) {
409        this.enabledProtocols = protocols;
410    }
411
412    /**
413     * Executed just before the filter is added into the chain, we do :
414     * <ul>
415     * <li>check that we don't have a SSL filter already present
416     * <li>we update the next filter
417     * <li>we create the SSL handler helper class
418     * <li>and we store it into the session's Attributes
419     * </ul>
420     */
421    @Override
422    public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
423        // Check that we don't have a SSL filter already present in the chain
424        if (parent.contains(SslFilter.class)) {
425            String msg = "Only one SSL filter is permitted in a chain.";
426            LOGGER.error(msg);
427            throw new IllegalStateException(msg);
428        }
429
430        LOGGER.debug("Adding the SSL Filter {} to the chain", name);
431
432        IoSession session = parent.getSession();
433        session.setAttribute(NEXT_FILTER, nextFilter);
434
435        // Create a SSL handler and start handshake.
436        SslHandler sslHandler = new SslHandler(this, session);
437        sslHandler.init();
438
439        // Adding the supported ciphers in the SSLHandler
440        // In Java 6, we should call sslContext.getSupportedSSLParameters()
441        // instead
442        String[] ciphers = sslContext.getServerSocketFactory().getSupportedCipherSuites();
443        setEnabledCipherSuites(ciphers);
444        session.setAttribute(SSL_HANDLER, sslHandler);
445    }
446
447    @Override
448    public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
449        if (autoStart == START_HANDSHAKE) {
450            initiateHandshake(nextFilter, parent.getSession());
451        }
452    }
453
454    @Override
455    public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
456        IoSession session = parent.getSession();
457        stopSsl(session);
458        session.removeAttribute(NEXT_FILTER);
459        session.removeAttribute(SSL_HANDLER);
460    }
461
462    // IoFilter impl.
463    @Override
464    public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
465        SslHandler sslHandler = getSslSessionHandler(session);
466
467        try {
468            synchronized (sslHandler) {
469                // release resources
470                sslHandler.destroy();
471            }
472
473            sslHandler.flushScheduledEvents();
474        } finally {
475            // notify closed session
476            nextFilter.sessionClosed(session);
477        }
478    }
479
480    @Override
481    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
482        if (LOGGER.isDebugEnabled()) {
483            LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
484        }
485
486        SslHandler sslHandler = getSslSessionHandler(session);
487
488        synchronized (sslHandler) {
489            if (!isSslStarted(session) && sslHandler.isInboundDone()) {
490                // The SSL session must be established first before we
491                // can push data to the application. Store the incoming
492                // data into a queue for a later processing
493                sslHandler.scheduleMessageReceived(nextFilter, message);
494            } else {
495                IoBuffer buf = (IoBuffer) message;
496
497                try {
498                    // forward read encrypted data to SSL handler
499                    sslHandler.messageReceived(nextFilter, buf.buf());
500
501                    // Handle data to be forwarded to application or written to net
502                    handleSslData(nextFilter, sslHandler);
503
504                    if (sslHandler.isInboundDone()) {
505                        if (sslHandler.isOutboundDone()) {
506                            sslHandler.destroy();
507                        } else {
508                            initiateClosure(nextFilter, session);
509                        }
510
511                        if (buf.hasRemaining()) {
512                            // Forward the data received after closure.
513                            sslHandler.scheduleMessageReceived(nextFilter, buf);
514                        }
515                    }
516                } catch (SSLException ssle) {
517                    if (!sslHandler.isHandshakeComplete()) {
518                        SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
519                        newSsle.initCause(ssle);
520                        ssle = newSsle;
521                    } else {
522                        // Free the SSL Handler buffers
523                        sslHandler.release();
524                    }
525
526                    throw ssle;
527                }
528            }
529        }
530
531        sslHandler.flushScheduledEvents();
532    }
533
534    @Override
535    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
536        if (writeRequest instanceof EncryptedWriteRequest) {
537            EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
538            nextFilter.messageSent(session, wrappedRequest.getParentRequest());
539        } else {
540            // ignore extra buffers used for handshaking
541        }
542    }
543
544    @Override
545    public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
546
547        if (cause instanceof WriteToClosedSessionException) {
548            // Filter out SSL close notify, which is likely to fail to flush
549            // due to disconnection.
550            WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
551            List<WriteRequest> failedRequests = e.getRequests();
552            boolean containsCloseNotify = false;
553
554            for (WriteRequest r : failedRequests) {
555                if (isCloseNotify(r.getMessage())) {
556                    containsCloseNotify = true;
557                    break;
558                }
559            }
560
561            if (containsCloseNotify) {
562                if (failedRequests.size() == 1) {
563                    // close notify is the only failed request; bail out.
564                    return;
565                }
566
567                List<WriteRequest> newFailedRequests = new ArrayList<WriteRequest>(failedRequests.size() - 1);
568
569                for (WriteRequest r : failedRequests) {
570                    if (!isCloseNotify(r.getMessage())) {
571                        newFailedRequests.add(r);
572                    }
573                }
574
575                if (newFailedRequests.isEmpty()) {
576                    // the failedRequests were full with close notify; bail out.
577                    return;
578                }
579
580                cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
581            }
582        }
583
584        nextFilter.exceptionCaught(session, cause);
585    }
586
587    private boolean isCloseNotify(Object message) {
588        if (!(message instanceof IoBuffer)) {
589            return false;
590        }
591
592        IoBuffer buf = (IoBuffer) message;
593        int offset = buf.position();
594
595        return (buf.get(offset + 0) == 0x15) /* Alert */
596                && (buf.get(offset + 1) == 0x03) /* TLS/SSL */
597                && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
598                        || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
599                        || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
600                        || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
601                        && (buf.get(offset + 3) == 0x00); /* close_notify */
602    }
603
604    @Override
605    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
606        if (LOGGER.isDebugEnabled()) {
607            LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
608        }
609
610        boolean needsFlush = true;
611        SslHandler sslHandler = getSslSessionHandler(session);
612
613        try {
614            synchronized (sslHandler) {
615                if (!isSslStarted(session)) {
616                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
617                }
618                // Don't encrypt the data if encryption is disabled.
619                else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
620                    // Remove the marker attribute because it is temporary.
621                    session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
622                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
623                } else {
624                    // Otherwise, encrypt the buffer.
625                    IoBuffer buf = (IoBuffer) writeRequest.getMessage();
626
627                    if (sslHandler.isWritingEncryptedData()) {
628                        // data already encrypted; simply return buffer
629                        sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
630                    } else if (sslHandler.isHandshakeComplete()) {
631                        // SSL encrypt
632                        int pos = buf.position();
633                        sslHandler.encrypt(buf.buf());
634                        buf.position(pos);
635                        IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
636                        sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
637                                encryptedBuffer));
638                    } else {
639                        if (session.isConnected()) {
640                            // Handshake not complete yet.
641                            sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
642                        }
643
644                        needsFlush = false;
645                    }
646                }
647            }
648
649            if (needsFlush) {
650                sslHandler.flushScheduledEvents();
651            }
652        } catch (SSLException se) {
653            sslHandler.release();
654            throw se;
655        }
656    }
657
658    @Override
659    public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
660        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
661
662        if (sslHandler == null) {
663            // The connection might already have closed, or
664            // SSL might have not started yet.
665            nextFilter.filterClose(session);
666            return;
667        }
668
669        WriteFuture future = null;
670
671        try {
672            synchronized (sslHandler) {
673                if (isSslStarted(session)) {
674                    future = initiateClosure(nextFilter, session);
675                    future.addListener(new IoFutureListener<IoFuture>() {
676                        public void operationComplete(IoFuture future) {
677                            nextFilter.filterClose(session);
678                        }
679                    });
680                }
681            }
682
683            sslHandler.flushScheduledEvents();
684        } catch (SSLException se) {
685            sslHandler.release();
686            throw se;
687        } finally {
688            if (future == null) {
689                nextFilter.filterClose(session);
690            }
691        }
692    }
693
694    private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
695        LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
696        SslHandler sslHandler = getSslSessionHandler(session);
697
698        try {
699            synchronized (sslHandler) {
700                sslHandler.handshake(nextFilter);
701            }
702
703            sslHandler.flushScheduledEvents();
704        } catch (SSLException se) {
705            sslHandler.release();
706            throw se;
707        }
708    }
709
710    private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
711        SslHandler sslHandler = getSslSessionHandler(session);
712        WriteFuture future = null;
713
714        // if already shut down
715        try {
716            if (!sslHandler.closeOutbound()) {
717                return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
718                        "SSL session is shut down already."));
719            }
720
721            // there might be data to write out here?
722            future = sslHandler.writeNetBuffer(nextFilter);
723
724            if (future == null) {
725                future = DefaultWriteFuture.newWrittenFuture(session);
726            }
727
728            if (sslHandler.isInboundDone()) {
729                sslHandler.destroy();
730            }
731
732            if (session.containsAttribute(USE_NOTIFICATION)) {
733                sslHandler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
734            }
735        } catch (SSLException se) {
736            sslHandler.release();
737            throw se;
738        }
739
740        return future;
741    }
742
743    // Utilities
744    private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
745        if (LOGGER.isDebugEnabled()) {
746            LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
747        }
748
749        // Flush any buffered write requests occurred before handshaking.
750        if (sslHandler.isHandshakeComplete()) {
751            sslHandler.flushPreHandshakeEvents();
752        }
753
754        // Write encrypted data to be written (if any)
755        sslHandler.writeNetBuffer(nextFilter);
756
757        // handle app. data read (if any)
758        handleAppDataRead(nextFilter, sslHandler);
759    }
760
761    private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
762        // forward read app data
763        IoBuffer readBuffer = sslHandler.fetchAppBuffer();
764
765        if (readBuffer.hasRemaining()) {
766            sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
767        }
768    }
769
770    private SslHandler getSslSessionHandler(IoSession session) {
771        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
772
773        if (sslHandler == null) {
774            throw new IllegalStateException();
775        }
776
777        if (sslHandler.getSslFilter() != this) {
778            throw new IllegalArgumentException("Not managed by this filter.");
779        }
780
781        return sslHandler;
782    }
783
784    /**
785     * A message that is sent from {@link SslFilter} when the connection became
786     * secure or is not secure anymore.
787     *
788     * @author <a href="http://mina.apache.org">Apache MINA Project</a>
789     */
790    public static class SslFilterMessage {
791        private final String name;
792
793        private SslFilterMessage(String name) {
794            this.name = name;
795        }
796
797        @Override
798        public String toString() {
799            return name;
800        }
801    }
802
803    private static class EncryptedWriteRequest extends WriteRequestWrapper {
804        private final IoBuffer encryptedMessage;
805
806        private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
807            super(writeRequest);
808            this.encryptedMessage = encryptedMessage;
809        }
810
811        @Override
812        public Object getMessage() {
813            return encryptedMessage;
814        }
815    }
816}