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 io.netty.handler.ssl;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.buffer.ByteBufUtil;
21  import io.netty.buffer.CompositeByteBuf;
22  import io.netty.buffer.Unpooled;
23  import io.netty.channel.Channel;
24  import io.netty.channel.ChannelException;
25  import io.netty.channel.ChannelFuture;
26  import io.netty.channel.ChannelFutureListener;
27  import io.netty.channel.ChannelHandler;
28  import io.netty.channel.ChannelHandlerContext;
29  import io.netty.channel.ChannelPipeline;
30  import io.netty.channel.ChannelPromise;
31  import io.netty.channel.PendingWriteQueue;
32  import io.netty.handler.codec.ByteToMessageDecoder;
33  import io.netty.util.concurrent.DefaultPromise;
34  import io.netty.util.concurrent.EventExecutor;
35  import io.netty.util.concurrent.Future;
36  import io.netty.util.concurrent.FutureListener;
37  import io.netty.util.concurrent.Promise;
38  import io.netty.util.internal.EmptyArrays;
39  import io.netty.util.internal.OneTimeTask;
40  import io.netty.util.internal.PlatformDependent;
41  import io.netty.util.internal.logging.InternalLogger;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  
44  import javax.net.ssl.SSLEngine;
45  import javax.net.ssl.SSLEngineResult;
46  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
47  import javax.net.ssl.SSLEngineResult.Status;
48  import javax.net.ssl.SSLException;
49  import java.io.IOException;
50  import java.nio.ByteBuffer;
51  import java.nio.channels.ClosedChannelException;
52  import java.nio.channels.DatagramChannel;
53  import java.nio.channels.SocketChannel;
54  import java.util.List;
55  import java.util.concurrent.ScheduledFuture;
56  import java.util.concurrent.TimeUnit;
57  import java.util.regex.Pattern;
58  
59  
60  /**
61   * Adds <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">SSL
62   * &middot; TLS</a> and StartTLS support to a {@link Channel}.  Please refer
63   * to the <strong>"SecureChat"</strong> example in the distribution or the web
64   * site for the detailed usage.
65   *
66   * <h3>Beginning the handshake</h3>
67   * <p>
68   * You must make sure not to write a message while the handshake is in progress unless you are
69   * renegotiating.  You will be notified by the {@link Future} which is
70   * returned by the {@link #handshakeFuture()} method when the handshake
71   * process succeeds or fails.
72   * <p>
73   * Beside using the handshake {@link ChannelFuture} to get notified about the completation of the handshake it's
74   * also possible to detect it by implement the
75   * {@link ChannelHandler#userEventTriggered(ChannelHandlerContext, Object)}
76   * method and check for a {@link SslHandshakeCompletionEvent}.
77   *
78   * <h3>Handshake</h3>
79   * <p>
80   * The handshake will be automaticly issued for you once the {@link Channel} is active and
81   * {@link SSLEngine#getUseClientMode()} returns {@code true}.
82   * So no need to bother with it by your self.
83   *
84   * <h3>Closing the session</h3>
85   * <p>
86   * To close the SSL session, the {@link #close()} method should be
87   * called to send the {@code close_notify} message to the remote peer.  One
88   * exception is when you close the {@link Channel} - {@link SslHandler}
89   * intercepts the close request and send the {@code close_notify} message
90   * before the channel closure automatically.  Once the SSL session is closed,
91   * it is not reusable, and consequently you should create a new
92   * {@link SslHandler} with a new {@link SSLEngine} as explained in the
93   * following section.
94   *
95   * <h3>Restarting the session</h3>
96   * <p>
97   * To restart the SSL session, you must remove the existing closed
98   * {@link SslHandler} from the {@link ChannelPipeline}, insert a new
99   * {@link SslHandler} with a new {@link SSLEngine} into the pipeline,
100  * and start the handshake process as described in the first section.
101  *
102  * <h3>Implementing StartTLS</h3>
103  * <p>
104  * <a href="http://en.wikipedia.org/wiki/STARTTLS">StartTLS</a> is the
105  * communication pattern that secures the wire in the middle of the plaintext
106  * connection.  Please note that it is different from SSL &middot; TLS, that
107  * secures the wire from the beginning of the connection.  Typically, StartTLS
108  * is composed of three steps:
109  * <ol>
110  * <li>Client sends a StartTLS request to server.</li>
111  * <li>Server sends a StartTLS response to client.</li>
112  * <li>Client begins SSL handshake.</li>
113  * </ol>
114  * If you implement a server, you need to:
115  * <ol>
116  * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
117  *     to {@code true},</li>
118  * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
119  * <li>write a StartTLS response.</li>
120  * </ol>
121  * Please note that you must insert {@link SslHandler} <em>before</em> sending
122  * the StartTLS response.  Otherwise the client can send begin SSL handshake
123  * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing
124  * data corruption.
125  * <p>
126  * The client-side implementation is much simpler.
127  * <ol>
128  * <li>Write a StartTLS request,</li>
129  * <li>wait for the StartTLS response,</li>
130  * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
131  *     to {@code false},</li>
132  * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
133  * <li>Initiate SSL handshake.</li>
134  * </ol>
135  *
136  * <h3>Known issues</h3>
137  * <p>
138  * Because of a known issue with the current implementation of the SslEngine that comes
139  * with Java it may be possible that you see blocked IO-Threads while a full GC is done.
140  * <p>
141  * So if you are affected you can workaround this problem by adjust the cache settings
142  * like shown below:
143  *
144  * <pre>
145  *     SslContext context = ...;
146  *     context.getServerSessionContext().setSessionCacheSize(someSaneSize);
147  *     context.getServerSessionContext().setSessionTime(someSameTimeout);
148  * </pre>
149  * <p>
150  * What values to use here depends on the nature of your application and should be set
151  * based on monitoring and debugging of it.
152  * For more details see
153  * <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
154  */
155 public class SslHandler extends ByteToMessageDecoder {
156 
157     private static final InternalLogger logger =
158             InternalLoggerFactory.getInstance(SslHandler.class);
159 
160     private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
161             "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
162     private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
163             "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
164 
165     /**
166      * Used in {@link #unwrapNonAppData(ChannelHandlerContext)} as input for
167      * {@link #unwrap(ChannelHandlerContext, ByteBuf, int,  int)}.  Using this static instance reduce object
168      * creation as {@link Unpooled#EMPTY_BUFFER#nioBuffer()} creates a new {@link ByteBuffer} everytime.
169      */
170     private static final SSLException SSLENGINE_CLOSED = new SSLException("SSLEngine closed already");
171     private static final SSLException HANDSHAKE_TIMED_OUT = new SSLException("handshake timed out");
172     private static final ClosedChannelException CHANNEL_CLOSED = new ClosedChannelException();
173 
174     static {
175         SSLENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
176         HANDSHAKE_TIMED_OUT.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
177         CHANNEL_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
178     }
179 
180     private volatile ChannelHandlerContext ctx;
181     private final SSLEngine engine;
182     private final int maxPacketBufferSize;
183 
184     /**
185      * Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])}
186      * should be called with a {@link ByteBuf} that is only backed by one {@link ByteBuffer} to reduce the object
187      * creation.
188      */
189     private final ByteBuffer[] singleBuffer = new ByteBuffer[1];
190 
191     // BEGIN Platform-dependent flags
192 
193     /**
194      * {@code true} if and only if {@link SSLEngine} expects a direct buffer.
195      */
196     private final boolean wantsDirectBuffer;
197     /**
198      * {@code true} if and only if {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} requires the output buffer
199      * to be always as large as {@link #maxPacketBufferSize} even if the input buffer contains small amount of data.
200      * <p>
201      * If this flag is {@code false}, we allocate a smaller output buffer.
202      * </p>
203      */
204     private final boolean wantsLargeOutboundNetworkBuffer;
205     /**
206      * {@code true} if and only if {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} expects a heap buffer rather than
207      * a direct buffer.  For an unknown reason, JDK8 SSLEngine causes JVM to crash when its cipher suite uses Galois
208      * Counter Mode (GCM).
209      */
210     private boolean wantsInboundHeapBuffer;
211 
212     // END Platform-dependent flags
213 
214     private final boolean startTls;
215     private boolean sentFirstMessage;
216     private boolean flushedBeforeHandshake;
217     private boolean readDuringHandshake;
218     private PendingWriteQueue pendingUnencryptedWrites;
219 
220     private Promise<Channel> handshakePromise = new LazyChannelPromise();
221     private final LazyChannelPromise sslCloseFuture = new LazyChannelPromise();
222 
223     /**
224      * Set by wrap*() methods when something is produced.
225      * {@link #channelReadComplete(ChannelHandlerContext)} will check this flag, clear it, and call ctx.flush().
226      */
227     private boolean needsFlush;
228 
229     private int packetLength;
230 
231     private volatile long handshakeTimeoutMillis = 10000;
232     private volatile long closeNotifyTimeoutMillis = 3000;
233 
234     /**
235      * Creates a new instance.
236      *
237      * @param engine  the {@link SSLEngine} this handler will use
238      */
239     public SslHandler(SSLEngine engine) {
240         this(engine, false);
241     }
242 
243     /**
244      * Creates a new instance.
245      *
246      * @param engine    the {@link SSLEngine} this handler will use
247      * @param startTls  {@code true} if the first write request shouldn't be
248      *                  encrypted by the {@link SSLEngine}
249      */
250     public SslHandler(SSLEngine engine, boolean startTls) {
251         if (engine == null) {
252             throw new NullPointerException("engine");
253         }
254         this.engine = engine;
255         this.startTls = startTls;
256         maxPacketBufferSize = engine.getSession().getPacketBufferSize();
257 
258         boolean opensslEngine = engine instanceof OpenSslEngine;
259         wantsDirectBuffer = opensslEngine;
260         wantsLargeOutboundNetworkBuffer = !opensslEngine;
261 
262         /**
263          * When using JDK {@link SSLEngine}, we use {@link #MERGE_CUMULATOR} because it works only with
264          * one {@link ByteBuffer}.
265          *
266          * When using {@link OpenSslEngine}, we can use {@link #COMPOSITE_CUMULATOR} because it has
267          * {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} which works with multiple {@link ByteBuffer}s
268          * and which does not need to do extra memory copies.
269          */
270         setCumulator(opensslEngine ? COMPOSITE_CUMULATOR : MERGE_CUMULATOR);
271     }
272 
273     public long getHandshakeTimeoutMillis() {
274         return handshakeTimeoutMillis;
275     }
276 
277     public void setHandshakeTimeout(long handshakeTimeout, TimeUnit unit) {
278         if (unit == null) {
279             throw new NullPointerException("unit");
280         }
281 
282         setHandshakeTimeoutMillis(unit.toMillis(handshakeTimeout));
283     }
284 
285     public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) {
286         if (handshakeTimeoutMillis < 0) {
287             throw new IllegalArgumentException(
288                     "handshakeTimeoutMillis: " + handshakeTimeoutMillis + " (expected: >= 0)");
289         }
290         this.handshakeTimeoutMillis = handshakeTimeoutMillis;
291     }
292 
293     public long getCloseNotifyTimeoutMillis() {
294         return closeNotifyTimeoutMillis;
295     }
296 
297     public void setCloseNotifyTimeout(long closeNotifyTimeout, TimeUnit unit) {
298         if (unit == null) {
299             throw new NullPointerException("unit");
300         }
301 
302         setCloseNotifyTimeoutMillis(unit.toMillis(closeNotifyTimeout));
303     }
304 
305     public void setCloseNotifyTimeoutMillis(long closeNotifyTimeoutMillis) {
306         if (closeNotifyTimeoutMillis < 0) {
307             throw new IllegalArgumentException(
308                     "closeNotifyTimeoutMillis: " + closeNotifyTimeoutMillis + " (expected: >= 0)");
309         }
310         this.closeNotifyTimeoutMillis = closeNotifyTimeoutMillis;
311     }
312 
313     /**
314      * Returns the {@link SSLEngine} which is used by this handler.
315      */
316     public SSLEngine engine() {
317         return engine;
318     }
319 
320     /**
321      * Returns a {@link Future} that will get notified once the current TLS handshake completes.
322      *
323      * @return the {@link Future} for the iniital TLS handshake if {@link #renegotiate()} was not invoked.
324      *         The {@link Future} for the most recent {@linkplain #renegotiate() TLS renegotiation} otherwise.
325      */
326     public Future<Channel> handshakeFuture() {
327         return handshakePromise;
328     }
329 
330     /**
331      * Sends an SSL {@code close_notify} message to the specified channel and
332      * destroys the underlying {@link SSLEngine}.
333      */
334     public ChannelFuture close() {
335         return close(ctx.newPromise());
336     }
337 
338     /**
339      * See {@link #close()}
340      */
341     public ChannelFuture close(final ChannelPromise future) {
342         final ChannelHandlerContext ctx = this.ctx;
343         ctx.executor().execute(new Runnable() {
344             @Override
345             public void run() {
346                 engine.closeOutbound();
347                 try {
348                     write(ctx, Unpooled.EMPTY_BUFFER, future);
349                     flush(ctx);
350                 } catch (Exception e) {
351                     if (!future.tryFailure(e)) {
352                         logger.warn("{} flush() raised a masked exception.", ctx.channel(), e);
353                     }
354                 }
355             }
356         });
357 
358         return future;
359     }
360 
361     /**
362      * Return the {@link Future} that will get notified if the inbound of the {@link SSLEngine} is closed.
363      *
364      * This method will return the same {@link Future} all the time.
365      *
366      * @see SSLEngine
367      */
368     public Future<Channel> sslCloseFuture() {
369         return sslCloseFuture;
370     }
371 
372     @Override
373     public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
374         if (!pendingUnencryptedWrites.isEmpty()) {
375             // Check if queue is not empty first because create a new ChannelException is expensive
376             pendingUnencryptedWrites.removeAndFailAll(new ChannelException("Pending write on removal of SslHandler"));
377         }
378     }
379 
380     @Override
381     public void disconnect(final ChannelHandlerContext ctx,
382                            final ChannelPromise promise) throws Exception {
383         closeOutboundAndChannel(ctx, promise, true);
384     }
385 
386     @Override
387     public void close(final ChannelHandlerContext ctx,
388                       final ChannelPromise promise) throws Exception {
389         closeOutboundAndChannel(ctx, promise, false);
390     }
391 
392     @Override
393     public void read(ChannelHandlerContext ctx) throws Exception {
394         if (!handshakePromise.isDone()) {
395             readDuringHandshake = true;
396         }
397 
398         ctx.read();
399     }
400 
401     @Override
402     public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
403         pendingUnencryptedWrites.add(msg, promise);
404     }
405 
406     @Override
407     public void flush(ChannelHandlerContext ctx) throws Exception {
408         // Do not encrypt the first write request if this handler is
409         // created with startTLS flag turned on.
410         if (startTls && !sentFirstMessage) {
411             sentFirstMessage = true;
412             pendingUnencryptedWrites.removeAndWriteAll();
413             ctx.flush();
414             return;
415         }
416         if (pendingUnencryptedWrites.isEmpty()) {
417             // It's important to NOT use a voidPromise here as the user
418             // may want to add a ChannelFutureListener to the ChannelPromise later.
419             //
420             // See https://github.com/netty/netty/issues/3364
421             pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise());
422         }
423         if (!handshakePromise.isDone()) {
424             flushedBeforeHandshake = true;
425         }
426         wrap(ctx, false);
427         ctx.flush();
428     }
429 
430     private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
431         ByteBuf out = null;
432         ChannelPromise promise = null;
433         ByteBufAllocator alloc = ctx.alloc();
434         try {
435             for (;;) {
436                 Object msg = pendingUnencryptedWrites.current();
437                 if (msg == null) {
438                     break;
439                 }
440 
441                 if (!(msg instanceof ByteBuf)) {
442                     pendingUnencryptedWrites.removeAndWrite();
443                     continue;
444                 }
445 
446                 ByteBuf buf = (ByteBuf) msg;
447                 if (out == null) {
448                     out = allocateOutNetBuf(ctx, buf.readableBytes());
449                 }
450 
451                 SSLEngineResult result = wrap(alloc, engine, buf, out);
452                 if (!buf.isReadable()) {
453                     promise = pendingUnencryptedWrites.remove();
454                 } else {
455                     promise = null;
456                 }
457 
458                 if (result.getStatus() == Status.CLOSED) {
459                     // SSLEngine has been closed already.
460                     // Any further write attempts should be denied.
461                     pendingUnencryptedWrites.removeAndFailAll(SSLENGINE_CLOSED);
462                     return;
463                 } else {
464                     switch (result.getHandshakeStatus()) {
465                         case NEED_TASK:
466                             runDelegatedTasks();
467                             break;
468                         case FINISHED:
469                             setHandshakeSuccess();
470                             // deliberate fall-through
471                         case NOT_HANDSHAKING:
472                             setHandshakeSuccessIfStillHandshaking();
473                             // deliberate fall-through
474                         case NEED_WRAP:
475                             finishWrap(ctx, out, promise, inUnwrap);
476                             promise = null;
477                             out = null;
478                             break;
479                         case NEED_UNWRAP:
480                             return;
481                         default:
482                             throw new IllegalStateException(
483                                     "Unknown handshake status: " + result.getHandshakeStatus());
484                     }
485                 }
486             }
487         } catch (SSLException e) {
488             setHandshakeFailure(ctx, e);
489             throw e;
490         } finally {
491             finishWrap(ctx, out, promise, inUnwrap);
492         }
493     }
494 
495     private void finishWrap(ChannelHandlerContext ctx, ByteBuf out, ChannelPromise promise, boolean inUnwrap) {
496         if (out == null) {
497             out = Unpooled.EMPTY_BUFFER;
498         } else if (!out.isReadable()) {
499             out.release();
500             out = Unpooled.EMPTY_BUFFER;
501         }
502 
503         if (promise != null) {
504             ctx.write(out, promise);
505         } else {
506             ctx.write(out);
507         }
508 
509         if (inUnwrap) {
510             needsFlush = true;
511         }
512     }
513 
514     private void wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
515         ByteBuf out = null;
516         ByteBufAllocator alloc = ctx.alloc();
517         try {
518             for (;;) {
519                 if (out == null) {
520                     out = allocateOutNetBuf(ctx, 0);
521                 }
522                 SSLEngineResult result = wrap(alloc, engine, Unpooled.EMPTY_BUFFER, out);
523 
524                 if (result.bytesProduced() > 0) {
525                     ctx.write(out);
526                     if (inUnwrap) {
527                         needsFlush = true;
528                     }
529                     out = null;
530                 }
531 
532                 switch (result.getHandshakeStatus()) {
533                     case FINISHED:
534                         setHandshakeSuccess();
535                         break;
536                     case NEED_TASK:
537                         runDelegatedTasks();
538                         break;
539                     case NEED_UNWRAP:
540                         if (!inUnwrap) {
541                             unwrapNonAppData(ctx);
542                         }
543                         break;
544                     case NEED_WRAP:
545                         break;
546                     case NOT_HANDSHAKING:
547                         setHandshakeSuccessIfStillHandshaking();
548                         // Workaround for TLS False Start problem reported at:
549                         // https://github.com/netty/netty/issues/1108#issuecomment-14266970
550                         if (!inUnwrap) {
551                             unwrapNonAppData(ctx);
552                         }
553                         break;
554                     default:
555                         throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus());
556                 }
557 
558                 if (result.bytesProduced() == 0) {
559                     break;
560                 }
561             }
562         } catch (SSLException e) {
563             setHandshakeFailure(ctx, e);
564             throw e;
565         }  finally {
566             if (out != null) {
567                 out.release();
568             }
569         }
570     }
571 
572     private SSLEngineResult wrap(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out)
573             throws SSLException {
574         ByteBuf newDirectIn = null;
575         try {
576             int readerIndex = in.readerIndex();
577             int readableBytes = in.readableBytes();
578 
579             // We will call SslEngine.wrap(ByteBuffer[], ByteBuffer) to allow efficient handling of
580             // CompositeByteBuf without force an extra memory copy when CompositeByteBuffer.nioBuffer() is called.
581             final ByteBuffer[] in0;
582             if (in.isDirect() || !wantsDirectBuffer) {
583                 // As CompositeByteBuf.nioBufferCount() can be expensive (as it needs to check all composed ByteBuf
584                 // to calculate the count) we will just assume a CompositeByteBuf contains more then 1 ByteBuf.
585                 // The worst that can happen is that we allocate an extra ByteBuffer[] in CompositeByteBuf.nioBuffers()
586                 // which is better then walking the composed ByteBuf in most cases.
587                 if (!(in instanceof CompositeByteBuf) && in.nioBufferCount() == 1) {
588                     in0 = singleBuffer;
589                     // We know its only backed by 1 ByteBuffer so use internalNioBuffer to keep object allocation
590                     // to a minimum.
591                     in0[0] = in.internalNioBuffer(readerIndex, readableBytes);
592                 } else {
593                     in0 = in.nioBuffers();
594                 }
595             } else {
596                 // We could even go further here and check if its a CompositeByteBuf and if so try to decompose it and
597                 // only replace the ByteBuffer that are not direct. At the moment we just will replace the whole
598                 // CompositeByteBuf to keep the complexity to a minimum
599                 newDirectIn = alloc.directBuffer(readableBytes);
600                 newDirectIn.writeBytes(in, readerIndex, readableBytes);
601                 in0 = singleBuffer;
602                 in0[0] = newDirectIn.internalNioBuffer(0, readableBytes);
603             }
604 
605             for (;;) {
606                 ByteBuffer out0 = out.nioBuffer(out.writerIndex(), out.writableBytes());
607                 SSLEngineResult result = engine.wrap(in0, out0);
608                 in.skipBytes(result.bytesConsumed());
609                 out.writerIndex(out.writerIndex() + result.bytesProduced());
610 
611                 switch (result.getStatus()) {
612                 case BUFFER_OVERFLOW:
613                     out.ensureWritable(maxPacketBufferSize);
614                     break;
615                 default:
616                     return result;
617                 }
618             }
619         } finally {
620             // Null out to allow GC of ByteBuffer
621             singleBuffer[0] = null;
622 
623             if (newDirectIn != null) {
624                 newDirectIn.release();
625             }
626         }
627     }
628 
629     @Override
630     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
631         // Make sure to release SSLEngine,
632         // and notify the handshake future if the connection has been closed during handshake.
633         setHandshakeFailure(ctx, CHANNEL_CLOSED);
634         super.channelInactive(ctx);
635     }
636 
637     @Override
638     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
639         if (ignoreException(cause)) {
640             // It is safe to ignore the 'connection reset by peer' or
641             // 'broken pipe' error after sending close_notify.
642             if (logger.isDebugEnabled()) {
643                 logger.debug(
644                         "{} Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred " +
645                         "while writing close_notify in response to the peer's close_notify", ctx.channel(), cause);
646             }
647 
648             // Close the connection explicitly just in case the transport
649             // did not close the connection automatically.
650             if (ctx.channel().isActive()) {
651                 ctx.close();
652             }
653         } else {
654             ctx.fireExceptionCaught(cause);
655         }
656     }
657 
658     /**
659      * Checks if the given {@link Throwable} can be ignore and just "swallowed"
660      *
661      * When an ssl connection is closed a close_notify message is sent.
662      * After that the peer also sends close_notify however, it's not mandatory to receive
663      * the close_notify. The party who sent the initial close_notify can close the connection immediately
664      * then the peer will get connection reset error.
665      *
666      */
667     private boolean ignoreException(Throwable t) {
668         if (!(t instanceof SSLException) && t instanceof IOException && sslCloseFuture.isDone()) {
669             String message = String.valueOf(t.getMessage()).toLowerCase();
670 
671             // first try to match connection reset / broke peer based on the regex. This is the fastest way
672             // but may fail on different jdk impls or OS's
673             if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
674                 return true;
675             }
676 
677             // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
678             StackTraceElement[] elements = t.getStackTrace();
679             for (StackTraceElement element: elements) {
680                 String classname = element.getClassName();
681                 String methodname = element.getMethodName();
682 
683                 // skip all classes that belong to the io.netty package
684                 if (classname.startsWith("io.netty.")) {
685                     continue;
686                 }
687 
688                 // check if the method name is read if not skip it
689                 if (!"read".equals(methodname)) {
690                     continue;
691                 }
692 
693                 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
694                 // also others
695                 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
696                     return true;
697                 }
698 
699                 try {
700                     // No match by now.. Try to load the class via classloader and inspect it.
701                     // This is mainly done as other JDK implementations may differ in name of
702                     // the impl.
703                     Class<?> clazz = PlatformDependent.getClassLoader(getClass()).loadClass(classname);
704 
705                     if (SocketChannel.class.isAssignableFrom(clazz)
706                             || DatagramChannel.class.isAssignableFrom(clazz)) {
707                         return true;
708                     }
709 
710                     // also match against SctpChannel via String matching as it may not present.
711                     if (PlatformDependent.javaVersion() >= 7
712                             && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
713                         return true;
714                     }
715                 } catch (ClassNotFoundException e) {
716                     // This should not happen just ignore
717                 }
718             }
719         }
720 
721         return false;
722     }
723 
724     /**
725      * Returns {@code true} if the given {@link ByteBuf} is encrypted. Be aware that this method
726      * will not increase the readerIndex of the given {@link ByteBuf}.
727      *
728      * @param   buffer
729      *                  The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read,
730      *                  otherwise it will throw an {@link IllegalArgumentException}.
731      * @return encrypted
732      *                  {@code true} if the {@link ByteBuf} is encrypted, {@code false} otherwise.
733      * @throws IllegalArgumentException
734      *                  Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read.
735      */
736     public static boolean isEncrypted(ByteBuf buffer) {
737         if (buffer.readableBytes() < 5) {
738             throw new IllegalArgumentException("buffer must have at least 5 readable bytes");
739         }
740         return getEncryptedPacketLength(buffer, buffer.readerIndex()) != -1;
741     }
742 
743     /**
744      * Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
745      * the readerIndex of the given {@link ByteBuf}.
746      *
747      * @param   buffer
748      *                  The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read,
749      *                  otherwise it will throw an {@link IllegalArgumentException}.
750      * @return length
751      *                  The length of the encrypted packet that is included in the buffer. This will
752      *                  return {@code -1} if the given {@link ByteBuf} is not encrypted at all.
753      * @throws IllegalArgumentException
754      *                  Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read.
755      */
756     private static int getEncryptedPacketLength(ByteBuf buffer, int offset) {
757         int packetLength = 0;
758 
759         // SSLv3 or TLS - Check ContentType
760         boolean tls;
761         switch (buffer.getUnsignedByte(offset)) {
762             case 20:  // change_cipher_spec
763             case 21:  // alert
764             case 22:  // handshake
765             case 23:  // application_data
766                 tls = true;
767                 break;
768             default:
769                 // SSLv2 or bad data
770                 tls = false;
771         }
772 
773         if (tls) {
774             // SSLv3 or TLS - Check ProtocolVersion
775             int majorVersion = buffer.getUnsignedByte(offset + 1);
776             if (majorVersion == 3) {
777                 // SSLv3 or TLS
778                 packetLength = buffer.getUnsignedShort(offset + 3) + 5;
779                 if (packetLength <= 5) {
780                     // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
781                     tls = false;
782                 }
783             } else {
784                 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
785                 tls = false;
786             }
787         }
788 
789         if (!tls) {
790             // SSLv2 or bad data - Check the version
791             boolean sslv2 = true;
792             int headerLength = (buffer.getUnsignedByte(offset) & 0x80) != 0 ? 2 : 3;
793             int majorVersion = buffer.getUnsignedByte(offset + headerLength + 1);
794             if (majorVersion == 2 || majorVersion == 3) {
795                 // SSLv2
796                 if (headerLength == 2) {
797                     packetLength = (buffer.getShort(offset) & 0x7FFF) + 2;
798                 } else {
799                     packetLength = (buffer.getShort(offset) & 0x3FFF) + 3;
800                 }
801                 if (packetLength <= headerLength) {
802                     sslv2 = false;
803                 }
804             } else {
805                 sslv2 = false;
806             }
807 
808             if (!sslv2) {
809                 return -1;
810             }
811         }
812         return packetLength;
813     }
814 
815     @Override
816     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException {
817         final int startOffset = in.readerIndex();
818         final int endOffset = in.writerIndex();
819         int offset = startOffset;
820         int totalLength = 0;
821 
822         // If we calculated the length of the current SSL record before, use that information.
823         if (packetLength > 0) {
824             if (endOffset - startOffset < packetLength) {
825                 return;
826             } else {
827                 offset += packetLength;
828                 totalLength = packetLength;
829                 packetLength = 0;
830             }
831         }
832 
833         boolean nonSslRecord = false;
834 
835         while (totalLength < OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
836             final int readableBytes = endOffset - offset;
837             if (readableBytes < 5) {
838                 break;
839             }
840 
841             final int packetLength = getEncryptedPacketLength(in, offset);
842             if (packetLength == -1) {
843                 nonSslRecord = true;
844                 break;
845             }
846 
847             assert packetLength > 0;
848 
849             if (packetLength > readableBytes) {
850                 // wait until the whole packet can be read
851                 this.packetLength = packetLength;
852                 break;
853             }
854 
855             int newTotalLength = totalLength + packetLength;
856             if (newTotalLength > OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
857                 // Don't read too much.
858                 break;
859             }
860 
861             // We have a whole packet.
862             // Increment the offset to handle the next packet.
863             offset += packetLength;
864             totalLength = newTotalLength;
865         }
866 
867         if (totalLength > 0) {
868             // The buffer contains one or more full SSL records.
869             // Slice out the whole packet so unwrap will only be called with complete packets.
870             // Also directly reset the packetLength. This is needed as unwrap(..) may trigger
871             // decode(...) again via:
872             // 1) unwrap(..) is called
873             // 2) wrap(...) is called from within unwrap(...)
874             // 3) wrap(...) calls unwrapLater(...)
875             // 4) unwrapLater(...) calls decode(...)
876             //
877             // See https://github.com/netty/netty/issues/1534
878 
879             in.skipBytes(totalLength);
880 
881             // If SSLEngine expects a heap buffer for unwrapping, do the conversion.
882             if (in.isDirect() && wantsInboundHeapBuffer) {
883                 ByteBuf copy = ctx.alloc().heapBuffer(totalLength);
884                 try {
885                     copy.writeBytes(in, startOffset, totalLength);
886                     unwrap(ctx, copy, 0, totalLength);
887                 } finally {
888                     copy.release();
889                 }
890             } else {
891                 unwrap(ctx, in, startOffset, totalLength);
892             }
893         }
894 
895         if (nonSslRecord) {
896             // Not an SSL/TLS packet
897             NotSslRecordException e = new NotSslRecordException(
898                     "not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
899             in.skipBytes(in.readableBytes());
900             ctx.fireExceptionCaught(e);
901             setHandshakeFailure(ctx, e);
902         }
903     }
904 
905     @Override
906     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
907         if (needsFlush) {
908             needsFlush = false;
909             ctx.flush();
910         }
911 
912         // If handshake is not finished yet, we need more data.
913         if (!handshakePromise.isDone() && !ctx.channel().config().isAutoRead()) {
914             ctx.read();
915         }
916 
917         ctx.fireChannelReadComplete();
918     }
919 
920     /**
921      * Calls {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} with an empty buffer to handle handshakes, etc.
922      */
923     private void unwrapNonAppData(ChannelHandlerContext ctx) throws SSLException {
924         unwrap(ctx, Unpooled.EMPTY_BUFFER, 0, 0);
925     }
926 
927     /**
928      * Unwraps inbound SSL records.
929      */
930     private void unwrap(
931             ChannelHandlerContext ctx, ByteBuf packet, int offset, int length) throws SSLException {
932 
933         boolean wrapLater = false;
934         boolean notifyClosure = false;
935         ByteBuf decodeOut = allocate(ctx, length);
936         try {
937             for (;;) {
938                 final SSLEngineResult result = unwrap(engine, packet, offset, length, decodeOut);
939                 final Status status = result.getStatus();
940                 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
941                 final int produced = result.bytesProduced();
942                 final int consumed = result.bytesConsumed();
943 
944                 // Update indexes for the next iteration
945                 offset += consumed;
946                 length -= consumed;
947 
948                 if (status == Status.CLOSED) {
949                     // notify about the CLOSED state of the SSLEngine. See #137
950                     notifyClosure = true;
951                 }
952 
953                 switch (handshakeStatus) {
954                     case NEED_UNWRAP:
955                         break;
956                     case NEED_WRAP:
957                         wrapNonAppData(ctx, true);
958                         break;
959                     case NEED_TASK:
960                         runDelegatedTasks();
961                         break;
962                     case FINISHED:
963                         setHandshakeSuccess();
964                         wrapLater = true;
965                         continue;
966                     case NOT_HANDSHAKING:
967                         if (setHandshakeSuccessIfStillHandshaking()) {
968                             wrapLater = true;
969                             continue;
970                         }
971                         if (flushedBeforeHandshake) {
972                             // We need to call wrap(...) in case there was a flush done before the handshake completed.
973                             //
974                             // See https://github.com/netty/netty/pull/2437
975                             flushedBeforeHandshake = false;
976                             wrapLater = true;
977                         }
978 
979                         break;
980                     default:
981                         throw new IllegalStateException("unknown handshake status: " + handshakeStatus);
982                 }
983 
984                 if (status == Status.BUFFER_UNDERFLOW || consumed == 0 && produced == 0) {
985                     break;
986                 }
987             }
988 
989             if (wrapLater) {
990                 wrap(ctx, true);
991             }
992 
993             if (notifyClosure) {
994                 sslCloseFuture.trySuccess(ctx.channel());
995             }
996         } catch (SSLException e) {
997             setHandshakeFailure(ctx, e);
998             throw e;
999         } finally {
1000             if (decodeOut.isReadable()) {
1001                 ctx.fireChannelRead(decodeOut);
1002             } else {
1003                 decodeOut.release();
1004             }
1005         }
1006     }
1007 
1008     private SSLEngineResult unwrap(
1009             SSLEngine engine, ByteBuf in, int readerIndex, int len, ByteBuf out) throws SSLException {
1010         int nioBufferCount = in.nioBufferCount();
1011         if (engine instanceof OpenSslEngine && nioBufferCount > 1) {
1012             /**
1013              * If {@link OpenSslEngine} is in use,
1014              * we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method
1015              * that accepts multiple {@link ByteBuffer}s without additional memory copies.
1016              */
1017             OpenSslEngine opensslEngine = (OpenSslEngine) engine;
1018             int overflows = 0;
1019             ByteBuffer[] in0 = in.nioBuffers(readerIndex, len);
1020             try {
1021                 for (;;) {
1022                     int writerIndex = out.writerIndex();
1023                     int writableBytes = out.writableBytes();
1024                     ByteBuffer out0;
1025                     if (out.nioBufferCount() == 1) {
1026                         out0 = out.internalNioBuffer(writerIndex, writableBytes);
1027                     } else {
1028                         out0 = out.nioBuffer(writerIndex, writableBytes);
1029                     }
1030                     singleBuffer[0] = out0;
1031                     SSLEngineResult result = opensslEngine.unwrap(in0, singleBuffer);
1032                     out.writerIndex(out.writerIndex() + result.bytesProduced());
1033                     switch (result.getStatus()) {
1034                         case BUFFER_OVERFLOW:
1035                             int max = engine.getSession().getApplicationBufferSize();
1036                             switch (overflows ++) {
1037                                 case 0:
1038                                     out.ensureWritable(Math.min(max, in.readableBytes()));
1039                                     break;
1040                                 default:
1041                                     out.ensureWritable(max);
1042                             }
1043                             break;
1044                         default:
1045                             return result;
1046                     }
1047                 }
1048             } finally {
1049                 singleBuffer[0] = null;
1050             }
1051         } else {
1052             int overflows = 0;
1053             ByteBuffer in0;
1054             if (nioBufferCount == 1) {
1055                 // Use internalNioBuffer to reduce object creation.
1056                 in0 = in.internalNioBuffer(readerIndex, len);
1057             } else {
1058                 // This should never be true as this is only the case when OpenSslEngine is used, anyway lets
1059                 // guard against it.
1060                 in0 = in.nioBuffer(readerIndex, len);
1061             }
1062             for (;;) {
1063                 int writerIndex = out.writerIndex();
1064                 int writableBytes = out.writableBytes();
1065                 ByteBuffer out0;
1066                 if (out.nioBufferCount() == 1) {
1067                     out0 = out.internalNioBuffer(writerIndex, writableBytes);
1068                 } else {
1069                     out0 = out.nioBuffer(writerIndex, writableBytes);
1070                 }
1071                 SSLEngineResult result = engine.unwrap(in0, out0);
1072                 out.writerIndex(out.writerIndex() + result.bytesProduced());
1073                 switch (result.getStatus()) {
1074                     case BUFFER_OVERFLOW:
1075                         int max = engine.getSession().getApplicationBufferSize();
1076                         switch (overflows ++) {
1077                             case 0:
1078                                 out.ensureWritable(Math.min(max, in.readableBytes()));
1079                                 break;
1080                             default:
1081                                 out.ensureWritable(max);
1082                         }
1083                         break;
1084                     default:
1085                         return result;
1086                 }
1087             }
1088         }
1089     }
1090 
1091     /**
1092      * Fetches all delegated tasks from the {@link SSLEngine} and runs them by invoking them directly.
1093      */
1094     private void runDelegatedTasks() {
1095         for (;;) {
1096             Runnable task = engine.getDelegatedTask();
1097             if (task == null) {
1098                 break;
1099             }
1100 
1101             task.run();
1102         }
1103     }
1104 
1105     /**
1106      * Works around some Android {@link SSLEngine} implementations that skip {@link HandshakeStatus#FINISHED} and
1107      * go straight into {@link HandshakeStatus#NOT_HANDSHAKING} when handshake is finished.
1108      *
1109      * @return {@code true} if and only if the workaround has been applied and thus {@link #handshakeFuture} has been
1110      *         marked as success by this method
1111      */
1112     private boolean setHandshakeSuccessIfStillHandshaking() {
1113         if (!handshakePromise.isDone()) {
1114             setHandshakeSuccess();
1115             return true;
1116         }
1117         return false;
1118     }
1119 
1120     /**
1121      * Notify all the handshake futures about the successfully handshake
1122      */
1123     private void setHandshakeSuccess() {
1124         // Work around the JVM crash which occurs when a cipher suite with GCM enabled.
1125         final String cipherSuite = String.valueOf(engine.getSession().getCipherSuite());
1126         if (!wantsDirectBuffer && (cipherSuite.contains("_GCM_") || cipherSuite.contains("-GCM-"))) {
1127             wantsInboundHeapBuffer = true;
1128         }
1129 
1130         handshakePromise.trySuccess(ctx.channel());
1131 
1132         if (logger.isDebugEnabled()) {
1133             logger.debug("{} HANDSHAKEN: {}", ctx.channel(), engine.getSession().getCipherSuite());
1134         }
1135         ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS);
1136 
1137         if (readDuringHandshake && !ctx.channel().config().isAutoRead()) {
1138             readDuringHandshake = false;
1139             ctx.read();
1140         }
1141     }
1142 
1143     /**
1144      * Notify all the handshake futures about the failure during the handshake.
1145      */
1146     private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) {
1147         // Release all resources such as internal buffers that SSLEngine
1148         // is managing.
1149         engine.closeOutbound();
1150 
1151         try {
1152             engine.closeInbound();
1153         } catch (SSLException e) {
1154             // only log in debug mode as it most likely harmless and latest chrome still trigger
1155             // this all the time.
1156             //
1157             // See https://github.com/netty/netty/issues/1340
1158             String msg = e.getMessage();
1159             if (msg == null || !msg.contains("possible truncation attack")) {
1160                 logger.debug("{} SSLEngine.closeInbound() raised an exception.", ctx.channel(), e);
1161             }
1162         }
1163         notifyHandshakeFailure(cause);
1164         pendingUnencryptedWrites.removeAndFailAll(cause);
1165     }
1166 
1167     private void notifyHandshakeFailure(Throwable cause) {
1168         if (handshakePromise.tryFailure(cause)) {
1169             ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause));
1170             ctx.close();
1171         }
1172     }
1173 
1174     private void closeOutboundAndChannel(
1175             final ChannelHandlerContext ctx, final ChannelPromise promise, boolean disconnect) throws Exception {
1176         if (!ctx.channel().isActive()) {
1177             if (disconnect) {
1178                 ctx.disconnect(promise);
1179             } else {
1180                 ctx.close(promise);
1181             }
1182             return;
1183         }
1184 
1185         engine.closeOutbound();
1186 
1187         ChannelPromise closeNotifyFuture = ctx.newPromise();
1188         write(ctx, Unpooled.EMPTY_BUFFER, closeNotifyFuture);
1189         flush(ctx);
1190         safeClose(ctx, closeNotifyFuture, promise);
1191     }
1192 
1193     @Override
1194     public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
1195         this.ctx = ctx;
1196         pendingUnencryptedWrites = new PendingWriteQueue(ctx);
1197 
1198         if (ctx.channel().isActive() && engine.getUseClientMode()) {
1199             // Begin the initial handshake.
1200             // channelActive() event has been fired already, which means this.channelActive() will
1201             // not be invoked. We have to initialize here instead.
1202             handshake(null);
1203         } else {
1204             // channelActive() event has not been fired yet.  this.channelOpen() will be invoked
1205             // and initialization will occur there.
1206         }
1207     }
1208 
1209     /**
1210      * Performs TLS renegotiation.
1211      */
1212     public Future<Channel> renegotiate() {
1213         ChannelHandlerContext ctx = this.ctx;
1214         if (ctx == null) {
1215             throw new IllegalStateException();
1216         }
1217 
1218         return renegotiate(ctx.executor().<Channel>newPromise());
1219     }
1220 
1221     /**
1222      * Performs TLS renegotiation.
1223      */
1224     public Future<Channel> renegotiate(final Promise<Channel> promise) {
1225         if (promise == null) {
1226             throw new NullPointerException("promise");
1227         }
1228 
1229         ChannelHandlerContext ctx = this.ctx;
1230         if (ctx == null) {
1231             throw new IllegalStateException();
1232         }
1233 
1234         EventExecutor executor = ctx.executor();
1235         if (!executor.inEventLoop()) {
1236             executor.execute(new OneTimeTask() {
1237                 @Override
1238                 public void run() {
1239                     handshake(promise);
1240                 }
1241             });
1242             return promise;
1243         }
1244 
1245         handshake(promise);
1246         return promise;
1247     }
1248 
1249     /**
1250      * Performs TLS (re)negotiation.
1251      *
1252      * @param newHandshakePromise if {@code null}, use the existing {@link #handshakePromise},
1253      *                            assuming that the current negotiation has not been finished.
1254      *                            Currently, {@code null} is expected only for the initial handshake.
1255      */
1256     private void handshake(final Promise<Channel> newHandshakePromise) {
1257         final Promise<Channel> p;
1258         if (newHandshakePromise != null) {
1259             final Promise<Channel> oldHandshakePromise = handshakePromise;
1260             if (!oldHandshakePromise.isDone()) {
1261                 // There's no need to handshake because handshake is in progress already.
1262                 // Merge the new promise into the old one.
1263                 oldHandshakePromise.addListener(new FutureListener<Channel>() {
1264                     @Override
1265                     public void operationComplete(Future<Channel> future) throws Exception {
1266                         if (future.isSuccess()) {
1267                             newHandshakePromise.setSuccess(future.getNow());
1268                         } else {
1269                             newHandshakePromise.setFailure(future.cause());
1270                         }
1271                     }
1272                 });
1273                 return;
1274             }
1275 
1276             handshakePromise = p = newHandshakePromise;
1277         } else {
1278             // Forced to reuse the old handshake.
1279             p = handshakePromise;
1280             assert !p.isDone();
1281         }
1282 
1283         // Begin handshake.
1284         final ChannelHandlerContext ctx = this.ctx;
1285         try {
1286             engine.beginHandshake();
1287             wrapNonAppData(ctx, false);
1288             ctx.flush();
1289         } catch (Exception e) {
1290             notifyHandshakeFailure(e);
1291         }
1292 
1293         // Set timeout if necessary.
1294         final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
1295         if (handshakeTimeoutMillis <= 0 || p.isDone()) {
1296             return;
1297         }
1298 
1299         final ScheduledFuture<?> timeoutFuture = ctx.executor().schedule(new Runnable() {
1300             @Override
1301             public void run() {
1302                 if (p.isDone()) {
1303                     return;
1304                 }
1305                 notifyHandshakeFailure(HANDSHAKE_TIMED_OUT);
1306             }
1307         }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
1308 
1309         // Cancel the handshake timeout when handshake is finished.
1310         p.addListener(new FutureListener<Channel>() {
1311             @Override
1312             public void operationComplete(Future<Channel> f) throws Exception {
1313                 timeoutFuture.cancel(false);
1314             }
1315         });
1316     }
1317 
1318     /**
1319      * Issues an initial TLS handshake once connected when used in client-mode
1320      */
1321     @Override
1322     public void channelActive(final ChannelHandlerContext ctx) throws Exception {
1323         if (!startTls && engine.getUseClientMode()) {
1324             // Begin the initial handshake
1325             handshake(null);
1326         }
1327         ctx.fireChannelActive();
1328     }
1329 
1330     private void safeClose(
1331             final ChannelHandlerContext ctx, ChannelFuture flushFuture,
1332             final ChannelPromise promise) {
1333         if (!ctx.channel().isActive()) {
1334             ctx.close(promise);
1335             return;
1336         }
1337 
1338         final ScheduledFuture<?> timeoutFuture;
1339         if (closeNotifyTimeoutMillis > 0) {
1340             // Force-close the connection if close_notify is not fully sent in time.
1341             timeoutFuture = ctx.executor().schedule(new Runnable() {
1342                 @Override
1343                 public void run() {
1344                     logger.warn("{} Last write attempt timed out; force-closing the connection.", ctx.channel());
1345                     ctx.close(promise);
1346                 }
1347             }, closeNotifyTimeoutMillis, TimeUnit.MILLISECONDS);
1348         } else {
1349             timeoutFuture = null;
1350         }
1351 
1352         // Close the connection if close_notify is sent in time.
1353         flushFuture.addListener(new ChannelFutureListener() {
1354             @Override
1355             public void operationComplete(ChannelFuture f)
1356                     throws Exception {
1357                 if (timeoutFuture != null) {
1358                     timeoutFuture.cancel(false);
1359                 }
1360                 // Trigger the close in all cases to make sure the promise is notified
1361                 // See https://github.com/netty/netty/issues/2358
1362                 ctx.close(promise);
1363             }
1364         });
1365     }
1366 
1367     /**
1368      * Always prefer a direct buffer when it's pooled, so that we reduce the number of memory copies
1369      * in {@link OpenSslEngine}.
1370      */
1371     private ByteBuf allocate(ChannelHandlerContext ctx, int capacity) {
1372         ByteBufAllocator alloc = ctx.alloc();
1373         if (wantsDirectBuffer) {
1374             return alloc.directBuffer(capacity);
1375         } else {
1376             return alloc.buffer(capacity);
1377         }
1378     }
1379 
1380     /**
1381      * Allocates an outbound network buffer for {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} which can encrypt
1382      * the specified amount of pending bytes.
1383      */
1384     private ByteBuf allocateOutNetBuf(ChannelHandlerContext ctx, int pendingBytes) {
1385         if (wantsLargeOutboundNetworkBuffer) {
1386             return allocate(ctx, maxPacketBufferSize);
1387         } else {
1388             return allocate(ctx, Math.min(
1389                     pendingBytes + OpenSslEngine.MAX_ENCRYPTION_OVERHEAD_LENGTH,
1390                     maxPacketBufferSize));
1391         }
1392     }
1393 
1394     private final class LazyChannelPromise extends DefaultPromise<Channel> {
1395 
1396         @Override
1397         protected EventExecutor executor() {
1398             if (ctx == null) {
1399                 throw new IllegalStateException();
1400             }
1401             return ctx.executor();
1402         }
1403     }
1404 }