View Javadoc

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