View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.handler.ssl;
17  
18  import org.jboss.netty.buffer.ChannelBuffer;
19  import org.jboss.netty.buffer.ChannelBuffers;
20  import org.jboss.netty.channel.Channel;
21  import org.jboss.netty.channel.ChannelDownstreamHandler;
22  import org.jboss.netty.channel.ChannelEvent;
23  import org.jboss.netty.channel.ChannelFuture;
24  import org.jboss.netty.channel.ChannelFutureListener;
25  import org.jboss.netty.channel.ChannelHandlerContext;
26  import org.jboss.netty.channel.ChannelPipeline;
27  import org.jboss.netty.channel.ChannelStateEvent;
28  import org.jboss.netty.channel.Channels;
29  import org.jboss.netty.channel.DefaultChannelFuture;
30  import org.jboss.netty.channel.DownstreamMessageEvent;
31  import org.jboss.netty.channel.ExceptionEvent;
32  import org.jboss.netty.channel.MessageEvent;
33  import org.jboss.netty.handler.codec.frame.FrameDecoder;
34  import org.jboss.netty.logging.InternalLogger;
35  import org.jboss.netty.logging.InternalLoggerFactory;
36  import org.jboss.netty.util.Timeout;
37  import org.jboss.netty.util.Timer;
38  import org.jboss.netty.util.TimerTask;
39  import org.jboss.netty.util.internal.DetectionUtil;
40  import org.jboss.netty.util.internal.NonReentrantLock;
41  
42  import javax.net.ssl.SSLEngine;
43  import javax.net.ssl.SSLEngineResult;
44  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
45  import javax.net.ssl.SSLEngineResult.Status;
46  import javax.net.ssl.SSLException;
47  import java.io.IOException;
48  import java.nio.ByteBuffer;
49  import java.nio.channels.ClosedChannelException;
50  import java.nio.channels.DatagramChannel;
51  import java.nio.channels.SocketChannel;
52  import java.util.LinkedList;
53  import java.util.Queue;
54  import java.util.concurrent.ConcurrentLinkedQueue;
55  import java.util.concurrent.Executor;
56  import java.util.concurrent.TimeUnit;
57  import java.util.concurrent.atomic.AtomicBoolean;
58  import java.util.regex.Pattern;
59  
60  import static org.jboss.netty.channel.Channels.*;
61  
62  /**
63   * Adds <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">SSL
64   * &middot; TLS</a> and StartTLS support to a {@link Channel}.  Please refer
65   * to the <strong>"SecureChat"</strong> example in the distribution or the web
66   * site for the detailed usage.
67   *
68   * <h3>Beginning the handshake</h3>
69   * <p>
70   * You must make sure not to write a message while the
71   * {@linkplain #handshake() handshake} is in progress unless you are
72   * renegotiating.  You will be notified by the {@link ChannelFuture} which is
73   * returned by the {@link #handshake()} method when the handshake
74   * process succeeds or fails.
75   *
76   * <h3>Handshake</h3>
77   * <p>
78   * If {@link #isIssueHandshake()} is {@code false}
79   * (default) you will need to take care of calling {@link #handshake()} by your own. In most
80   * situations were {@link SslHandler} is used in 'client mode' you want to issue a handshake once
81   * the connection was established. if {@link #setIssueHandshake(boolean)} is set to {@code true}
82   * you don't need to worry about this as the {@link SslHandler} will take care of it.
83   * <p>
84   *
85   * <h3>Renegotiation</h3>
86   * <p>
87   * If {@link #isEnableRenegotiation() enableRenegotiation} is {@code true}
88   * (default) and the initial handshake has been done successfully, you can call
89   * {@link #handshake()} to trigger the renegotiation.
90   * <p>
91   * If {@link #isEnableRenegotiation() enableRenegotiation} is {@code false},
92   * an attempt to trigger renegotiation will result in the connection closure.
93   * <p>
94   * Please note that TLS renegotiation had a security issue before.  If your
95   * runtime environment did not fix it, please make sure to disable TLS
96   * renegotiation by calling {@link #setEnableRenegotiation(boolean)} with
97   * {@code false}.  For more information, please refer to the following documents:
98   * <ul>
99   *   <li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555">CVE-2009-3555</a></li>
100  *   <li><a href="http://www.ietf.org/rfc/rfc5746.txt">RFC5746</a></li>
101  *   <li><a href="http://www.oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html">Phased
102  *       Approach to Fixing the TLS Renegotiation Issue</a></li>
103  * </ul>
104  *
105  * <h3>Closing the session</h3>
106  * <p>
107  * To close the SSL session, the {@link #close()} method should be
108  * called to send the {@code close_notify} message to the remote peer.  One
109  * exception is when you close the {@link Channel} - {@link SslHandler}
110  * intercepts the close request and send the {@code close_notify} message
111  * before the channel closure automatically.  Once the SSL session is closed,
112  * it is not reusable, and consequently you should create a new
113  * {@link SslHandler} with a new {@link SSLEngine} as explained in the
114  * following section.
115  *
116  * <h3>Restarting the session</h3>
117  * <p>
118  * To restart the SSL session, you must remove the existing closed
119  * {@link SslHandler} from the {@link ChannelPipeline}, insert a new
120  * {@link SslHandler} with a new {@link SSLEngine} into the pipeline,
121  * and start the handshake process as described in the first section.
122  *
123  * <h3>Implementing StartTLS</h3>
124  * <p>
125  * <a href="http://en.wikipedia.org/wiki/STARTTLS">StartTLS</a> is the
126  * communication pattern that secures the wire in the middle of the plaintext
127  * connection.  Please note that it is different from SSL &middot; TLS, that
128  * secures the wire from the beginning of the connection.  Typically, StartTLS
129  * is composed of three steps:
130  * <ol>
131  * <li>Client sends a StartTLS request to server.</li>
132  * <li>Server sends a StartTLS response to client.</li>
133  * <li>Client begins SSL handshake.</li>
134  * </ol>
135  * If you implement a server, you need to:
136  * <ol>
137  * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
138  *     to {@code true},</li>
139  * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
140  * <li>write a StartTLS response.</li>
141  * </ol>
142  * Please note that you must insert {@link SslHandler} <em>before</em> sending
143  * the StartTLS response.  Otherwise the client can send begin SSL handshake
144  * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing
145  * data corruption.
146  * <p>
147  * The client-side implementation is much simpler.
148  * <ol>
149  * <li>Write a StartTLS request,</li>
150  * <li>wait for the StartTLS response,</li>
151  * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
152  *     to {@code false},</li>
153  * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
154  * <li>Initiate SSL handshake by calling {@link SslHandler#handshake()}.</li>
155  * </ol>
156  *
157  * <h3>Known issues</h3>
158  * <p>
159  * Because of a known issue with the current implementation of the SslEngine that comes
160  * with Java it may be possible that you see blocked IO-Threads while a full GC is done.
161  * <p>
162  * So if you are affected you can workaround this problem by adjust the cache settings
163  * like shown below:
164  *
165  * <pre>
166  *     SslContext context = ...;
167  *     context.getServerSessionContext().setSessionCacheSize(someSaneSize);
168  *     context.getServerSessionContext().setSessionTime(someSameTimeout);
169  * </pre>
170  * <p>
171  * What values to use here depends on the nature of your application and should be set
172  * based on monitoring and debugging of it.
173  * For more details see
174  * <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
175  * @apiviz.landmark
176  * @apiviz.uses org.jboss.netty.handler.ssl.SslBufferPool
177  */
178 public class SslHandler extends FrameDecoder
179                         implements ChannelDownstreamHandler {
180 
181     private static final InternalLogger logger =
182         InternalLoggerFactory.getInstance(SslHandler.class);
183 
184     private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
185 
186     private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
187             "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
188     private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
189             "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
190 
191     private static SslBufferPool defaultBufferPool;
192 
193     /**
194      * Returns the default {@link SslBufferPool} used when no pool is
195      * specified in the constructor.
196      */
197     public static synchronized SslBufferPool getDefaultBufferPool() {
198         if (defaultBufferPool == null) {
199             defaultBufferPool = new SslBufferPool();
200         }
201         return defaultBufferPool;
202     }
203 
204     private volatile ChannelHandlerContext ctx;
205     private final SSLEngine engine;
206     private final SslBufferPool bufferPool;
207     private final Executor delegatedTaskExecutor;
208     private final boolean startTls;
209 
210     private volatile boolean enableRenegotiation = true;
211 
212     final Object handshakeLock = new Object();
213     private boolean handshaking;
214     private volatile boolean handshaken;
215     private volatile ChannelFuture handshakeFuture;
216 
217     private final AtomicBoolean sentFirstMessage = new AtomicBoolean();
218     private final AtomicBoolean sentCloseNotify = new AtomicBoolean();
219     int ignoreClosedChannelException;
220     final Object ignoreClosedChannelExceptionLock = new Object();
221     private final Queue<PendingWrite> pendingUnencryptedWrites = new LinkedList<PendingWrite>();
222     private final NonReentrantLock pendingUnencryptedWritesLock = new NonReentrantLock();
223     private final Queue<MessageEvent> pendingEncryptedWrites = new ConcurrentLinkedQueue<MessageEvent>();
224     private final NonReentrantLock pendingEncryptedWritesLock = new NonReentrantLock();
225 
226     private volatile boolean issueHandshake;
227 
228     private final SSLEngineInboundCloseFuture sslEngineCloseFuture = new SSLEngineInboundCloseFuture();
229 
230     private boolean closeOnSSLException;
231 
232     private int packetLength = Integer.MIN_VALUE;
233 
234     private final Timer timer;
235     private final long handshakeTimeoutInMillis;
236     private Timeout handshakeTimeout;
237 
238     /**
239      * Creates a new instance.
240      *
241      * @param engine  the {@link SSLEngine} this handler will use
242      */
243     public SslHandler(SSLEngine engine) {
244         this(engine, getDefaultBufferPool(), ImmediateExecutor.INSTANCE);
245     }
246 
247     /**
248      * Creates a new instance.
249      *
250      * @param engine      the {@link SSLEngine} this handler will use
251      * @param bufferPool  the {@link SslBufferPool} where this handler will
252      *                    acquire the buffers required by the {@link SSLEngine}
253      */
254     public SslHandler(SSLEngine engine, SslBufferPool bufferPool) {
255         this(engine, bufferPool, ImmediateExecutor.INSTANCE);
256     }
257 
258     /**
259      * Creates a new instance.
260      *
261      * @param engine    the {@link SSLEngine} this handler will use
262      * @param startTls  {@code true} if the first write request shouldn't be
263      *                  encrypted by the {@link SSLEngine}
264      */
265     public SslHandler(SSLEngine engine, boolean startTls) {
266         this(engine, getDefaultBufferPool(), startTls);
267     }
268 
269     /**
270      * Creates a new instance.
271      *
272      * @param engine      the {@link SSLEngine} this handler will use
273      * @param bufferPool  the {@link SslBufferPool} where this handler will
274      *                    acquire the buffers required by the {@link SSLEngine}
275      * @param startTls    {@code true} if the first write request shouldn't be
276      *                    encrypted by the {@link SSLEngine}
277      */
278     public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls) {
279         this(engine, bufferPool, startTls, ImmediateExecutor.INSTANCE);
280     }
281 
282     /**
283      * Creates a new instance.
284      *
285      * @param engine
286      *        the {@link SSLEngine} this handler will use
287      * @param delegatedTaskExecutor
288      *        the {@link Executor} which will execute the delegated task
289      *        that {@link SSLEngine#getDelegatedTask()} will return
290      */
291     public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) {
292         this(engine, getDefaultBufferPool(), delegatedTaskExecutor);
293     }
294 
295     /**
296      * Creates a new instance.
297      *
298      * @param engine
299      *        the {@link SSLEngine} this handler will use
300      * @param bufferPool
301      *        the {@link SslBufferPool} where this handler will acquire
302      *        the buffers required by the {@link SSLEngine}
303      * @param delegatedTaskExecutor
304      *        the {@link Executor} which will execute the delegated task
305      *        that {@link SSLEngine#getDelegatedTask()} will return
306      */
307     public SslHandler(SSLEngine engine, SslBufferPool bufferPool, Executor delegatedTaskExecutor) {
308         this(engine, bufferPool, false, delegatedTaskExecutor);
309     }
310 
311     /**
312      * Creates a new instance.
313      *
314      * @param engine
315      *        the {@link SSLEngine} this handler will use
316      * @param startTls
317      *        {@code true} if the first write request shouldn't be encrypted
318      *        by the {@link SSLEngine}
319      * @param delegatedTaskExecutor
320      *        the {@link Executor} which will execute the delegated task
321      *        that {@link SSLEngine#getDelegatedTask()} will return
322      */
323     public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) {
324         this(engine, getDefaultBufferPool(), startTls, delegatedTaskExecutor);
325     }
326 
327     /**
328      * Creates a new instance.
329      *
330      * @param engine
331      *        the {@link SSLEngine} this handler will use
332      * @param bufferPool
333      *        the {@link SslBufferPool} where this handler will acquire
334      *        the buffers required by the {@link SSLEngine}
335      * @param startTls
336      *        {@code true} if the first write request shouldn't be encrypted
337      *        by the {@link SSLEngine}
338      * @param delegatedTaskExecutor
339      *        the {@link Executor} which will execute the delegated task
340      *        that {@link SSLEngine#getDelegatedTask()} will return
341      */
342     public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls, Executor delegatedTaskExecutor) {
343         this(engine, bufferPool, startTls, delegatedTaskExecutor, null, 0);
344     }
345 
346     /**
347      * Creates a new instance.
348      *
349      * @param engine
350      *        the {@link SSLEngine} this handler will use
351      * @param bufferPool
352      *        the {@link SslBufferPool} where this handler will acquire
353      *        the buffers required by the {@link SSLEngine}
354      * @param startTls
355      *        {@code true} if the first write request shouldn't be encrypted
356      *        by the {@link SSLEngine}
357      * @param delegatedTaskExecutor
358      *        the {@link Executor} which will execute the delegated task
359      *        that {@link SSLEngine#getDelegatedTask()} will return
360      * @param timer
361      *        the {@link Timer} which will be used to process the timeout of the {@link #handshake()}.
362      *        Be aware that the given {@link Timer} will not get stopped automaticly, so it is up to you to cleanup
363      *        once you not need it anymore
364      * @param handshakeTimeoutInMillis
365      *        the time in milliseconds after whic the {@link #handshake()}  will be failed, and so the future notified
366      *
367      */
368     public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls, Executor delegatedTaskExecutor,
369                       Timer timer, long handshakeTimeoutInMillis) {
370         if (engine == null) {
371             throw new NullPointerException("engine");
372         }
373         if (bufferPool == null) {
374             throw new NullPointerException("bufferPool");
375         }
376         if (delegatedTaskExecutor == null) {
377             throw new NullPointerException("delegatedTaskExecutor");
378         }
379         if (timer == null && handshakeTimeoutInMillis > 0) {
380             throw new IllegalArgumentException("No Timer was given but a handshakeTimeoutInMillis, need both or none");
381         }
382 
383         this.engine = engine;
384         this.bufferPool = bufferPool;
385         this.delegatedTaskExecutor = delegatedTaskExecutor;
386         this.startTls = startTls;
387         this.timer = timer;
388         this.handshakeTimeoutInMillis = handshakeTimeoutInMillis;
389     }
390 
391     /**
392      * Returns the {@link SSLEngine} which is used by this handler.
393      */
394     public SSLEngine getEngine() {
395         return engine;
396     }
397 
398     /**
399      * Starts an SSL / TLS handshake for the specified channel.
400      *
401      * @return a {@link ChannelFuture} which is notified when the handshake
402      *         succeeds or fails.
403      */
404     public ChannelFuture handshake() {
405         synchronized (handshakeLock) {
406             if (handshaken && !isEnableRenegotiation()) {
407                 throw new IllegalStateException("renegotiation disabled");
408             }
409 
410             final ChannelHandlerContext ctx = this.ctx;
411             final Channel channel = ctx.getChannel();
412             ChannelFuture handshakeFuture;
413             Exception exception = null;
414 
415             if (handshaking) {
416                 return this.handshakeFuture;
417             }
418 
419             handshaking = true;
420             try {
421                 engine.beginHandshake();
422                 runDelegatedTasks();
423                 handshakeFuture = this.handshakeFuture = future(channel);
424                 if (handshakeTimeoutInMillis > 0) {
425                     handshakeTimeout = timer.newTimeout(new TimerTask() {
426                             public void run(Timeout timeout) throws Exception {
427                             ChannelFuture future = SslHandler.this.handshakeFuture;
428                             if (future != null && future.isDone()) {
429                                 return;
430                             }
431 
432                             setHandshakeFailure(channel, new SSLException("Handshake did not complete within " +
433                                             handshakeTimeoutInMillis + "ms"));
434                         }
435                         }, handshakeTimeoutInMillis, TimeUnit.MILLISECONDS);
436                 }
437             } catch (Exception e) {
438                 handshakeFuture = this.handshakeFuture = failedFuture(channel, e);
439                 exception = e;
440             }
441 
442             if (exception == null) { // Began handshake successfully.
443                 try {
444                     final ChannelFuture hsFuture = handshakeFuture;
445                     wrapNonAppData(ctx, channel).addListener(new ChannelFutureListener() {
446                         public void operationComplete(ChannelFuture future) throws Exception {
447                             if (!future.isSuccess()) {
448                                 Throwable cause = future.getCause();
449                                 hsFuture.setFailure(cause);
450 
451                                 fireExceptionCaught(ctx, cause);
452                                 if (closeOnSSLException) {
453                                     Channels.close(ctx, future(channel));
454                                 }
455                             }
456                         }
457                     });
458                 } catch (SSLException e) {
459                     handshakeFuture.setFailure(e);
460 
461                     fireExceptionCaught(ctx, e);
462                     if (closeOnSSLException) {
463                         Channels.close(ctx, future(channel));
464                     }
465                 }
466             } else { // Failed to initiate handshake.
467                 fireExceptionCaught(ctx, exception);
468                 if (closeOnSSLException) {
469                     Channels.close(ctx, future(channel));
470                 }
471             }
472             return handshakeFuture;
473         }
474     }
475 
476     /**
477      * @deprecated Use {@link #handshake()} instead.
478      */
479     @Deprecated
480     public ChannelFuture handshake(@SuppressWarnings("unused") Channel channel) {
481         return handshake();
482     }
483 
484     /**
485      * Sends an SSL {@code close_notify} message to the specified channel and
486      * destroys the underlying {@link SSLEngine}.
487      */
488     public ChannelFuture close() {
489         ChannelHandlerContext ctx = this.ctx;
490         Channel channel = ctx.getChannel();
491         try {
492             engine.closeOutbound();
493             return wrapNonAppData(ctx, channel);
494         } catch (SSLException e) {
495             fireExceptionCaught(ctx, e);
496             if (closeOnSSLException) {
497                 Channels.close(ctx, future(channel));
498             }
499             return failedFuture(channel, e);
500         }
501     }
502 
503     /**
504      * @deprecated Use {@link #close()} instead.
505      */
506     @Deprecated
507     public ChannelFuture close(@SuppressWarnings("unused") Channel channel) {
508         return close();
509     }
510 
511     /**
512      * Returns {@code true} if and only if TLS renegotiation is enabled.
513      */
514     public boolean isEnableRenegotiation() {
515         return enableRenegotiation;
516     }
517 
518     /**
519      * Enables or disables TLS renegotiation.
520      */
521     public void setEnableRenegotiation(boolean enableRenegotiation) {
522         this.enableRenegotiation = enableRenegotiation;
523     }
524 
525     /**
526      * Enables or disables the automatic handshake once the {@link Channel} is
527      * connected. The value will only have affect if its set before the
528      * {@link Channel} is connected.
529      */
530     public void setIssueHandshake(boolean issueHandshake) {
531         this.issueHandshake = issueHandshake;
532     }
533 
534     /**
535      * Returns {@code true} if the automatic handshake is enabled
536      */
537     public boolean isIssueHandshake() {
538         return issueHandshake;
539     }
540 
541     /**
542      * Return the {@link ChannelFuture} that will get notified if the inbound of the {@link SSLEngine} will get closed.
543      *
544      * This method will return the same {@link ChannelFuture} all the time.
545      *
546      * For more informations see the apidocs of {@link SSLEngine}
547      *
548      */
549     public ChannelFuture getSSLEngineInboundCloseFuture() {
550         return sslEngineCloseFuture;
551     }
552 
553     /**
554      * Return the timeout (in ms) after which the {@link ChannelFuture} of {@link #handshake()} will be failed, while
555      * a handshake is in progress
556      */
557     public long getHandshakeTimeout() {
558         return handshakeTimeoutInMillis;
559     }
560 
561     /**
562      * If set to {@code true}, the {@link Channel} will automatically get closed
563      * one a {@link SSLException} was caught. This is most times what you want, as after this
564      * its almost impossible to recover.
565      *
566      * Anyway the default is {@code false} to not break compatibility with older releases. This
567      * will be changed to {@code true} in the next major release.
568      *
569      */
570     public void setCloseOnSSLException(boolean closeOnSslException) {
571         if (ctx != null) {
572             throw new IllegalStateException("Can only get changed before attached to ChannelPipeline");
573         }
574         closeOnSSLException = closeOnSslException;
575     }
576 
577     public boolean getCloseOnSSLException() {
578         return closeOnSSLException;
579     }
580 
581     public void handleDownstream(
582             final ChannelHandlerContext context, final ChannelEvent evt) throws Exception {
583         if (evt instanceof ChannelStateEvent) {
584             ChannelStateEvent e = (ChannelStateEvent) evt;
585             switch (e.getState()) {
586             case OPEN:
587             case CONNECTED:
588             case BOUND:
589                 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
590                     closeOutboundAndChannel(context, e);
591                     return;
592                 }
593             }
594         }
595         if (!(evt instanceof MessageEvent)) {
596             context.sendDownstream(evt);
597             return;
598         }
599 
600         MessageEvent e = (MessageEvent) evt;
601         if (!(e.getMessage() instanceof ChannelBuffer)) {
602             context.sendDownstream(evt);
603             return;
604         }
605 
606         // Do not encrypt the first write request if this handler is
607         // created with startTLS flag turned on.
608         if (startTls && sentFirstMessage.compareAndSet(false, true)) {
609             context.sendDownstream(evt);
610             return;
611         }
612 
613         // Otherwise, all messages are encrypted.
614         ChannelBuffer msg = (ChannelBuffer) e.getMessage();
615         PendingWrite pendingWrite;
616 
617         if (msg.readable()) {
618             pendingWrite = new PendingWrite(evt.getFuture(), msg.toByteBuffer(msg.readerIndex(), msg.readableBytes()));
619         } else {
620             pendingWrite = new PendingWrite(evt.getFuture(), null);
621         }
622 
623         pendingUnencryptedWritesLock.lock();
624         try {
625             pendingUnencryptedWrites.add(pendingWrite);
626         } finally {
627             pendingUnencryptedWritesLock.unlock();
628         }
629 
630         wrap(context, evt.getChannel());
631     }
632 
633     private void cancelHandshakeTimeout() {
634         if (handshakeTimeout != null) {
635             // cancel the task as we will fail the handshake future now
636             handshakeTimeout.cancel();
637         }
638     }
639 
640     @Override
641     public void channelDisconnected(ChannelHandlerContext ctx,
642             ChannelStateEvent e) throws Exception {
643 
644         // Make sure the handshake future is notified when a connection has
645         // been closed during handshake.
646         synchronized (handshakeLock) {
647             if (handshaking) {
648                 cancelHandshakeTimeout();
649                 handshakeFuture.setFailure(new ClosedChannelException());
650             }
651         }
652 
653         try {
654             super.channelDisconnected(ctx, e);
655         } finally {
656             unwrap(ctx, e.getChannel(), ChannelBuffers.EMPTY_BUFFER, 0, 0);
657             engine.closeOutbound();
658             if (!sentCloseNotify.get() && handshaken) {
659                 try {
660                     engine.closeInbound();
661                 } catch (SSLException ex) {
662                     if (logger.isDebugEnabled()) {
663                         logger.debug("Failed to clean up SSLEngine.", ex);
664                     }
665                 }
666             }
667         }
668     }
669     @Override
670     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
671             throws Exception {
672 
673         Throwable cause = e.getCause();
674         if (cause instanceof IOException) {
675             if (cause instanceof ClosedChannelException) {
676                 synchronized (ignoreClosedChannelExceptionLock) {
677                     if (ignoreClosedChannelException > 0) {
678                         ignoreClosedChannelException --;
679                         if (logger.isDebugEnabled()) {
680                             logger.debug(
681                                     "Swallowing an exception raised while " +
682                                     "writing non-app data", cause);
683                         }
684 
685                         return;
686                     }
687                 }
688             } else {
689                 if (ignoreException(cause)) {
690                     return;
691                 }
692             }
693         }
694 
695         ctx.sendUpstream(e);
696     }
697 
698     /**
699      * Checks if the given {@link Throwable} can be ignore and just "swallowed"
700      *
701      * When an ssl connection is closed a close_notify message is sent.
702      * After that the peer also sends close_notify however, it's not mandatory to receive
703      * the close_notify. The party who sent the initial close_notify can close the connection immediately
704      * then the peer will get connection reset error.
705      *
706      */
707     private boolean ignoreException(Throwable t) {
708         if (!(t instanceof SSLException) && t instanceof IOException && engine.isOutboundDone()) {
709             String message = String.valueOf(t.getMessage()).toLowerCase();
710 
711             // first try to match connection reset / broke peer based on the regex. This is the fastest way
712             // but may fail on different jdk impls or OS's
713             if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
714                 return true;
715             }
716 
717             // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
718             StackTraceElement[] elements = t.getStackTrace();
719             for (StackTraceElement element: elements) {
720                 String classname = element.getClassName();
721                 String methodname = element.getMethodName();
722 
723                 // skip all classes that belong to the io.netty package
724                 if (classname.startsWith("org.jboss.netty.")) {
725                     continue;
726                 }
727 
728                 // check if the method name is read if not skip it
729                 if (!"read".equals(methodname)) {
730                     continue;
731                 }
732 
733                 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
734                 // also others
735                 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
736                     return true;
737                 }
738 
739                 try {
740                     // No match by now.. Try to load the class via classloader and inspect it.
741                     // This is mainly done as other JDK implementations may differ in name of
742                     // the impl.
743                     Class<?> clazz = getClass().getClassLoader().loadClass(classname);
744 
745                     if (SocketChannel.class.isAssignableFrom(clazz)
746                             || DatagramChannel.class.isAssignableFrom(clazz)) {
747                         return true;
748                     }
749 
750                     // also match against SctpChannel via String matching as it may not present.
751                     if (DetectionUtil.javaVersion() >= 7
752                             && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
753                         return true;
754                     }
755                 } catch (ClassNotFoundException e) {
756                     // This should not happen just ignore
757                 }
758             }
759         }
760 
761         return false;
762     }
763 
764     /**
765      * Returns {@code true} if the given {@link ChannelBuffer} is encrypted. Be aware that this method
766      * will not increase the readerIndex of the given {@link ChannelBuffer}.
767      *
768      * @param   buffer
769      *                  The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
770      *                  otherwise it will throw an {@link IllegalArgumentException}.
771      * @return encrypted
772      *                  {@code true} if the {@link ChannelBuffer} is encrypted, {@code false} otherwise.
773      * @throws IllegalArgumentException
774      *                  Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
775      */
776     public static boolean isEncrypted(ChannelBuffer buffer) {
777         return getEncryptedPacketLength(buffer) != -1;
778     }
779 
780     /**
781      * Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
782      * the readerIndex of the given {@link ChannelBuffer}.
783      *
784      * @param   buffer
785      *                  The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
786      *                  otherwise it will throw an {@link IllegalArgumentException}.
787      * @return length
788      *                  The length of the encrypted packet that is included in the buffer. This will
789      *                  return {@code -1} if the given {@link ChannelBuffer} is not encrypted at all.
790      * @throws IllegalArgumentException
791      *                  Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
792      */
793     private static int getEncryptedPacketLength(ChannelBuffer buffer) {
794         if (buffer.readableBytes() < 5) {
795             throw new IllegalArgumentException("buffer must have at least 5 readable bytes");
796         }
797 
798         int packetLength = 0;
799 
800         // SSLv3 or TLS - Check ContentType
801         boolean tls;
802         switch (buffer.getUnsignedByte(buffer.readerIndex())) {
803         case 20:  // change_cipher_spec
804         case 21:  // alert
805         case 22:  // handshake
806         case 23:  // application_data
807             tls = true;
808             break;
809         default:
810             // SSLv2 or bad data
811             tls = false;
812         }
813 
814         if (tls) {
815             // SSLv3 or TLS - Check ProtocolVersion
816             int majorVersion = buffer.getUnsignedByte(buffer.readerIndex() + 1);
817             if (majorVersion == 3) {
818                 // SSLv3 or TLS
819                 packetLength = (getShort(buffer, buffer.readerIndex() + 3) & 0xFFFF) + 5;
820                 if (packetLength <= 5) {
821                     // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
822                     tls = false;
823                 }
824             } else {
825                 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
826                 tls = false;
827             }
828         }
829 
830         if (!tls) {
831             // SSLv2 or bad data - Check the version
832             boolean sslv2 = true;
833             int headerLength = (buffer.getUnsignedByte(
834                     buffer.readerIndex()) & 0x80) != 0 ? 2 : 3;
835             int majorVersion = buffer.getUnsignedByte(
836                     buffer.readerIndex() + headerLength + 1);
837             if (majorVersion == 2 || majorVersion == 3) {
838                 // SSLv2
839                 if (headerLength == 2) {
840                     packetLength = (getShort(buffer, buffer.readerIndex()) & 0x7FFF) + 2;
841                 } else {
842                     packetLength = (getShort(buffer, buffer.readerIndex()) & 0x3FFF) + 3;
843                 }
844                 if (packetLength <= headerLength) {
845                     sslv2 = false;
846                 }
847             } else {
848                 sslv2 = false;
849             }
850 
851             if (!sslv2) {
852                 return -1;
853             }
854         }
855         return packetLength;
856     }
857 
858     @Override
859     protected Object decode(
860             final ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
861 
862         // Check if the packet length was parsed yet, if so we can skip the parsing
863         if (packetLength == Integer.MIN_VALUE) {
864             if (buffer.readableBytes() < 5) {
865                 return null;
866             }
867             int packetLength = getEncryptedPacketLength(buffer);
868 
869             if (packetLength == -1) {
870                 // Bad data - discard the buffer and raise an exception.
871                 NotSslRecordException e = new NotSslRecordException(
872                         "not an SSL/TLS record: " + ChannelBuffers.hexDump(buffer));
873                 buffer.skipBytes(buffer.readableBytes());
874 
875                 if (closeOnSSLException) {
876                     // first trigger the exception and then close the channel
877                     fireExceptionCaught(ctx, e);
878                     Channels.close(ctx, future(channel));
879 
880                     // just return null as we closed the channel before, that
881                     // will take care of cleanup etc
882                     return null;
883                 } else {
884                     throw e;
885                 }
886             }
887 
888             assert packetLength > 0;
889             this.packetLength = packetLength;
890         }
891 
892         if (buffer.readableBytes() < packetLength) {
893             return null;
894         }
895 
896         // We advance the buffer's readerIndex before calling unwrap() because
897         // unwrap() can trigger FrameDecoder call decode(), this method, recursively.
898         // The recursive call results in decoding the same packet twice if
899         // the readerIndex is advanced *after* decode().
900         //
901         // Here's an example:
902         // 1) An SSL packet is received from the wire.
903         // 2) SslHandler.decode() deciphers the packet and calls the user code.
904         // 3) The user closes the channel in the same thread.
905         // 4) The same thread triggers a channelDisconnected() event.
906         // 5) FrameDecoder.cleanup() is called, and it calls SslHandler.decode().
907         // 6) SslHandler.decode() will feed the same packet with what was
908         //    deciphered at the step 2 again if the readerIndex was not advanced
909         //    before calling the user code.
910         final int packetOffset = buffer.readerIndex();
911         buffer.skipBytes(packetLength);
912         try {
913             return unwrap(ctx, channel, buffer, packetOffset, packetLength);
914         } finally {
915             // reset the packet length so it will be parsed again on the next call
916             packetLength = Integer.MIN_VALUE;
917         }
918     }
919 
920     /**
921      * Reads a big-endian short integer from the buffer.  Please note that we do not use
922      * {@link ChannelBuffer#getShort(int)} because it might be a little-endian buffer.
923      */
924     private static short getShort(ChannelBuffer buf, int offset) {
925         return (short) (buf.getByte(offset) << 8 | buf.getByte(offset + 1) & 0xFF);
926     }
927 
928     private void wrap(ChannelHandlerContext context, Channel channel)
929             throws SSLException {
930 
931         ChannelBuffer msg;
932         ByteBuffer outNetBuf = bufferPool.acquireBuffer();
933         boolean success = true;
934         boolean offered = false;
935         boolean needsUnwrap = false;
936         PendingWrite pendingWrite = null;
937 
938         try {
939             loop:
940             for (;;) {
941                 // Acquire a lock to make sure unencrypted data is polled
942                 // in order and their encrypted counterpart is offered in
943                 // order.
944                 pendingUnencryptedWritesLock.lock();
945                 try {
946                     pendingWrite = pendingUnencryptedWrites.peek();
947                     if (pendingWrite == null) {
948                         break;
949                     }
950 
951                     ByteBuffer outAppBuf = pendingWrite.outAppBuf;
952                     if (outAppBuf == null) {
953                         // A write request with an empty buffer
954                         pendingUnencryptedWrites.remove();
955                         offerEncryptedWriteRequest(
956                                 new DownstreamMessageEvent(
957                                         channel, pendingWrite.future,
958                                         ChannelBuffers.EMPTY_BUFFER,
959                                         channel.getRemoteAddress()));
960                         offered = true;
961                     } else {
962                         synchronized (handshakeLock) {
963                             SSLEngineResult result = null;
964                             try {
965                                 result = engine.wrap(outAppBuf, outNetBuf);
966                             } finally {
967                                 if (!outAppBuf.hasRemaining()) {
968                                     pendingUnencryptedWrites.remove();
969                                 }
970                             }
971 
972                             if (result.bytesProduced() > 0) {
973                                 outNetBuf.flip();
974                                 int remaining = outNetBuf.remaining();
975                                 msg = ctx.getChannel().getConfig().getBufferFactory().getBuffer(remaining);
976 
977                                 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
978                                 // work with "non" heap buffers
979                                 //
980                                 // See https://github.com/netty/netty/issues/329
981                                 msg.writeBytes(outNetBuf);
982                                 outNetBuf.clear();
983 
984                                 ChannelFuture future;
985                                 if (pendingWrite.outAppBuf.hasRemaining()) {
986                                     // pendingWrite's future shouldn't be notified if
987                                     // only partial data is written.
988                                     future = succeededFuture(channel);
989                                 } else {
990                                     future = pendingWrite.future;
991                                 }
992 
993                                 MessageEvent encryptedWrite = new DownstreamMessageEvent(
994                                         channel, future, msg, channel.getRemoteAddress());
995                                 offerEncryptedWriteRequest(encryptedWrite);
996                                 offered = true;
997                             } else if (result.getStatus() == Status.CLOSED) {
998                                 // SSLEngine has been closed already.
999                                 // Any further write attempts should be denied.
1000                                 success = false;
1001                                 break;
1002                             } else {
1003                                 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1004                                 handleRenegotiation(handshakeStatus);
1005                                 switch (handshakeStatus) {
1006                                 case NEED_WRAP:
1007                                     if (outAppBuf.hasRemaining()) {
1008                                         break;
1009                                     } else {
1010                                         break loop;
1011                                     }
1012                                 case NEED_UNWRAP:
1013                                     needsUnwrap = true;
1014                                     break loop;
1015                                 case NEED_TASK:
1016                                     runDelegatedTasks();
1017                                     break;
1018                                 case FINISHED:
1019                                 case NOT_HANDSHAKING:
1020                                     if (handshakeStatus == HandshakeStatus.FINISHED) {
1021                                         setHandshakeSuccess(channel);
1022                                     }
1023                                     if (result.getStatus() == Status.CLOSED) {
1024                                         success = false;
1025                                     }
1026                                     break loop;
1027                                 default:
1028                                     throw new IllegalStateException(
1029                                             "Unknown handshake status: " +
1030                                             handshakeStatus);
1031                                 }
1032                             }
1033                         }
1034                     }
1035                 } finally {
1036                     pendingUnencryptedWritesLock.unlock();
1037                 }
1038             }
1039         } catch (SSLException e) {
1040             success = false;
1041             setHandshakeFailure(channel, e);
1042             throw e;
1043         } finally {
1044             bufferPool.releaseBuffer(outNetBuf);
1045 
1046             if (offered) {
1047                 flushPendingEncryptedWrites(context);
1048             }
1049 
1050             if (!success) {
1051                 IllegalStateException cause =
1052                     new IllegalStateException("SSLEngine already closed");
1053 
1054                 // Check if we had a pendingWrite in process, if so we need to also notify as otherwise
1055                 // the ChannelFuture will never get notified
1056                 if (pendingWrite != null) {
1057                     pendingWrite.future.setFailure(cause);
1058                 }
1059 
1060                 // Mark all remaining pending writes as failure if anything
1061                 // wrong happened before the write requests are wrapped.
1062                 // Please note that we do not call setFailure while a lock is
1063                 // acquired, to avoid a potential dead lock.
1064                 for (;;) {
1065                     pendingUnencryptedWritesLock.lock();
1066                     try {
1067                         pendingWrite = pendingUnencryptedWrites.poll();
1068                         if (pendingWrite == null) {
1069                             break;
1070                         }
1071                     } finally {
1072                         pendingUnencryptedWritesLock.unlock();
1073                     }
1074 
1075                     pendingWrite.future.setFailure(cause);
1076                 }
1077             }
1078         }
1079 
1080         if (needsUnwrap) {
1081             unwrap(context, channel, ChannelBuffers.EMPTY_BUFFER, 0, 0);
1082         }
1083     }
1084 
1085     private void offerEncryptedWriteRequest(MessageEvent encryptedWrite) {
1086         final boolean locked = pendingEncryptedWritesLock.tryLock();
1087         try {
1088             pendingEncryptedWrites.add(encryptedWrite);
1089         } finally {
1090             if (locked) {
1091                 pendingEncryptedWritesLock.unlock();
1092             }
1093         }
1094     }
1095 
1096     private void flushPendingEncryptedWrites(ChannelHandlerContext ctx) {
1097         while (!pendingEncryptedWrites.isEmpty()) {
1098             // Avoid possible dead lock and data integrity issue
1099             // which is caused by cross communication between more than one channel
1100             // in the same VM.
1101             if (!pendingEncryptedWritesLock.tryLock()) {
1102                 return;
1103             }
1104 
1105             try {
1106                 MessageEvent e;
1107                 while ((e = pendingEncryptedWrites.poll()) != null) {
1108                     ctx.sendDownstream(e);
1109                 }
1110             } finally {
1111                 pendingEncryptedWritesLock.unlock();
1112             }
1113 
1114             // Other thread might have added more elements at this point, so we loop again if the queue got unempty.
1115         }
1116     }
1117 
1118     private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel) throws SSLException {
1119         ChannelFuture future = null;
1120         ByteBuffer outNetBuf = bufferPool.acquireBuffer();
1121 
1122         SSLEngineResult result;
1123         try {
1124             for (;;) {
1125                 synchronized (handshakeLock) {
1126                     result = engine.wrap(EMPTY_BUFFER, outNetBuf);
1127                 }
1128 
1129                 if (result.bytesProduced() > 0) {
1130                     outNetBuf.flip();
1131                     ChannelBuffer msg =
1132                             ctx.getChannel().getConfig().getBufferFactory().getBuffer(outNetBuf.remaining());
1133 
1134                     // Transfer the bytes to the new ChannelBuffer using some safe method that will also
1135                     // work with "non" heap buffers
1136                     //
1137                     // See https://github.com/netty/netty/issues/329
1138                     msg.writeBytes(outNetBuf);
1139                     outNetBuf.clear();
1140 
1141                     future = future(channel);
1142                     future.addListener(new ChannelFutureListener() {
1143                         public void operationComplete(ChannelFuture future)
1144                                 throws Exception {
1145                             if (future.getCause() instanceof ClosedChannelException) {
1146                                 synchronized (ignoreClosedChannelExceptionLock) {
1147                                     ignoreClosedChannelException ++;
1148                                 }
1149                             }
1150                         }
1151                     });
1152 
1153                     write(ctx, future, msg);
1154                 }
1155 
1156                 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1157                 handleRenegotiation(handshakeStatus);
1158                 switch (handshakeStatus) {
1159                 case FINISHED:
1160                     setHandshakeSuccess(channel);
1161                     runDelegatedTasks();
1162                     break;
1163                 case NEED_TASK:
1164                     runDelegatedTasks();
1165                     break;
1166                 case NEED_UNWRAP:
1167                     if (!Thread.holdsLock(handshakeLock)) {
1168                         // unwrap shouldn't be called when this method was
1169                         // called by unwrap - unwrap will keep running after
1170                         // this method returns.
1171                         unwrap(ctx, channel, ChannelBuffers.EMPTY_BUFFER, 0, 0);
1172                     }
1173                     break;
1174                 case NOT_HANDSHAKING:
1175                 case NEED_WRAP:
1176                     break;
1177                 default:
1178                     throw new IllegalStateException(
1179                             "Unexpected handshake status: " + handshakeStatus);
1180                 }
1181 
1182                 if (result.bytesProduced() == 0) {
1183                     break;
1184                 }
1185             }
1186         } catch (SSLException e) {
1187             setHandshakeFailure(channel, e);
1188             throw e;
1189         } finally {
1190             bufferPool.releaseBuffer(outNetBuf);
1191         }
1192 
1193         if (future == null) {
1194             future = succeededFuture(channel);
1195         }
1196 
1197         return future;
1198     }
1199 
1200     private ChannelBuffer unwrap(
1201             ChannelHandlerContext ctx, Channel channel,
1202             ChannelBuffer buffer, int offset, int length) throws SSLException {
1203         ByteBuffer inNetBuf = buffer.toByteBuffer(offset, length);
1204         ByteBuffer outAppBuf = bufferPool.acquireBuffer();
1205 
1206         try {
1207             boolean needsWrap = false;
1208             loop:
1209             for (;;) {
1210                 SSLEngineResult result;
1211                 boolean needsHandshake = false;
1212                 synchronized (handshakeLock) {
1213                     if (!handshaken && !handshaking &&
1214                         !engine.getUseClientMode() &&
1215                         !engine.isInboundDone() && !engine.isOutboundDone()) {
1216                         needsHandshake = true;
1217                     }
1218                 }
1219 
1220                 if (needsHandshake) {
1221                     handshake();
1222                 }
1223 
1224                 synchronized (handshakeLock) {
1225                     result = engine.unwrap(inNetBuf, outAppBuf);
1226 
1227                     switch (result.getStatus()) {
1228                         case CLOSED:
1229                             // notify about the CLOSED state of the SSLEngine. See #137
1230                             sslEngineCloseFuture.setClosed();
1231                             break;
1232                         case BUFFER_OVERFLOW:
1233                             throw new SSLException("SSLEngine.unwrap() reported an impossible buffer overflow.");
1234                     }
1235 
1236                     final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1237                     handleRenegotiation(handshakeStatus);
1238                     switch (handshakeStatus) {
1239                     case NEED_UNWRAP:
1240                         if (inNetBuf.hasRemaining() && !engine.isInboundDone()) {
1241                             break;
1242                         } else {
1243                             break loop;
1244                         }
1245                     case NEED_WRAP:
1246                         wrapNonAppData(ctx, channel);
1247                         break;
1248                     case NEED_TASK:
1249                         runDelegatedTasks();
1250                         break;
1251                     case FINISHED:
1252                         setHandshakeSuccess(channel);
1253                         needsWrap = true;
1254                         break loop;
1255                     case NOT_HANDSHAKING:
1256                         needsWrap = true;
1257                         break loop;
1258                     default:
1259                         throw new IllegalStateException(
1260                                 "Unknown handshake status: " + handshakeStatus);
1261                     }
1262                 }
1263             }
1264             if (needsWrap) {
1265                 // wrap() acquires pendingUnencryptedWrites first and then
1266                 // handshakeLock.  If handshakeLock is already hold by the
1267                 // current thread, calling wrap() will lead to a dead lock
1268                 // i.e. pendingUnencryptedWrites -> handshakeLock vs.
1269                 //      handshakeLock -> pendingUnencryptedLock -> handshakeLock
1270                 //
1271                 // There is also the same issue between pendingEncryptedWrites
1272                 // and pendingUnencryptedWrites.
1273                 if (!Thread.holdsLock(handshakeLock) &&
1274                         !pendingEncryptedWritesLock.isHeldByCurrentThread()) {
1275                     wrap(ctx, channel);
1276                 }
1277             }
1278             outAppBuf.flip();
1279 
1280             if (outAppBuf.hasRemaining()) {
1281                 ChannelBuffer frame = ctx.getChannel().getConfig().getBufferFactory().getBuffer(outAppBuf.remaining());
1282                 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
1283                 // work with "non" heap buffers
1284                 //
1285                 // See https://github.com/netty/netty/issues/329
1286                 frame.writeBytes(outAppBuf);
1287 
1288                 return frame;
1289             } else {
1290                 return null;
1291             }
1292         } catch (SSLException e) {
1293             setHandshakeFailure(channel, e);
1294             throw e;
1295         } finally {
1296             bufferPool.releaseBuffer(outAppBuf);
1297         }
1298     }
1299 
1300     private void handleRenegotiation(HandshakeStatus handshakeStatus) {
1301         synchronized (handshakeLock) {
1302             if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING ||
1303                 handshakeStatus == HandshakeStatus.FINISHED) {
1304                 // Not handshaking
1305                 return;
1306             }
1307 
1308             if (!handshaken) {
1309                 // Not renegotiation
1310                 return;
1311             }
1312 
1313             final boolean renegotiate;
1314             if (handshaking) {
1315                 // Renegotiation in progress or failed already.
1316                 // i.e. Renegotiation check has been done already below.
1317                 return;
1318             }
1319 
1320             if (engine.isInboundDone() || engine.isOutboundDone()) {
1321                 // Not handshaking but closing.
1322                 return;
1323             }
1324 
1325             if (isEnableRenegotiation()) {
1326                 // Continue renegotiation.
1327                 renegotiate = true;
1328             } else {
1329                 // Do not renegotiate.
1330                 renegotiate = false;
1331                 // Prevent reentrance of this method.
1332                 handshaking = true;
1333             }
1334 
1335             if (renegotiate) {
1336                 // Renegotiate.
1337                 handshake();
1338             } else {
1339                 // Raise an exception.
1340                 fireExceptionCaught(
1341                         ctx, new SSLException(
1342                                 "renegotiation attempted by peer; " +
1343                                 "closing the connection"));
1344 
1345                 // Close the connection to stop renegotiation.
1346                 Channels.close(ctx, succeededFuture(ctx.getChannel()));
1347             }
1348         }
1349     }
1350 
1351     private void runDelegatedTasks() {
1352         for (;;) {
1353             final Runnable task;
1354             synchronized (handshakeLock) {
1355                 task = engine.getDelegatedTask();
1356             }
1357 
1358             if (task == null) {
1359                 break;
1360             }
1361 
1362             delegatedTaskExecutor.execute(new Runnable() {
1363                 public void run() {
1364                     synchronized (handshakeLock) {
1365                         task.run();
1366                     }
1367                 }
1368             });
1369         }
1370     }
1371 
1372     private void setHandshakeSuccess(Channel channel) {
1373         synchronized (handshakeLock) {
1374             handshaking = false;
1375             handshaken = true;
1376 
1377             if (handshakeFuture == null) {
1378                 handshakeFuture = future(channel);
1379             }
1380             cancelHandshakeTimeout();
1381         }
1382 
1383         handshakeFuture.setSuccess();
1384     }
1385 
1386     private void setHandshakeFailure(Channel channel, SSLException cause) {
1387         synchronized (handshakeLock) {
1388             if (!handshaking) {
1389                 return;
1390             }
1391             handshaking = false;
1392             handshaken = false;
1393 
1394             if (handshakeFuture == null) {
1395                 handshakeFuture = future(channel);
1396             }
1397 
1398             // cancel the timeout now
1399             cancelHandshakeTimeout();
1400 
1401             // Release all resources such as internal buffers that SSLEngine
1402             // is managing.
1403 
1404             engine.closeOutbound();
1405 
1406             try {
1407                 engine.closeInbound();
1408             } catch (SSLException e) {
1409                 if (logger.isDebugEnabled()) {
1410                     logger.debug(
1411                             "SSLEngine.closeInbound() raised an exception after " +
1412                             "a handshake failure.", e);
1413                 }
1414             }
1415         }
1416 
1417         handshakeFuture.setFailure(cause);
1418         if (closeOnSSLException) {
1419             Channels.close(ctx, future(channel));
1420         }
1421     }
1422 
1423     private void closeOutboundAndChannel(
1424             final ChannelHandlerContext context, final ChannelStateEvent e) {
1425         if (!e.getChannel().isConnected()) {
1426             context.sendDownstream(e);
1427             return;
1428         }
1429 
1430         boolean passthrough = true;
1431         try {
1432             try {
1433                 unwrap(context, e.getChannel(), ChannelBuffers.EMPTY_BUFFER, 0, 0);
1434             } catch (SSLException ex) {
1435                 if (logger.isDebugEnabled()) {
1436                     logger.debug("Failed to unwrap before sending a close_notify message", ex);
1437                 }
1438             }
1439 
1440             if (!engine.isOutboundDone()) {
1441                 if (sentCloseNotify.compareAndSet(false, true)) {
1442                     engine.closeOutbound();
1443                     try {
1444                         ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel());
1445                         closeNotifyFuture.addListener(
1446                                 new ClosingChannelFutureListener(context, e));
1447                         passthrough = false;
1448                     } catch (SSLException ex) {
1449                         if (logger.isDebugEnabled()) {
1450                             logger.debug("Failed to encode a close_notify message", ex);
1451                         }
1452                     }
1453                 }
1454             }
1455         } finally {
1456             if (passthrough) {
1457                 context.sendDownstream(e);
1458             }
1459         }
1460     }
1461 
1462     private static final class PendingWrite {
1463         final ChannelFuture future;
1464         final ByteBuffer outAppBuf;
1465 
1466         PendingWrite(ChannelFuture future, ByteBuffer outAppBuf) {
1467             this.future = future;
1468             this.outAppBuf = outAppBuf;
1469         }
1470     }
1471 
1472     private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1473 
1474         private final ChannelHandlerContext context;
1475         private final ChannelStateEvent e;
1476 
1477         ClosingChannelFutureListener(
1478                 ChannelHandlerContext context, ChannelStateEvent e) {
1479             this.context = context;
1480             this.e = e;
1481         }
1482 
1483         public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception {
1484             if (!(closeNotifyFuture.getCause() instanceof ClosedChannelException)) {
1485                 Channels.close(context, e.getFuture());
1486             } else {
1487                 e.getFuture().setSuccess();
1488             }
1489         }
1490     }
1491 
1492     @Override
1493     public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
1494         super.beforeAdd(ctx);
1495         this.ctx = ctx;
1496     }
1497 
1498     /**
1499      * Fail all pending writes which we were not able to flush out
1500      */
1501     @Override
1502     public void afterRemove(ChannelHandlerContext ctx) throws Exception {
1503 
1504         // there is no need for synchronization here as we do not receive downstream events anymore
1505         Throwable cause = null;
1506         for (;;) {
1507             PendingWrite pw = pendingUnencryptedWrites.poll();
1508             if (pw == null) {
1509                 break;
1510             }
1511             if (cause == null) {
1512                 cause = new IOException("Unable to write data");
1513             }
1514             pw.future.setFailure(cause);
1515         }
1516 
1517         for (;;) {
1518             MessageEvent ev = pendingEncryptedWrites.poll();
1519             if (ev == null) {
1520                 break;
1521             }
1522             if (cause == null) {
1523                 cause = new IOException("Unable to write data");
1524             }
1525             ev.getFuture().setFailure(cause);
1526         }
1527 
1528         if (cause != null) {
1529             fireExceptionCaughtLater(ctx, cause);
1530         }
1531     }
1532 
1533     /**
1534      * Calls {@link #handshake()} once the {@link Channel} is connected
1535      */
1536     @Override
1537     public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception {
1538         if (issueHandshake) {
1539             // issue and handshake and add a listener to it which will fire an exception event if
1540             // an exception was thrown while doing the handshake
1541             handshake().addListener(new ChannelFutureListener() {
1542 
1543                 public void operationComplete(ChannelFuture future) throws Exception {
1544                     if (future.isSuccess()) {
1545                         // Send the event upstream after the handshake was completed without an error.
1546                         //
1547                         // See https://github.com/netty/netty/issues/358
1548                         ctx.sendUpstream(e);
1549                     }
1550                 }
1551             });
1552         } else {
1553             super.channelConnected(ctx, e);
1554         }
1555     }
1556 
1557     /**
1558      * Loop over all the pending writes and fail them.
1559      *
1560      * See <a href="https://github.com/netty/netty/issues/305">#305</a> for more details.
1561      */
1562     @Override
1563     public void channelClosed(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
1564         // Move the fail of the writes to the IO-Thread to prevent possible deadlock
1565         // See https://github.com/netty/netty/issues/989
1566         ctx.getPipeline().execute(new Runnable() {
1567             public void run() {
1568                 if (!pendingUnencryptedWritesLock.tryLock()) {
1569                     return;
1570                 }
1571 
1572                 Throwable cause = null;
1573                 try {
1574                     for (;;) {
1575                         PendingWrite pw = pendingUnencryptedWrites.poll();
1576                         if (pw == null) {
1577                             break;
1578                         }
1579                         if (cause == null) {
1580                             cause = new ClosedChannelException();
1581                         }
1582                         pw.future.setFailure(cause);
1583                     }
1584 
1585                     for (;;) {
1586                         MessageEvent ev = pendingEncryptedWrites.poll();
1587                         if (ev == null) {
1588                             break;
1589                         }
1590                         if (cause == null) {
1591                             cause = new ClosedChannelException();
1592                         }
1593                         ev.getFuture().setFailure(cause);
1594                     }
1595                 } finally {
1596                     pendingUnencryptedWritesLock.unlock();
1597                 }
1598 
1599                 if (cause != null) {
1600                     fireExceptionCaught(ctx, cause);
1601                 }
1602             }
1603         });
1604 
1605         super.channelClosed(ctx, e);
1606     }
1607 
1608     private final class SSLEngineInboundCloseFuture extends DefaultChannelFuture {
1609         public SSLEngineInboundCloseFuture() {
1610             super(null, true);
1611         }
1612 
1613         void setClosed() {
1614             super.setSuccess();
1615         }
1616 
1617         @Override
1618         public Channel getChannel() {
1619             if (ctx == null) {
1620                 // Maybe we should better throw an IllegalStateException() ?
1621                 return null;
1622             } else {
1623                 return ctx.getChannel();
1624             }
1625         }
1626 
1627         @Override
1628         public boolean setSuccess() {
1629             return false;
1630         }
1631 
1632         @Override
1633         public boolean setFailure(Throwable cause) {
1634             return false;
1635         }
1636     }
1637 }