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 * https://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.ChannelConfig;
25 import io.netty.channel.ChannelException;
26 import io.netty.channel.ChannelFuture;
27 import io.netty.channel.ChannelFutureListener;
28 import io.netty.channel.ChannelHandlerContext;
29 import io.netty.channel.ChannelInboundHandler;
30 import io.netty.channel.ChannelOption;
31 import io.netty.channel.ChannelOutboundBuffer;
32 import io.netty.channel.ChannelOutboundHandler;
33 import io.netty.channel.ChannelPipeline;
34 import io.netty.channel.ChannelPromise;
35 import io.netty.channel.unix.UnixChannel;
36 import io.netty.handler.codec.ByteToMessageDecoder;
37 import io.netty.handler.codec.DecoderException;
38 import io.netty.handler.codec.UnsupportedMessageTypeException;
39 import io.netty.util.ReferenceCountUtil;
40 import io.netty.util.concurrent.DefaultPromise;
41 import io.netty.util.concurrent.EventExecutor;
42 import io.netty.util.concurrent.Future;
43 import io.netty.util.concurrent.FutureListener;
44 import io.netty.util.concurrent.ImmediateExecutor;
45 import io.netty.util.concurrent.Promise;
46 import io.netty.util.concurrent.PromiseNotifier;
47 import io.netty.util.internal.ObjectUtil;
48 import io.netty.util.internal.PlatformDependent;
49 import io.netty.util.internal.ThrowableUtil;
50 import io.netty.util.internal.UnstableApi;
51 import io.netty.util.internal.logging.InternalLogger;
52 import io.netty.util.internal.logging.InternalLoggerFactory;
53
54 import javax.net.ssl.SSLEngine;
55 import javax.net.ssl.SSLEngineResult;
56 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
57 import javax.net.ssl.SSLEngineResult.Status;
58 import javax.net.ssl.SSLException;
59 import javax.net.ssl.SSLHandshakeException;
60 import javax.net.ssl.SSLSession;
61 import java.io.IOException;
62 import java.net.SocketAddress;
63 import java.nio.ByteBuffer;
64 import java.nio.channels.ClosedChannelException;
65 import java.nio.channels.DatagramChannel;
66 import java.nio.channels.SocketChannel;
67 import java.security.cert.CertificateException;
68 import java.util.List;
69 import java.util.concurrent.Executor;
70 import java.util.concurrent.RejectedExecutionException;
71 import java.util.concurrent.TimeUnit;
72 import java.util.regex.Pattern;
73
74 import static io.netty.handler.ssl.SslUtils.NOT_ENOUGH_DATA;
75 import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
76 import static io.netty.util.internal.ObjectUtil.checkNotNull;
77 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
78
79 /**
80 * Adds <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">SSL
81 * · TLS</a> and StartTLS support to a {@link Channel}. Please refer
82 * to the <strong>"SecureChat"</strong> example in the distribution or the web
83 * site for the detailed usage.
84 *
85 * <h3>Beginning the handshake</h3>
86 * <p>
87 * Beside using the handshake {@link ChannelFuture} to get notified about the completion of the handshake it's
88 * also possible to detect it by implement the
89 * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)}
90 * method and check for a {@link SslHandshakeCompletionEvent}.
91 *
92 * <h3>Handshake</h3>
93 * <p>
94 * The handshake will be automatically issued for you once the {@link Channel} is active and
95 * {@link SSLEngine#getUseClientMode()} returns {@code true}.
96 * So no need to bother with it by your self.
97 *
98 * <h3>Closing the session</h3>
99 * <p>
100 * To close the SSL session, the {@link #closeOutbound()} method should be
101 * called to send the {@code close_notify} message to the remote peer. One
102 * exception is when you close the {@link Channel} - {@link SslHandler}
103 * intercepts the close request and send the {@code close_notify} message
104 * before the channel closure automatically. Once the SSL session is closed,
105 * it is not reusable, and consequently you should create a new
106 * {@link SslHandler} with a new {@link SSLEngine} as explained in the
107 * following section.
108 *
109 * <h3>Restarting the session</h3>
110 * <p>
111 * To restart the SSL session, you must remove the existing closed
112 * {@link SslHandler} from the {@link ChannelPipeline}, insert a new
113 * {@link SslHandler} with a new {@link SSLEngine} into the pipeline,
114 * and start the handshake process as described in the first section.
115 *
116 * <h3>Implementing StartTLS</h3>
117 * <p>
118 * <a href="https://en.wikipedia.org/wiki/STARTTLS">StartTLS</a> is the
119 * communication pattern that secures the wire in the middle of the plaintext
120 * connection. Please note that it is different from SSL · TLS, that
121 * secures the wire from the beginning of the connection. Typically, StartTLS
122 * is composed of three steps:
123 * <ol>
124 * <li>Client sends a StartTLS request to server.</li>
125 * <li>Server sends a StartTLS response to client.</li>
126 * <li>Client begins SSL handshake.</li>
127 * </ol>
128 * If you implement a server, you need to:
129 * <ol>
130 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
131 * to {@code true},</li>
132 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
133 * <li>write a StartTLS response.</li>
134 * </ol>
135 * Please note that you must insert {@link SslHandler} <em>before</em> sending
136 * the StartTLS response. Otherwise the client can send begin SSL handshake
137 * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing
138 * data corruption.
139 * <p>
140 * The client-side implementation is much simpler.
141 * <ol>
142 * <li>Write a StartTLS request,</li>
143 * <li>wait for the StartTLS response,</li>
144 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
145 * to {@code false},</li>
146 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
147 * <li>Initiate SSL handshake.</li>
148 * </ol>
149 *
150 * <h3>Known issues</h3>
151 * <p>
152 * Because of a known issue with the current implementation of the SslEngine that comes
153 * with Java it may be possible that you see blocked IO-Threads while a full GC is done.
154 * <p>
155 * So if you are affected you can workaround this problem by adjust the cache settings
156 * like shown below:
157 *
158 * <pre>
159 * SslContext context = ...;
160 * context.getServerSessionContext().setSessionCacheSize(someSaneSize);
161 * context.getServerSessionContext().setSessionTime(someSameTimeout);
162 * </pre>
163 * <p>
164 * What values to use here depends on the nature of your application and should be set
165 * based on monitoring and debugging of it.
166 * For more details see
167 * <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
168 */
169 public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundHandler {
170 private static final InternalLogger logger =
171 InternalLoggerFactory.getInstance(SslHandler.class);
172 private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
173 "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
174 private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
175 "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
176 private static final int STATE_SENT_FIRST_MESSAGE = 1;
177 private static final int STATE_FLUSHED_BEFORE_HANDSHAKE = 1 << 1;
178 private static final int STATE_READ_DURING_HANDSHAKE = 1 << 2;
179 private static final int STATE_HANDSHAKE_STARTED = 1 << 3;
180 /**
181 * Set by wrap*() methods when something is produced.
182 * {@link #channelReadComplete(ChannelHandlerContext)} will check this flag, clear it, and call ctx.flush().
183 */
184 private static final int STATE_NEEDS_FLUSH = 1 << 4;
185 private static final int STATE_OUTBOUND_CLOSED = 1 << 5;
186 private static final int STATE_CLOSE_NOTIFY = 1 << 6;
187 private static final int STATE_PROCESS_TASK = 1 << 7;
188 /**
189 * This flag is used to determine if we need to call {@link ChannelHandlerContext#read()} to consume more data
190 * when {@link ChannelConfig#isAutoRead()} is {@code false}.
191 */
192 private static final int STATE_FIRE_CHANNEL_READ = 1 << 8;
193 private static final int STATE_UNWRAP_REENTRY = 1 << 9;
194
195 /**
196 * <a href="https://tools.ietf.org/html/rfc5246#section-6.2">2^14</a> which is the maximum sized plaintext chunk
197 * allowed by the TLS RFC.
198 */
199 private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024;
200
201 private enum SslEngineType {
202 TCNATIVE(true, COMPOSITE_CUMULATOR) {
203 @Override
204 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException {
205 int nioBufferCount = in.nioBufferCount();
206 int writerIndex = out.writerIndex();
207 final SSLEngineResult result;
208 if (nioBufferCount > 1) {
209 /*
210 * If {@link OpenSslEngine} is in use,
211 * we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method
212 * that accepts multiple {@link ByteBuffer}s without additional memory copies.
213 */
214 ReferenceCountedOpenSslEngine opensslEngine = (ReferenceCountedOpenSslEngine) handler.engine;
215 try {
216 handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes());
217 result = opensslEngine.unwrap(in.nioBuffers(in.readerIndex(), len), handler.singleBuffer);
218 } finally {
219 handler.singleBuffer[0] = null;
220 }
221 } else {
222 result = handler.engine.unwrap(toByteBuffer(in, in.readerIndex(), len),
223 toByteBuffer(out, writerIndex, out.writableBytes()));
224 }
225 out.writerIndex(writerIndex + result.bytesProduced());
226 return result;
227 }
228
229 @Override
230 ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator,
231 int pendingBytes, int numComponents) {
232 return allocator.directBuffer(((ReferenceCountedOpenSslEngine) handler.engine)
233 .calculateOutNetBufSize(pendingBytes, numComponents));
234 }
235
236 @Override
237 int calculateRequiredOutBufSpace(SslHandler handler, int pendingBytes, int numComponents) {
238 return ((ReferenceCountedOpenSslEngine) handler.engine)
239 .calculateMaxLengthForWrap(pendingBytes, numComponents);
240 }
241
242 @Override
243 int calculatePendingData(SslHandler handler, int guess) {
244 int sslPending = ((ReferenceCountedOpenSslEngine) handler.engine).sslPending();
245 return sslPending > 0 ? sslPending : guess;
246 }
247
248 @Override
249 boolean jdkCompatibilityMode(SSLEngine engine) {
250 return ((ReferenceCountedOpenSslEngine) engine).jdkCompatibilityMode;
251 }
252 },
253 CONSCRYPT(true, COMPOSITE_CUMULATOR) {
254 @Override
255 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException {
256 int nioBufferCount = in.nioBufferCount();
257 int writerIndex = out.writerIndex();
258 final SSLEngineResult result;
259 if (nioBufferCount > 1) {
260 /*
261 * Use a special unwrap method without additional memory copies.
262 */
263 try {
264 handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes());
265 result = ((ConscryptAlpnSslEngine) handler.engine).unwrap(
266 in.nioBuffers(in.readerIndex(), len),
267 handler.singleBuffer);
268 } finally {
269 handler.singleBuffer[0] = null;
270 }
271 } else {
272 result = handler.engine.unwrap(toByteBuffer(in, in.readerIndex(), len),
273 toByteBuffer(out, writerIndex, out.writableBytes()));
274 }
275 out.writerIndex(writerIndex + result.bytesProduced());
276 return result;
277 }
278
279 @Override
280 ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator,
281 int pendingBytes, int numComponents) {
282 return allocator.directBuffer(
283 ((ConscryptAlpnSslEngine) handler.engine).calculateOutNetBufSize(pendingBytes, numComponents));
284 }
285
286 @Override
287 int calculateRequiredOutBufSpace(SslHandler handler, int pendingBytes, int numComponents) {
288 return ((ConscryptAlpnSslEngine) handler.engine)
289 .calculateRequiredOutBufSpace(pendingBytes, numComponents);
290 }
291
292 @Override
293 int calculatePendingData(SslHandler handler, int guess) {
294 return guess;
295 }
296
297 @Override
298 boolean jdkCompatibilityMode(SSLEngine engine) {
299 return true;
300 }
301 },
302 JDK(false, MERGE_CUMULATOR) {
303 @Override
304 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException {
305 int writerIndex = out.writerIndex();
306 ByteBuffer inNioBuffer = toByteBuffer(in, in.readerIndex(), len);
307 int position = inNioBuffer.position();
308 final SSLEngineResult result = handler.engine.unwrap(inNioBuffer,
309 toByteBuffer(out, writerIndex, out.writableBytes()));
310 out.writerIndex(writerIndex + result.bytesProduced());
311
312 // This is a workaround for a bug in Android 5.0. Android 5.0 does not correctly update the
313 // SSLEngineResult.bytesConsumed() in some cases and just return 0.
314 //
315 // See:
316 // - https://android-review.googlesource.com/c/platform/external/conscrypt/+/122080
317 // - https://github.com/netty/netty/issues/7758
318 if (result.bytesConsumed() == 0) {
319 int consumed = inNioBuffer.position() - position;
320 if (consumed != result.bytesConsumed()) {
321 // Create a new SSLEngineResult with the correct bytesConsumed().
322 return new SSLEngineResult(
323 result.getStatus(), result.getHandshakeStatus(), consumed, result.bytesProduced());
324 }
325 }
326 return result;
327 }
328
329 @Override
330 ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator,
331 int pendingBytes, int numComponents) {
332 // For JDK we don't have a good source for the max wrap overhead. We need at least one packet buffer
333 // size, but may be able to fit more in based on the total requested.
334 return allocator.heapBuffer(Math.max(pendingBytes, handler.engine.getSession().getPacketBufferSize()));
335 }
336
337 @Override
338 int calculateRequiredOutBufSpace(SslHandler handler, int pendingBytes, int numComponents) {
339 // As for the JDK SSLEngine we always need to operate on buffer space required by the SSLEngine
340 // (normally ~16KB). This is required even if the amount of data to encrypt is very small. Use heap
341 // buffers to reduce the native memory usage.
342 //
343 // Beside this the JDK SSLEngine also (as of today) will do an extra heap to direct buffer copy
344 // if a direct buffer is used as its internals operate on byte[].
345 return handler.engine.getSession().getPacketBufferSize();
346 }
347
348 @Override
349 int calculatePendingData(SslHandler handler, int guess) {
350 return guess;
351 }
352
353 @Override
354 boolean jdkCompatibilityMode(SSLEngine engine) {
355 return true;
356 }
357 };
358
359 static SslEngineType forEngine(SSLEngine engine) {
360 return engine instanceof ReferenceCountedOpenSslEngine ? TCNATIVE :
361 engine instanceof ConscryptAlpnSslEngine ? CONSCRYPT : JDK;
362 }
363
364 SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) {
365 this.wantsDirectBuffer = wantsDirectBuffer;
366 this.cumulator = cumulator;
367 }
368
369 abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException;
370
371 abstract int calculatePendingData(SslHandler handler, int guess);
372
373 abstract boolean jdkCompatibilityMode(SSLEngine engine);
374
375 abstract ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator,
376 int pendingBytes, int numComponents);
377
378 abstract int calculateRequiredOutBufSpace(SslHandler handler, int pendingBytes, int numComponents);
379
380 // BEGIN Platform-dependent flags
381
382 /**
383 * {@code true} if and only if {@link SSLEngine} expects a direct buffer and so if a heap buffer
384 * is given will make an extra memory copy.
385 */
386 final boolean wantsDirectBuffer;
387
388 // END Platform-dependent flags
389
390 /**
391 * When using JDK {@link SSLEngine}, we use {@link #MERGE_CUMULATOR} because it works only with
392 * one {@link ByteBuffer}.
393 *
394 * When using {@link OpenSslEngine}, we can use {@link #COMPOSITE_CUMULATOR} because it has
395 * {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} which works with multiple {@link ByteBuffer}s
396 * and which does not need to do extra memory copies.
397 */
398 final Cumulator cumulator;
399 }
400
401 private volatile ChannelHandlerContext ctx;
402 private final SSLEngine engine;
403 private final SslEngineType engineType;
404 private final Executor delegatedTaskExecutor;
405 private final boolean jdkCompatibilityMode;
406
407 /**
408 * Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])}
409 * should be called with a {@link ByteBuf} that is only backed by one {@link ByteBuffer} to reduce the object
410 * creation.
411 */
412 private final ByteBuffer[] singleBuffer = new ByteBuffer[1];
413
414 private final boolean startTls;
415 private final ResumptionController resumptionController;
416
417 private final SslTasksRunner sslTaskRunnerForUnwrap = new SslTasksRunner(true);
418 private final SslTasksRunner sslTaskRunner = new SslTasksRunner(false);
419
420 private SslHandlerCoalescingBufferQueue pendingUnencryptedWrites;
421 private Promise<Channel> handshakePromise = new LazyChannelPromise();
422 private final LazyChannelPromise sslClosePromise = new LazyChannelPromise();
423
424 private int packetLength;
425 private short state;
426
427 private volatile long handshakeTimeoutMillis = 10000;
428 private volatile long closeNotifyFlushTimeoutMillis = 3000;
429 private volatile long closeNotifyReadTimeoutMillis;
430 volatile int wrapDataSize = MAX_PLAINTEXT_LENGTH;
431
432 /**
433 * Creates a new instance which runs all delegated tasks directly on the {@link EventExecutor}.
434 *
435 * @param engine the {@link SSLEngine} this handler will use
436 */
437 public SslHandler(SSLEngine engine) {
438 this(engine, false);
439 }
440
441 /**
442 * Creates a new instance which runs all delegated tasks directly on the {@link EventExecutor}.
443 *
444 * @param engine the {@link SSLEngine} this handler will use
445 * @param startTls {@code true} if the first write request shouldn't be
446 * encrypted by the {@link SSLEngine}
447 */
448 public SslHandler(SSLEngine engine, boolean startTls) {
449 this(engine, startTls, ImmediateExecutor.INSTANCE);
450 }
451
452 /**
453 * Creates a new instance.
454 *
455 * @param engine the {@link SSLEngine} this handler will use
456 * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
457 * {@link SSLEngine#getDelegatedTask()}.
458 */
459 public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) {
460 this(engine, false, delegatedTaskExecutor);
461 }
462
463 /**
464 * Creates a new instance.
465 *
466 * @param engine the {@link SSLEngine} this handler will use
467 * @param startTls {@code true} if the first write request shouldn't be
468 * encrypted by the {@link SSLEngine}
469 * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by
470 * {@link SSLEngine#getDelegatedTask()}.
471 */
472 public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) {
473 this(engine, startTls, delegatedTaskExecutor, null);
474 }
475
476 SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor,
477 ResumptionController resumptionController) {
478 this.engine = ObjectUtil.checkNotNull(engine, "engine");
479 this.delegatedTaskExecutor = ObjectUtil.checkNotNull(delegatedTaskExecutor, "delegatedTaskExecutor");
480 engineType = SslEngineType.forEngine(engine);
481 this.startTls = startTls;
482 this.jdkCompatibilityMode = engineType.jdkCompatibilityMode(engine);
483 setCumulator(engineType.cumulator);
484 this.resumptionController = resumptionController;
485 }
486
487 public long getHandshakeTimeoutMillis() {
488 return handshakeTimeoutMillis;
489 }
490
491 public void setHandshakeTimeout(long handshakeTimeout, TimeUnit unit) {
492 checkNotNull(unit, "unit");
493 setHandshakeTimeoutMillis(unit.toMillis(handshakeTimeout));
494 }
495
496 public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) {
497 this.handshakeTimeoutMillis = checkPositiveOrZero(handshakeTimeoutMillis, "handshakeTimeoutMillis");
498 }
499
500 /**
501 * Sets the number of bytes to pass to each {@link SSLEngine#wrap(ByteBuffer[], int, int, ByteBuffer)} call.
502 * <p>
503 * This value will partition data which is passed to write
504 * {@link #write(ChannelHandlerContext, Object, ChannelPromise)}. The partitioning will work as follows:
505 * <ul>
506 * <li>If {@code wrapDataSize <= 0} then we will write each data chunk as is.</li>
507 * <li>If {@code wrapDataSize > data size} then we will attempt to aggregate multiple data chunks together.</li>
508 * <li>If {@code wrapDataSize > data size} Else if {@code wrapDataSize <= data size} then we will divide the data
509 * into chunks of {@code wrapDataSize} when writing.</li>
510 * </ul>
511 * <p>
512 * If the {@link SSLEngine} doesn't support a gather wrap operation (e.g. {@link SslProvider#OPENSSL}) then
513 * aggregating data before wrapping can help reduce the ratio between TLS overhead vs data payload which will lead
514 * to better goodput. Writing fixed chunks of data can also help target the underlying transport's (e.g. TCP)
515 * frame size. Under lossy/congested network conditions this may help the peer get full TLS packets earlier and
516 * be able to do work sooner, as opposed to waiting for the all the pieces of the TLS packet to arrive.
517 * @param wrapDataSize the number of bytes which will be passed to each
518 * {@link SSLEngine#wrap(ByteBuffer[], int, int, ByteBuffer)} call.
519 */
520 @UnstableApi
521 public final void setWrapDataSize(int wrapDataSize) {
522 this.wrapDataSize = wrapDataSize;
523 }
524
525 /**
526 * @deprecated use {@link #getCloseNotifyFlushTimeoutMillis()}
527 */
528 @Deprecated
529 public long getCloseNotifyTimeoutMillis() {
530 return getCloseNotifyFlushTimeoutMillis();
531 }
532
533 /**
534 * @deprecated use {@link #setCloseNotifyFlushTimeout(long, TimeUnit)}
535 */
536 @Deprecated
537 public void setCloseNotifyTimeout(long closeNotifyTimeout, TimeUnit unit) {
538 setCloseNotifyFlushTimeout(closeNotifyTimeout, unit);
539 }
540
541 /**
542 * @deprecated use {@link #setCloseNotifyFlushTimeoutMillis(long)}
543 */
544 @Deprecated
545 public void setCloseNotifyTimeoutMillis(long closeNotifyFlushTimeoutMillis) {
546 setCloseNotifyFlushTimeoutMillis(closeNotifyFlushTimeoutMillis);
547 }
548
549 /**
550 * Gets the timeout for flushing the close_notify that was triggered by closing the
551 * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed
552 * forcibly.
553 */
554 public final long getCloseNotifyFlushTimeoutMillis() {
555 return closeNotifyFlushTimeoutMillis;
556 }
557
558 /**
559 * Sets the timeout for flushing the close_notify that was triggered by closing the
560 * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed
561 * forcibly.
562 */
563 public final void setCloseNotifyFlushTimeout(long closeNotifyFlushTimeout, TimeUnit unit) {
564 setCloseNotifyFlushTimeoutMillis(unit.toMillis(closeNotifyFlushTimeout));
565 }
566
567 /**
568 * See {@link #setCloseNotifyFlushTimeout(long, TimeUnit)}.
569 */
570 public final void setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis) {
571 this.closeNotifyFlushTimeoutMillis = checkPositiveOrZero(closeNotifyFlushTimeoutMillis,
572 "closeNotifyFlushTimeoutMillis");
573 }
574
575 /**
576 * Gets the timeout (in ms) for receiving the response for the close_notify that was triggered by closing the
577 * {@link Channel}. This timeout starts after the close_notify message was successfully written to the
578 * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response.
579 */
580 public final long getCloseNotifyReadTimeoutMillis() {
581 return closeNotifyReadTimeoutMillis;
582 }
583
584 /**
585 * Sets the timeout for receiving the response for the close_notify that was triggered by closing the
586 * {@link Channel}. This timeout starts after the close_notify message was successfully written to the
587 * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response.
588 */
589 public final void setCloseNotifyReadTimeout(long closeNotifyReadTimeout, TimeUnit unit) {
590 setCloseNotifyReadTimeoutMillis(unit.toMillis(closeNotifyReadTimeout));
591 }
592
593 /**
594 * See {@link #setCloseNotifyReadTimeout(long, TimeUnit)}.
595 */
596 public final void setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis) {
597 this.closeNotifyReadTimeoutMillis = checkPositiveOrZero(closeNotifyReadTimeoutMillis,
598 "closeNotifyReadTimeoutMillis");
599 }
600
601 /**
602 * Returns the {@link SSLEngine} which is used by this handler.
603 */
604 public SSLEngine engine() {
605 return engine;
606 }
607
608 /**
609 * Returns the name of the current application-level protocol.
610 *
611 * @return the protocol name or {@code null} if application-level protocol has not been negotiated
612 */
613 public String applicationProtocol() {
614 SSLEngine engine = engine();
615 if (!(engine instanceof ApplicationProtocolAccessor)) {
616 return null;
617 }
618
619 return ((ApplicationProtocolAccessor) engine).getNegotiatedApplicationProtocol();
620 }
621
622 /**
623 * Returns a {@link Future} that will get notified once the current TLS handshake completes.
624 *
625 * @return the {@link Future} for the initial TLS handshake if {@link #renegotiate()} was not invoked.
626 * The {@link Future} for the most recent {@linkplain #renegotiate() TLS renegotiation} otherwise.
627 */
628 public Future<Channel> handshakeFuture() {
629 return handshakePromise;
630 }
631
632 /**
633 * Use {@link #closeOutbound()}
634 */
635 @Deprecated
636 public ChannelFuture close() {
637 return closeOutbound();
638 }
639
640 /**
641 * Use {@link #closeOutbound(ChannelPromise)}
642 */
643 @Deprecated
644 public ChannelFuture close(ChannelPromise promise) {
645 return closeOutbound(promise);
646 }
647
648 /**
649 * Sends an SSL {@code close_notify} message to the specified channel and
650 * destroys the underlying {@link SSLEngine}. This will <strong>not</strong> close the underlying
651 * {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or
652 * {@link ChannelHandlerContext#close()}
653 */
654 public ChannelFuture closeOutbound() {
655 return closeOutbound(ctx.newPromise());
656 }
657
658 /**
659 * Sends an SSL {@code close_notify} message to the specified channel and
660 * destroys the underlying {@link SSLEngine}. This will <strong>not</strong> close the underlying
661 * {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or
662 * {@link ChannelHandlerContext#close()}
663 */
664 public ChannelFuture closeOutbound(final ChannelPromise promise) {
665 final ChannelHandlerContext ctx = this.ctx;
666 if (ctx.executor().inEventLoop()) {
667 closeOutbound0(promise);
668 } else {
669 ctx.executor().execute(new Runnable() {
670 @Override
671 public void run() {
672 closeOutbound0(promise);
673 }
674 });
675 }
676 return promise;
677 }
678
679 private void closeOutbound0(ChannelPromise promise) {
680 setState(STATE_OUTBOUND_CLOSED);
681 engine.closeOutbound();
682 try {
683 flush(ctx, promise);
684 } catch (Exception e) {
685 if (!promise.tryFailure(e)) {
686 logger.warn("{} flush() raised a masked exception.", ctx.channel(), e);
687 }
688 }
689 }
690
691 /**
692 * Return the {@link Future} that will get notified if the inbound of the {@link SSLEngine} is closed.
693 *
694 * This method will return the same {@link Future} all the time.
695 *
696 * @see SSLEngine
697 */
698 public Future<Channel> sslCloseFuture() {
699 return sslClosePromise;
700 }
701
702 @Override
703 public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
704 try {
705 if (pendingUnencryptedWrites != null && !pendingUnencryptedWrites.isEmpty()) {
706 // Check if queue is not empty first because create a new ChannelException is expensive
707 pendingUnencryptedWrites.releaseAndFailAll(ctx,
708 new ChannelException("Pending write on removal of SslHandler"));
709 }
710 pendingUnencryptedWrites = null;
711
712 SSLException cause = null;
713
714 // If the handshake or SSLEngine closure is not done yet we should fail corresponding promise and
715 // notify the rest of the
716 // pipeline.
717 if (!handshakePromise.isDone()) {
718 cause = new SSLHandshakeException("SslHandler removed before handshake completed");
719 if (handshakePromise.tryFailure(cause)) {
720 ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause));
721 }
722 }
723 if (!sslClosePromise.isDone()) {
724 if (cause == null) {
725 cause = new SSLException("SslHandler removed before SSLEngine was closed");
726 }
727 notifyClosePromise(cause);
728 }
729 } finally {
730 ReferenceCountUtil.release(engine);
731 }
732 }
733
734 @Override
735 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
736 ctx.bind(localAddress, promise);
737 }
738
739 @Override
740 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
741 ChannelPromise promise) throws Exception {
742 ctx.connect(remoteAddress, localAddress, promise);
743 }
744
745 @Override
746 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
747 ctx.deregister(promise);
748 }
749
750 @Override
751 public void disconnect(final ChannelHandlerContext ctx,
752 final ChannelPromise promise) throws Exception {
753 closeOutboundAndChannel(ctx, promise, true);
754 }
755
756 @Override
757 public void close(final ChannelHandlerContext ctx,
758 final ChannelPromise promise) throws Exception {
759 closeOutboundAndChannel(ctx, promise, false);
760 }
761
762 @Override
763 public void read(ChannelHandlerContext ctx) throws Exception {
764 if (!handshakePromise.isDone()) {
765 setState(STATE_READ_DURING_HANDSHAKE);
766 }
767
768 ctx.read();
769 }
770
771 private static IllegalStateException newPendingWritesNullException() {
772 return new IllegalStateException("pendingUnencryptedWrites is null, handlerRemoved0 called?");
773 }
774
775 @Override
776 public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
777 if (!(msg instanceof ByteBuf)) {
778 UnsupportedMessageTypeException exception = new UnsupportedMessageTypeException(msg, ByteBuf.class);
779 ReferenceCountUtil.safeRelease(msg);
780 promise.setFailure(exception);
781 } else if (pendingUnencryptedWrites == null) {
782 ReferenceCountUtil.safeRelease(msg);
783 promise.setFailure(newPendingWritesNullException());
784 } else {
785 pendingUnencryptedWrites.add((ByteBuf) msg, promise);
786 }
787 }
788
789 @Override
790 public void flush(ChannelHandlerContext ctx) throws Exception {
791 // Do not encrypt the first write request if this handler is
792 // created with startTLS flag turned on.
793 if (startTls && !isStateSet(STATE_SENT_FIRST_MESSAGE)) {
794 setState(STATE_SENT_FIRST_MESSAGE);
795 pendingUnencryptedWrites.writeAndRemoveAll(ctx);
796 forceFlush(ctx);
797 // Explicit start handshake processing once we send the first message. This will also ensure
798 // we will schedule the timeout if needed.
799 startHandshakeProcessing(true);
800 return;
801 }
802
803 if (isStateSet(STATE_PROCESS_TASK)) {
804 return;
805 }
806
807 try {
808 wrapAndFlush(ctx);
809 } catch (Throwable cause) {
810 setHandshakeFailure(ctx, cause);
811 PlatformDependent.throwException(cause);
812 }
813 }
814
815 private void wrapAndFlush(ChannelHandlerContext ctx) throws SSLException {
816 if (pendingUnencryptedWrites.isEmpty()) {
817 // It's important to NOT use a voidPromise here as the user
818 // may want to add a ChannelFutureListener to the ChannelPromise later.
819 //
820 // See https://github.com/netty/netty/issues/3364
821 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise());
822 }
823 if (!handshakePromise.isDone()) {
824 setState(STATE_FLUSHED_BEFORE_HANDSHAKE);
825 }
826 try {
827 wrap(ctx, false);
828 } finally {
829 // We may have written some parts of data before an exception was thrown so ensure we always flush.
830 // See https://github.com/netty/netty/issues/3900#issuecomment-172481830
831 forceFlush(ctx);
832 }
833 }
834
835 // This method will not call setHandshakeFailure(...) !
836 private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
837 ByteBuf out = null;
838 ByteBufAllocator alloc = ctx.alloc();
839 try {
840 final int wrapDataSize = this.wrapDataSize;
841 // Only continue to loop if the handler was not removed in the meantime.
842 // See https://github.com/netty/netty/issues/5860
843 outer: while (!ctx.isRemoved()) {
844 ChannelPromise promise = ctx.newPromise();
845 ByteBuf buf = wrapDataSize > 0 ?
846 pendingUnencryptedWrites.remove(alloc, wrapDataSize, promise) :
847 pendingUnencryptedWrites.removeFirst(promise);
848 if (buf == null) {
849 break;
850 }
851
852 SSLEngineResult result;
853
854 try {
855 if (buf.readableBytes() > MAX_PLAINTEXT_LENGTH) {
856 // If we pulled a buffer larger than the supported packet size, we can slice it up and
857 // iteratively, encrypting multiple packets into a single larger buffer. This substantially
858 // saves on allocations for large responses. Here we estimate how large of a buffer we need.
859 // If we overestimate a bit, that's fine. If we underestimate, we'll simply re-enqueue the
860 // remaining buffer and get it on the next outer loop.
861 int readableBytes = buf.readableBytes();
862 int numPackets = readableBytes / MAX_PLAINTEXT_LENGTH;
863 if (readableBytes % MAX_PLAINTEXT_LENGTH != 0) {
864 numPackets += 1;
865 }
866
867 if (out == null) {
868 out = allocateOutNetBuf(ctx, readableBytes, buf.nioBufferCount() + numPackets);
869 }
870 result = wrapMultiple(alloc, engine, buf, out);
871 } else {
872 if (out == null) {
873 out = allocateOutNetBuf(ctx, buf.readableBytes(), buf.nioBufferCount());
874 }
875 result = wrap(alloc, engine, buf, out);
876 }
877 } catch (SSLException e) {
878 // Either wrapMultiple(...) or wrap(...) did throw. In this case we need to release the buffer
879 // that we removed from pendingUnencryptedWrites before failing the promise and rethrowing it.
880 // Failing to do so would result in a buffer leak.
881 // See https://github.com/netty/netty/issues/14644
882 //
883 // We don't need to release out here as this is done in a finally block already.
884 buf.release();
885 promise.setFailure(e);
886 throw e;
887 }
888
889 if (buf.isReadable()) {
890 pendingUnencryptedWrites.addFirst(buf, promise);
891 // When we add the buffer/promise pair back we need to be sure we don't complete the promise
892 // later. We only complete the promise if the buffer is completely consumed.
893 promise = null;
894 } else {
895 buf.release();
896 }
897
898 // We need to write any data before we invoke any methods which may trigger re-entry, otherwise
899 // writes may occur out of order and TLS sequencing may be off (e.g. SSLV3_ALERT_BAD_RECORD_MAC).
900 if (out.isReadable()) {
901 final ByteBuf b = out;
902 out = null;
903 if (promise != null) {
904 ctx.write(b, promise);
905 } else {
906 ctx.write(b);
907 }
908 } else if (promise != null) {
909 ctx.write(Unpooled.EMPTY_BUFFER, promise);
910 }
911 // else out is not readable we can re-use it and so save an extra allocation
912
913 if (result.getStatus() == Status.CLOSED) {
914 // First check if there is any write left that needs to be failed, if there is none we don't need
915 // to create a new exception or obtain an existing one.
916 if (!pendingUnencryptedWrites.isEmpty()) {
917 // Make a best effort to preserve any exception that way previously encountered from the
918 // handshake or the transport, else fallback to a general error.
919 Throwable exception = handshakePromise.cause();
920 if (exception == null) {
921 exception = sslClosePromise.cause();
922 if (exception == null) {
923 exception = new SslClosedEngineException("SSLEngine closed already");
924 }
925 }
926 pendingUnencryptedWrites.releaseAndFailAll(ctx, exception);
927 }
928
929 return;
930 } else {
931 switch (result.getHandshakeStatus()) {
932 case NEED_TASK:
933 if (!runDelegatedTasks(inUnwrap)) {
934 // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will
935 // resume once the task completes.
936 break outer;
937 }
938 break;
939 case FINISHED:
940 case NOT_HANDSHAKING: // work around for android bug that skips the FINISHED state.
941 setHandshakeSuccess();
942 break;
943 case NEED_WRAP:
944 // If we are expected to wrap again and we produced some data we need to ensure there
945 // is something in the queue to process as otherwise we will not try again before there
946 // was more added. Failing to do so may fail to produce an alert that can be
947 // consumed by the remote peer.
948 if (result.bytesProduced() > 0 && pendingUnencryptedWrites.isEmpty()) {
949 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER);
950 }
951 break;
952 case NEED_UNWRAP:
953 // The underlying engine is starving so we need to feed it with more data.
954 // See https://github.com/netty/netty/pull/5039
955 readIfNeeded(ctx);
956 return;
957 default:
958 throw new IllegalStateException(
959 "Unknown handshake status: " + result.getHandshakeStatus());
960 }
961 }
962 }
963 } finally {
964 if (out != null) {
965 out.release();
966 }
967 if (inUnwrap) {
968 setState(STATE_NEEDS_FLUSH);
969 }
970 }
971 }
972
973 /**
974 * This method will not call
975 * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean, boolean, boolean)} or
976 * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}.
977 * @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}.
978 */
979 private boolean wrapNonAppData(final ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
980 ByteBuf out = null;
981 ByteBufAllocator alloc = ctx.alloc();
982 try {
983 // Only continue to loop if the handler was not removed in the meantime.
984 // See https://github.com/netty/netty/issues/5860
985 outer: while (!ctx.isRemoved()) {
986 if (out == null) {
987 // As this is called for the handshake we have no real idea how big the buffer needs to be.
988 // That said 2048 should give us enough room to include everything like ALPN / NPN data.
989 // If this is not enough we will increase the buffer in wrap(...).
990 out = allocateOutNetBuf(ctx, 2048, 1);
991 }
992 SSLEngineResult result = wrap(alloc, engine, Unpooled.EMPTY_BUFFER, out);
993 if (result.bytesProduced() > 0) {
994 ctx.write(out).addListener(new ChannelFutureListener() {
995 @Override
996 public void operationComplete(ChannelFuture future) {
997 Throwable cause = future.cause();
998 if (cause != null) {
999 setHandshakeFailureTransportFailure(ctx, cause);
1000 }
1001 }
1002 });
1003 if (inUnwrap) {
1004 setState(STATE_NEEDS_FLUSH);
1005 }
1006 out = null;
1007 }
1008
1009 HandshakeStatus status = result.getHandshakeStatus();
1010 switch (status) {
1011 case FINISHED:
1012 // We may be here because we read data and discovered the remote peer initiated a renegotiation
1013 // and this write is to complete the new handshake. The user may have previously done a
1014 // writeAndFlush which wasn't able to wrap data due to needing the pending handshake, so we
1015 // attempt to wrap application data here if any is pending.
1016 if (setHandshakeSuccess() && inUnwrap && !pendingUnencryptedWrites.isEmpty()) {
1017 wrap(ctx, true);
1018 }
1019 return false;
1020 case NEED_TASK:
1021 if (!runDelegatedTasks(inUnwrap)) {
1022 // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will
1023 // resume once the task completes.
1024 break outer;
1025 }
1026 break;
1027 case NEED_UNWRAP:
1028 if (inUnwrap || unwrapNonAppData(ctx) <= 0) {
1029 // If we asked for a wrap, the engine requested an unwrap, and we are in unwrap there is
1030 // no use in trying to call wrap again because we have already attempted (or will after we
1031 // return) to feed more data to the engine.
1032 return false;
1033 }
1034 break;
1035 case NEED_WRAP:
1036 break;
1037 case NOT_HANDSHAKING:
1038 if (setHandshakeSuccess() && inUnwrap && !pendingUnencryptedWrites.isEmpty()) {
1039 wrap(ctx, true);
1040 }
1041 // Workaround for TLS False Start problem reported at:
1042 // https://github.com/netty/netty/issues/1108#issuecomment-14266970
1043 if (!inUnwrap) {
1044 unwrapNonAppData(ctx);
1045 }
1046 return true;
1047 default:
1048 throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus());
1049 }
1050
1051 // Check if did not produce any bytes and if so break out of the loop, but only if we did not process
1052 // a task as last action. It's fine to not produce any data as part of executing a task.
1053 if (result.bytesProduced() == 0 && status != HandshakeStatus.NEED_TASK) {
1054 break;
1055 }
1056
1057 // It should not consume empty buffers when it is not handshaking
1058 // Fix for Android, where it was encrypting empty buffers even when not handshaking
1059 if (result.bytesConsumed() == 0 && result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
1060 break;
1061 }
1062 }
1063 } finally {
1064 if (out != null) {
1065 out.release();
1066 }
1067 }
1068 return false;
1069 }
1070
1071 private SSLEngineResult wrapMultiple(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out)
1072 throws SSLException {
1073 SSLEngineResult result = null;
1074
1075 do {
1076 int nextSliceSize = Math.min(MAX_PLAINTEXT_LENGTH, in.readableBytes());
1077 // This call over-estimates, because we are slicing and not every nioBuffer will be part of
1078 // every slice. We could improve the estimate by having an nioBufferCount(offset, length).
1079 int nextOutSize = engineType.calculateRequiredOutBufSpace(this, nextSliceSize, in.nioBufferCount());
1080
1081 if (!out.isWritable(nextOutSize)) {
1082 if (result != null) {
1083 // We underestimated the space needed to encrypt the entire in buf. Break out, and
1084 // upstream will re-enqueue the buffer for later.
1085 break;
1086 }
1087 // This shouldn't happen, as the out buf was properly sized for at least packetLength
1088 // prior to calling wrap.
1089 out.ensureWritable(nextOutSize);
1090 }
1091
1092 ByteBuf wrapBuf = in.readSlice(nextSliceSize);
1093 result = wrap(alloc, engine, wrapBuf, out);
1094
1095 if (result.getStatus() == Status.CLOSED) {
1096 // If the engine gets closed, we can exit out early. Otherwise, we'll do a full handling of
1097 // possible results once finished.
1098 break;
1099 }
1100
1101 if (wrapBuf.isReadable()) {
1102 // There may be some left-over, in which case we can just pick it up next loop, so reset the original
1103 // reader index so its included again in the next slice.
1104 in.readerIndex(in.readerIndex() - wrapBuf.readableBytes());
1105 }
1106 } while (in.readableBytes() > 0);
1107
1108 return result;
1109 }
1110
1111 private SSLEngineResult wrap(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out)
1112 throws SSLException {
1113 ByteBuf newDirectIn = null;
1114 try {
1115 int readerIndex = in.readerIndex();
1116 int readableBytes = in.readableBytes();
1117
1118 // We will call SslEngine.wrap(ByteBuffer[], ByteBuffer) to allow efficient handling of
1119 // CompositeByteBuf without force an extra memory copy when CompositeByteBuffer.nioBuffer() is called.
1120 final ByteBuffer[] in0;
1121 if (in.isDirect() || !engineType.wantsDirectBuffer) {
1122 // As CompositeByteBuf.nioBufferCount() can be expensive (as it needs to check all composed ByteBuf
1123 // to calculate the count) we will just assume a CompositeByteBuf contains more then 1 ByteBuf.
1124 // The worst that can happen is that we allocate an extra ByteBuffer[] in CompositeByteBuf.nioBuffers()
1125 // which is better then walking the composed ByteBuf in most cases.
1126 if (!(in instanceof CompositeByteBuf) && in.nioBufferCount() == 1) {
1127 in0 = singleBuffer;
1128 // We know its only backed by 1 ByteBuffer so use internalNioBuffer to keep object allocation
1129 // to a minimum.
1130 in0[0] = in.internalNioBuffer(readerIndex, readableBytes);
1131 } else {
1132 in0 = in.nioBuffers();
1133 }
1134 } else {
1135 // We could even go further here and check if its a CompositeByteBuf and if so try to decompose it and
1136 // only replace the ByteBuffer that are not direct. At the moment we just will replace the whole
1137 // CompositeByteBuf to keep the complexity to a minimum
1138 newDirectIn = alloc.directBuffer(readableBytes);
1139 newDirectIn.writeBytes(in, readerIndex, readableBytes);
1140 in0 = singleBuffer;
1141 in0[0] = newDirectIn.internalNioBuffer(newDirectIn.readerIndex(), readableBytes);
1142 }
1143
1144 for (;;) {
1145 // Use toByteBuffer(...) which might be able to return the internal ByteBuffer and so reduce
1146 // allocations.
1147 ByteBuffer out0 = toByteBuffer(out, out.writerIndex(), out.writableBytes());
1148 SSLEngineResult result = engine.wrap(in0, out0);
1149 in.skipBytes(result.bytesConsumed());
1150 out.writerIndex(out.writerIndex() + result.bytesProduced());
1151
1152 if (result.getStatus() == Status.BUFFER_OVERFLOW) {
1153 out.ensureWritable(engine.getSession().getPacketBufferSize());
1154 } else {
1155 return result;
1156 }
1157 }
1158 } finally {
1159 // Null out to allow GC of ByteBuffer
1160 singleBuffer[0] = null;
1161
1162 if (newDirectIn != null) {
1163 newDirectIn.release();
1164 }
1165 }
1166 }
1167
1168 @Override
1169 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
1170 boolean handshakeFailed = handshakePromise.cause() != null;
1171
1172 // Channel closed, we will generate 'ClosedChannelException' now.
1173 ClosedChannelException exception = new ClosedChannelException();
1174
1175 // Add a supressed exception if the handshake was not completed yet.
1176 if (isStateSet(STATE_HANDSHAKE_STARTED) && !handshakePromise.isDone()) {
1177 ThrowableUtil.addSuppressed(exception, StacklessSSLHandshakeException.newInstance(
1178 "Connection closed while SSL/TLS handshake was in progress",
1179 SslHandler.class, "channelInactive"));
1180 }
1181
1182 // Make sure to release SSLEngine,
1183 // and notify the handshake future if the connection has been closed during handshake.
1184 setHandshakeFailure(ctx, exception, !isStateSet(STATE_OUTBOUND_CLOSED), isStateSet(STATE_HANDSHAKE_STARTED),
1185 false);
1186
1187 // Ensure we always notify the sslClosePromise as well
1188 notifyClosePromise(exception);
1189
1190 try {
1191 super.channelInactive(ctx);
1192 } catch (DecoderException e) {
1193 if (!handshakeFailed || !(e.getCause() instanceof SSLException)) {
1194 // We only rethrow the exception if the handshake did not fail before channelInactive(...) was called
1195 // as otherwise this may produce duplicated failures as super.channelInactive(...) will also call
1196 // channelRead(...).
1197 //
1198 // See https://github.com/netty/netty/issues/10119
1199 throw e;
1200 }
1201 }
1202 }
1203
1204 @Override
1205 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
1206 if (ignoreException(cause)) {
1207 // It is safe to ignore the 'connection reset by peer' or
1208 // 'broken pipe' error after sending close_notify.
1209 if (logger.isDebugEnabled()) {
1210 logger.debug(
1211 "{} Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred " +
1212 "while writing close_notify in response to the peer's close_notify", ctx.channel(), cause);
1213 }
1214
1215 // Close the connection explicitly just in case the transport
1216 // did not close the connection automatically.
1217 if (ctx.channel().isActive()) {
1218 ctx.close();
1219 }
1220 } else {
1221 ctx.fireExceptionCaught(cause);
1222 }
1223 }
1224
1225 /**
1226 * Checks if the given {@link Throwable} can be ignore and just "swallowed"
1227 *
1228 * When an ssl connection is closed a close_notify message is sent.
1229 * After that the peer also sends close_notify however, it's not mandatory to receive
1230 * the close_notify. The party who sent the initial close_notify can close the connection immediately
1231 * then the peer will get connection reset error.
1232 *
1233 */
1234 private boolean ignoreException(Throwable t) {
1235 if (!(t instanceof SSLException) && t instanceof IOException && sslClosePromise.isDone()) {
1236 String message = t.getMessage();
1237
1238 // first try to match connection reset / broke peer based on the regex. This is the fastest way
1239 // but may fail on different jdk impls or OS's
1240 if (message != null && IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
1241 return true;
1242 }
1243
1244 // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
1245 StackTraceElement[] elements = t.getStackTrace();
1246 for (StackTraceElement element: elements) {
1247 String classname = element.getClassName();
1248 String methodname = element.getMethodName();
1249
1250 // skip all classes that belong to the io.netty package
1251 if (classname.startsWith("io.netty.")) {
1252 continue;
1253 }
1254
1255 // check if the method name is read if not skip it
1256 if (!"read".equals(methodname)) {
1257 continue;
1258 }
1259
1260 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
1261 // also others
1262 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
1263 return true;
1264 }
1265
1266 try {
1267 // No match by now.. Try to load the class via classloader and inspect it.
1268 // This is mainly done as other JDK implementations may differ in name of
1269 // the impl.
1270 Class<?> clazz = PlatformDependent.getClassLoader(getClass()).loadClass(classname);
1271
1272 if (SocketChannel.class.isAssignableFrom(clazz)
1273 || DatagramChannel.class.isAssignableFrom(clazz)) {
1274 return true;
1275 }
1276
1277 // also match against SctpChannel via String matching as it may not present.
1278 if (PlatformDependent.javaVersion() >= 7
1279 && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
1280 return true;
1281 }
1282 } catch (Throwable cause) {
1283 if (logger.isDebugEnabled()) {
1284 logger.debug("Unexpected exception while loading class {} classname {}",
1285 getClass(), classname, cause);
1286 }
1287 }
1288 }
1289 }
1290
1291 return false;
1292 }
1293
1294 /**
1295 * Returns {@code true} if the given {@link ByteBuf} is encrypted. Be aware that this method
1296 * will not increase the readerIndex of the given {@link ByteBuf}.
1297 *
1298 * @param buffer
1299 * The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read,
1300 * otherwise it will throw an {@link IllegalArgumentException}.
1301 * @return encrypted
1302 * {@code true} if the {@link ByteBuf} is encrypted, {@code false} otherwise.
1303 * @throws IllegalArgumentException
1304 * Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read.
1305 * @deprecated use {@link #isEncrypted(ByteBuf, boolean)}.
1306 */
1307 @Deprecated
1308 public static boolean isEncrypted(ByteBuf buffer) {
1309 return isEncrypted(buffer, false);
1310 }
1311
1312 /**
1313 * Returns {@code true} if the given {@link ByteBuf} is encrypted. Be aware that this method
1314 * will not increase the readerIndex of the given {@link ByteBuf}.
1315 *
1316 * @param buffer
1317 * The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read,
1318 * otherwise it will throw an {@link IllegalArgumentException}.
1319 * @return encrypted
1320 * {@code true} if the {@link ByteBuf} is encrypted, {@code false} otherwise.
1321 * @param probeSSLv2
1322 * {@code true} if the input {@code buffer} might be SSLv2. If {@code true} is used this
1323 * methods might produce false-positives in some cases so it's strongly suggested to
1324 * use {@code false}.
1325 * @throws IllegalArgumentException
1326 * Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read.
1327 */
1328 public static boolean isEncrypted(ByteBuf buffer, boolean probeSSLv2) {
1329 if (buffer.readableBytes() < SslUtils.SSL_RECORD_HEADER_LENGTH) {
1330 throw new IllegalArgumentException(
1331 "buffer must have at least " + SslUtils.SSL_RECORD_HEADER_LENGTH + " readable bytes");
1332 }
1333 return getEncryptedPacketLength(buffer, buffer.readerIndex(), probeSSLv2) != SslUtils.NOT_ENCRYPTED;
1334 }
1335
1336 private void decodeJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) throws NotSslRecordException {
1337 int packetLength = this.packetLength;
1338 // If we calculated the length of the current SSL record before, use that information.
1339 if (packetLength > 0) {
1340 if (in.readableBytes() < packetLength) {
1341 return;
1342 }
1343 } else {
1344 // Get the packet length and wait until we get a packets worth of data to unwrap.
1345 final int readableBytes = in.readableBytes();
1346 if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) {
1347 return;
1348 }
1349 packetLength = getEncryptedPacketLength(in, in.readerIndex(), true);
1350 if (packetLength == SslUtils.NOT_ENCRYPTED) {
1351 // Not an SSL/TLS packet
1352 NotSslRecordException e = new NotSslRecordException(
1353 "not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
1354 in.skipBytes(in.readableBytes());
1355
1356 // First fail the handshake promise as we may need to have access to the SSLEngine which may
1357 // be released because the user will remove the SslHandler in an exceptionCaught(...) implementation.
1358 setHandshakeFailure(ctx, e);
1359
1360 throw e;
1361 }
1362 if (packetLength == NOT_ENOUGH_DATA) {
1363 return;
1364 }
1365 assert packetLength > 0;
1366 if (packetLength > readableBytes) {
1367 // wait until the whole packet can be read
1368 this.packetLength = packetLength;
1369 return;
1370 }
1371 }
1372
1373 // Reset the state of this class so we can get the length of the next packet. We assume the entire packet will
1374 // be consumed by the SSLEngine.
1375 this.packetLength = 0;
1376 try {
1377 final int bytesConsumed = unwrap(ctx, in, packetLength);
1378 if (bytesConsumed != packetLength && !engine.isInboundDone()) {
1379 // The JDK equivalent of getEncryptedPacketLength has some optimizations and can behave slightly
1380 // differently to ours, but this should always be a sign of bad input data.
1381 throw new NotSslRecordException();
1382 }
1383 } catch (Throwable cause) {
1384 handleUnwrapThrowable(ctx, cause);
1385 }
1386 }
1387
1388 private void decodeNonJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) {
1389 try {
1390 unwrap(ctx, in, in.readableBytes());
1391 } catch (Throwable cause) {
1392 handleUnwrapThrowable(ctx, cause);
1393 }
1394 }
1395
1396 private void handleUnwrapThrowable(ChannelHandlerContext ctx, Throwable cause) {
1397 try {
1398 // We should attempt to notify the handshake failure before writing any pending data. If we are in unwrap
1399 // and failed during the handshake process, and we attempt to wrap, then promises will fail, and if
1400 // listeners immediately close the Channel then we may end up firing the handshake event after the Channel
1401 // has been closed.
1402 if (handshakePromise.tryFailure(cause)) {
1403 ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause));
1404 }
1405
1406 // Let's check if the handler was removed in the meantime and so pendingUnencryptedWrites is null.
1407 if (pendingUnencryptedWrites != null) {
1408 // We need to flush one time as there may be an alert that we should send to the remote peer because
1409 // of the SSLException reported here.
1410 wrapAndFlush(ctx);
1411 }
1412 } catch (SSLException ex) {
1413 logger.debug("SSLException during trying to call SSLEngine.wrap(...)" +
1414 " because of an previous SSLException, ignoring...", ex);
1415 } finally {
1416 // ensure we always flush and close the channel.
1417 setHandshakeFailure(ctx, cause, true, false, true);
1418 }
1419 PlatformDependent.throwException(cause);
1420 }
1421
1422 @Override
1423 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException {
1424 if (isStateSet(STATE_PROCESS_TASK)) {
1425 return;
1426 }
1427 if (jdkCompatibilityMode) {
1428 decodeJdkCompatible(ctx, in);
1429 } else {
1430 decodeNonJdkCompatible(ctx, in);
1431 }
1432 }
1433
1434 @Override
1435 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
1436 channelReadComplete0(ctx);
1437 }
1438
1439 private void channelReadComplete0(ChannelHandlerContext ctx) {
1440 // Discard bytes of the cumulation buffer if needed.
1441 discardSomeReadBytes();
1442
1443 flushIfNeeded(ctx);
1444 readIfNeeded(ctx);
1445
1446 clearState(STATE_FIRE_CHANNEL_READ);
1447 ctx.fireChannelReadComplete();
1448 }
1449
1450 private void readIfNeeded(ChannelHandlerContext ctx) {
1451 // If handshake is not finished yet, we need more data.
1452 if (!ctx.channel().config().isAutoRead() &&
1453 (!isStateSet(STATE_FIRE_CHANNEL_READ) || !handshakePromise.isDone())) {
1454 // No auto-read used and no message passed through the ChannelPipeline or the handshake was not complete
1455 // yet, which means we need to trigger the read to ensure we not encounter any stalls.
1456 ctx.read();
1457 }
1458 }
1459
1460 private void flushIfNeeded(ChannelHandlerContext ctx) {
1461 if (isStateSet(STATE_NEEDS_FLUSH)) {
1462 forceFlush(ctx);
1463 }
1464 }
1465
1466 /**
1467 * Calls {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} with an empty buffer to handle handshakes, etc.
1468 */
1469 private int unwrapNonAppData(ChannelHandlerContext ctx) throws SSLException {
1470 return unwrap(ctx, Unpooled.EMPTY_BUFFER, 0);
1471 }
1472
1473 /**
1474 * Unwraps inbound SSL records.
1475 */
1476 private int unwrap(ChannelHandlerContext ctx, ByteBuf packet, int length) throws SSLException {
1477 final int originalLength = length;
1478 boolean wrapLater = false;
1479 boolean notifyClosure = false;
1480 boolean executedRead = false;
1481 ByteBuf decodeOut = allocate(ctx, length);
1482 try {
1483 // Only continue to loop if the handler was not removed in the meantime.
1484 // See https://github.com/netty/netty/issues/5860
1485 do {
1486 final SSLEngineResult result = engineType.unwrap(this, packet, length, decodeOut);
1487 final Status status = result.getStatus();
1488 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1489 final int produced = result.bytesProduced();
1490 final int consumed = result.bytesConsumed();
1491
1492 // Skip bytes now in case unwrap is called in a re-entry scenario. For example LocalChannel.read()
1493 // may entry this method in a re-entry fashion and if the peer is writing into a shared buffer we may
1494 // unwrap the same data multiple times.
1495 packet.skipBytes(consumed);
1496 length -= consumed;
1497
1498 // The expected sequence of events is:
1499 // 1. Notify of handshake success
1500 // 2. fireChannelRead for unwrapped data
1501 if (handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) {
1502 wrapLater |= (decodeOut.isReadable() ?
1503 setHandshakeSuccessUnwrapMarkReentry() : setHandshakeSuccess()) ||
1504 handshakeStatus == HandshakeStatus.FINISHED ||
1505 // We need to check if pendingUnecryptedWrites is null as the SslHandler
1506 // might have been removed in the meantime.
1507 (pendingUnencryptedWrites != null && !pendingUnencryptedWrites.isEmpty());
1508 }
1509
1510 // Dispatch decoded data after we have notified of handshake success. If this method has been invoked
1511 // in a re-entry fashion we execute a task on the executor queue to process after the stack unwinds
1512 // to preserve order of events.
1513 if (decodeOut.isReadable()) {
1514 setState(STATE_FIRE_CHANNEL_READ);
1515 if (isStateSet(STATE_UNWRAP_REENTRY)) {
1516 executedRead = true;
1517 executeChannelRead(ctx, decodeOut);
1518 } else {
1519 ctx.fireChannelRead(decodeOut);
1520 }
1521 decodeOut = null;
1522 }
1523
1524 if (status == Status.CLOSED) {
1525 notifyClosure = true; // notify about the CLOSED state of the SSLEngine. See #137
1526 } else if (status == Status.BUFFER_OVERFLOW) {
1527 if (decodeOut != null) {
1528 decodeOut.release();
1529 }
1530 final int applicationBufferSize = engine.getSession().getApplicationBufferSize();
1531 // Allocate a new buffer which can hold all the rest data and loop again.
1532 // It may happen that applicationBufferSize < produced while there is still more to unwrap, in this
1533 // case we will just allocate a new buffer with the capacity of applicationBufferSize and call
1534 // unwrap again.
1535 decodeOut = allocate(ctx, engineType.calculatePendingData(this, applicationBufferSize < produced ?
1536 applicationBufferSize : applicationBufferSize - produced));
1537 continue;
1538 }
1539
1540 if (handshakeStatus == HandshakeStatus.NEED_TASK) {
1541 boolean pending = runDelegatedTasks(true);
1542 if (!pending) {
1543 // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will
1544 // resume once the task completes.
1545 //
1546 // We break out of the loop only and do NOT return here as we still may need to notify
1547 // about the closure of the SSLEngine.
1548 wrapLater = false;
1549 break;
1550 }
1551 } else if (handshakeStatus == HandshakeStatus.NEED_WRAP) {
1552 // If the wrap operation transitions the status to NOT_HANDSHAKING and there is no more data to
1553 // unwrap then the next call to unwrap will not produce any data. We can avoid the potentially
1554 // costly unwrap operation and break out of the loop.
1555 if (wrapNonAppData(ctx, true) && length == 0) {
1556 break;
1557 }
1558 }
1559
1560 if (status == Status.BUFFER_UNDERFLOW ||
1561 // If we processed NEED_TASK we should try again even we did not consume or produce anything.
1562 handshakeStatus != HandshakeStatus.NEED_TASK && (consumed == 0 && produced == 0 ||
1563 (length == 0 && handshakeStatus == HandshakeStatus.NOT_HANDSHAKING))) {
1564 if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
1565 // The underlying engine is starving so we need to feed it with more data.
1566 // See https://github.com/netty/netty/pull/5039
1567 readIfNeeded(ctx);
1568 }
1569
1570 break;
1571 } else if (decodeOut == null) {
1572 decodeOut = allocate(ctx, length);
1573 }
1574 } while (!ctx.isRemoved());
1575
1576 if (isStateSet(STATE_FLUSHED_BEFORE_HANDSHAKE) && handshakePromise.isDone()) {
1577 // We need to call wrap(...) in case there was a flush done before the handshake completed to ensure
1578 // we do not stale.
1579 //
1580 // See https://github.com/netty/netty/pull/2437
1581 clearState(STATE_FLUSHED_BEFORE_HANDSHAKE);
1582 wrapLater = true;
1583 }
1584
1585 if (wrapLater) {
1586 wrap(ctx, true);
1587 }
1588 } finally {
1589 if (decodeOut != null) {
1590 decodeOut.release();
1591 }
1592
1593 if (notifyClosure) {
1594 if (executedRead) {
1595 executeNotifyClosePromise(ctx);
1596 } else {
1597 notifyClosePromise(null);
1598 }
1599 }
1600 }
1601 return originalLength - length;
1602 }
1603
1604 private boolean setHandshakeSuccessUnwrapMarkReentry() throws SSLException {
1605 // setHandshakeSuccess calls out to external methods which may trigger re-entry. We need to preserve ordering of
1606 // fireChannelRead for decodeOut relative to re-entry data.
1607 final boolean setReentryState = !isStateSet(STATE_UNWRAP_REENTRY);
1608 if (setReentryState) {
1609 setState(STATE_UNWRAP_REENTRY);
1610 }
1611 try {
1612 return setHandshakeSuccess();
1613 } finally {
1614 // It is unlikely this specific method will be re-entry because handshake completion is infrequent, but just
1615 // in case we only clear the state if we set it in the first place.
1616 if (setReentryState) {
1617 clearState(STATE_UNWRAP_REENTRY);
1618 }
1619 }
1620 }
1621
1622 private void executeNotifyClosePromise(final ChannelHandlerContext ctx) {
1623 try {
1624 ctx.executor().execute(new Runnable() {
1625 @Override
1626 public void run() {
1627 notifyClosePromise(null);
1628 }
1629 });
1630 } catch (RejectedExecutionException e) {
1631 notifyClosePromise(e);
1632 }
1633 }
1634
1635 private void executeChannelRead(final ChannelHandlerContext ctx, final ByteBuf decodedOut) {
1636 try {
1637 ctx.executor().execute(new Runnable() {
1638 @Override
1639 public void run() {
1640 ctx.fireChannelRead(decodedOut);
1641 }
1642 });
1643 } catch (RejectedExecutionException e) {
1644 decodedOut.release();
1645 throw e;
1646 }
1647 }
1648
1649 private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) {
1650 return out.nioBufferCount() == 1 ? out.internalNioBuffer(index, len) :
1651 out.nioBuffer(index, len);
1652 }
1653
1654 private static boolean inEventLoop(Executor executor) {
1655 return executor instanceof EventExecutor && ((EventExecutor) executor).inEventLoop();
1656 }
1657
1658 /**
1659 * Will either run the delegated task directly calling {@link Runnable#run()} and return {@code true} or will
1660 * offload the delegated task using {@link Executor#execute(Runnable)} and return {@code false}.
1661 *
1662 * If the task is offloaded it will take care to resume its work on the {@link EventExecutor} once there are no
1663 * more tasks to process.
1664 */
1665 private boolean runDelegatedTasks(boolean inUnwrap) {
1666 if (delegatedTaskExecutor == ImmediateExecutor.INSTANCE || inEventLoop(delegatedTaskExecutor)) {
1667 // We should run the task directly in the EventExecutor thread and not offload at all. As we are on the
1668 // EventLoop we can just run all tasks at once.
1669 for (;;) {
1670 Runnable task = engine.getDelegatedTask();
1671 if (task == null) {
1672 return true;
1673 }
1674 setState(STATE_PROCESS_TASK);
1675 if (task instanceof AsyncRunnable) {
1676 // Let's set the task to processing task before we try to execute it.
1677 boolean pending = false;
1678 try {
1679 AsyncRunnable asyncTask = (AsyncRunnable) task;
1680 AsyncTaskCompletionHandler completionHandler = new AsyncTaskCompletionHandler(inUnwrap);
1681 asyncTask.run(completionHandler);
1682 pending = completionHandler.resumeLater();
1683 if (pending) {
1684 return false;
1685 }
1686 } finally {
1687 if (!pending) {
1688 // The task has completed, lets clear the state. If it is not completed we will clear the
1689 // state once it is.
1690 clearState(STATE_PROCESS_TASK);
1691 }
1692 }
1693 } else {
1694 try {
1695 task.run();
1696 } finally {
1697 clearState(STATE_PROCESS_TASK);
1698 }
1699 }
1700 }
1701 } else {
1702 executeDelegatedTask(inUnwrap);
1703 return false;
1704 }
1705 }
1706
1707 private SslTasksRunner getTaskRunner(boolean inUnwrap) {
1708 return inUnwrap ? sslTaskRunnerForUnwrap : sslTaskRunner;
1709 }
1710
1711 private void executeDelegatedTask(boolean inUnwrap) {
1712 executeDelegatedTask(getTaskRunner(inUnwrap));
1713 }
1714
1715 private void executeDelegatedTask(SslTasksRunner task) {
1716 setState(STATE_PROCESS_TASK);
1717 try {
1718 delegatedTaskExecutor.execute(task);
1719 } catch (RejectedExecutionException e) {
1720 clearState(STATE_PROCESS_TASK);
1721 throw e;
1722 }
1723 }
1724
1725 private final class AsyncTaskCompletionHandler implements Runnable {
1726 private final boolean inUnwrap;
1727 boolean didRun;
1728 boolean resumeLater;
1729
1730 AsyncTaskCompletionHandler(boolean inUnwrap) {
1731 this.inUnwrap = inUnwrap;
1732 }
1733
1734 @Override
1735 public void run() {
1736 didRun = true;
1737 if (resumeLater) {
1738 getTaskRunner(inUnwrap).runComplete();
1739 }
1740 }
1741
1742 boolean resumeLater() {
1743 if (!didRun) {
1744 resumeLater = true;
1745 return true;
1746 }
1747 return false;
1748 }
1749 }
1750
1751 /**
1752 * {@link Runnable} that will be scheduled on the {@code delegatedTaskExecutor} and will take care
1753 * of resume work on the {@link EventExecutor} once the task was executed.
1754 */
1755 private final class SslTasksRunner implements Runnable {
1756 private final boolean inUnwrap;
1757 private final Runnable runCompleteTask = new Runnable() {
1758 @Override
1759 public void run() {
1760 runComplete();
1761 }
1762 };
1763
1764 SslTasksRunner(boolean inUnwrap) {
1765 this.inUnwrap = inUnwrap;
1766 }
1767
1768 // Handle errors which happened during task processing.
1769 private void taskError(Throwable e) {
1770 if (inUnwrap) {
1771 // As the error happened while the task was scheduled as part of unwrap(...) we also need to ensure
1772 // we fire it through the pipeline as inbound error to be consistent with what we do in decode(...).
1773 //
1774 // This will also ensure we fail the handshake future and flush all produced data.
1775 try {
1776 handleUnwrapThrowable(ctx, e);
1777 } catch (Throwable cause) {
1778 safeExceptionCaught(cause);
1779 }
1780 } else {
1781 setHandshakeFailure(ctx, e);
1782 forceFlush(ctx);
1783 }
1784 }
1785
1786 // Try to call exceptionCaught(...)
1787 private void safeExceptionCaught(Throwable cause) {
1788 try {
1789 exceptionCaught(ctx, wrapIfNeeded(cause));
1790 } catch (Throwable error) {
1791 ctx.fireExceptionCaught(error);
1792 }
1793 }
1794
1795 private Throwable wrapIfNeeded(Throwable cause) {
1796 if (!inUnwrap) {
1797 // If we are not in unwrap(...) we can just rethrow without wrapping at all.
1798 return cause;
1799 }
1800 // As the exception would have been triggered by an inbound operation we will need to wrap it in a
1801 // DecoderException to mimic what a decoder would do when decode(...) throws.
1802 return cause instanceof DecoderException ? cause : new DecoderException(cause);
1803 }
1804
1805 private void tryDecodeAgain() {
1806 try {
1807 channelRead(ctx, Unpooled.EMPTY_BUFFER);
1808 } catch (Throwable cause) {
1809 safeExceptionCaught(cause);
1810 } finally {
1811 // As we called channelRead(...) we also need to call channelReadComplete(...) which
1812 // will ensure we either call ctx.fireChannelReadComplete() or will trigger a ctx.read() if
1813 // more data is needed.
1814 channelReadComplete0(ctx);
1815 }
1816 }
1817
1818 /**
1819 * Executed after the wrapped {@code task} was executed via {@code delegatedTaskExecutor} to resume work
1820 * on the {@link EventExecutor}.
1821 */
1822 private void resumeOnEventExecutor() {
1823 assert ctx.executor().inEventLoop();
1824 clearState(STATE_PROCESS_TASK);
1825 try {
1826 HandshakeStatus status = engine.getHandshakeStatus();
1827 switch (status) {
1828 // There is another task that needs to be executed and offloaded to the delegatingTaskExecutor as
1829 // a result of this. Let's reschedule....
1830 case NEED_TASK:
1831 executeDelegatedTask(this);
1832
1833 break;
1834
1835 // The handshake finished, lets notify about the completion of it and resume processing.
1836 case FINISHED:
1837 // Not handshaking anymore, lets notify about the completion if not done yet and resume processing.
1838 case NOT_HANDSHAKING:
1839 setHandshakeSuccess(); // NOT_HANDSHAKING -> workaround for android skipping FINISHED state.
1840 try {
1841 // Lets call wrap to ensure we produce the alert if there is any pending and also to
1842 // ensure we flush any queued data..
1843 wrap(ctx, inUnwrap);
1844 } catch (Throwable e) {
1845 taskError(e);
1846 return;
1847 }
1848 if (inUnwrap) {
1849 // If we were in the unwrap call when the task was processed we should also try to unwrap
1850 // non app data first as there may not anything left in the inbound buffer to process.
1851 unwrapNonAppData(ctx);
1852 }
1853
1854 // Flush now as we may have written some data as part of the wrap call.
1855 forceFlush(ctx);
1856
1857 tryDecodeAgain();
1858 break;
1859
1860 // We need more data so lets try to unwrap first and then call decode again which will feed us
1861 // with buffered data (if there is any).
1862 case NEED_UNWRAP:
1863 try {
1864 unwrapNonAppData(ctx);
1865 } catch (SSLException e) {
1866 handleUnwrapThrowable(ctx, e);
1867 return;
1868 }
1869 tryDecodeAgain();
1870 break;
1871
1872 // To make progress we need to call SSLEngine.wrap(...) which may produce more output data
1873 // that will be written to the Channel.
1874 case NEED_WRAP:
1875 try {
1876 if (!wrapNonAppData(ctx, false) && inUnwrap) {
1877 // The handshake finished in wrapNonAppData(...), we need to try call
1878 // unwrapNonAppData(...) as we may have some alert that we should read.
1879 //
1880 // This mimics what we would do when we are calling this method while in unwrap(...).
1881 unwrapNonAppData(ctx);
1882 }
1883
1884 // Flush now as we may have written some data as part of the wrap call.
1885 forceFlush(ctx);
1886 } catch (Throwable e) {
1887 taskError(e);
1888 return;
1889 }
1890
1891 // Now try to feed in more data that we have buffered.
1892 tryDecodeAgain();
1893 break;
1894
1895 default:
1896 // Should never reach here as we handle all cases.
1897 throw new AssertionError();
1898 }
1899 } catch (Throwable cause) {
1900 safeExceptionCaught(cause);
1901 }
1902 }
1903
1904 void runComplete() {
1905 EventExecutor executor = ctx.executor();
1906 if (executor.isShuttingDown()) {
1907 // The executor is already shutting down, just return.
1908 return;
1909 }
1910 // Jump back on the EventExecutor. We do this even if we are already on the EventLoop to guard against
1911 // reentrancy issues. Failing to do so could lead to the situation of tryDecode(...) be called and so
1912 // channelRead(...) while still in the decode loop. In this case channelRead(...) might release the input
1913 // buffer if its empty which would then result in an IllegalReferenceCountException when we try to continue
1914 // decoding.
1915 //
1916 // See https://github.com/netty/netty-tcnative/issues/680
1917 executor.execute(new Runnable() {
1918 @Override
1919 public void run() {
1920 resumeOnEventExecutor();
1921 }
1922 });
1923 }
1924
1925 @Override
1926 public void run() {
1927 try {
1928 Runnable task = engine.getDelegatedTask();
1929 if (task == null) {
1930 // The task was processed in the meantime. Let's just return.
1931 return;
1932 }
1933 if (task instanceof AsyncRunnable) {
1934 AsyncRunnable asyncTask = (AsyncRunnable) task;
1935 asyncTask.run(runCompleteTask);
1936 } else {
1937 task.run();
1938 runComplete();
1939 }
1940 } catch (final Throwable cause) {
1941 handleException(cause);
1942 }
1943 }
1944
1945 private void handleException(final Throwable cause) {
1946 EventExecutor executor = ctx.executor();
1947 if (executor.inEventLoop()) {
1948 clearState(STATE_PROCESS_TASK);
1949 safeExceptionCaught(cause);
1950 } else {
1951 try {
1952 executor.execute(new Runnable() {
1953 @Override
1954 public void run() {
1955 clearState(STATE_PROCESS_TASK);
1956 safeExceptionCaught(cause);
1957 }
1958 });
1959 } catch (RejectedExecutionException ignore) {
1960 clearState(STATE_PROCESS_TASK);
1961 // the context itself will handle the rejected exception when try to schedule the operation so
1962 // ignore the RejectedExecutionException
1963 ctx.fireExceptionCaught(cause);
1964 }
1965 }
1966 }
1967 }
1968
1969 /**
1970 * Notify all the handshake futures about the successfully handshake
1971 * @return {@code true} if {@link #handshakePromise} was set successfully and a {@link SslHandshakeCompletionEvent}
1972 * was fired. {@code false} otherwise.
1973 */
1974 private boolean setHandshakeSuccess() throws SSLException {
1975 // Our control flow may invoke this method multiple times for a single FINISHED event. For example
1976 // wrapNonAppData may drain pendingUnencryptedWrites in wrap which transitions to handshake from FINISHED to
1977 // NOT_HANDSHAKING which invokes setHandshakeSuccess, and then wrapNonAppData also directly invokes this method.
1978 final SSLSession session = engine.getSession();
1979 if (resumptionController != null && !handshakePromise.isDone()) {
1980 try {
1981 if (resumptionController.validateResumeIfNeeded(engine) && logger.isDebugEnabled()) {
1982 logger.debug("{} Resumed and reauthenticated session", ctx.channel());
1983 }
1984 } catch (CertificateException e) {
1985 SSLHandshakeException exception = new SSLHandshakeException(e.getMessage());
1986 exception.initCause(e);
1987 throw exception;
1988 }
1989 }
1990 final boolean notified;
1991 if (notified = !handshakePromise.isDone() && handshakePromise.trySuccess(ctx.channel())) {
1992 if (logger.isDebugEnabled()) {
1993 logger.debug(
1994 "{} HANDSHAKEN: protocol:{} cipher suite:{}",
1995 ctx.channel(),
1996 session.getProtocol(),
1997 session.getCipherSuite());
1998 }
1999 ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS);
2000 }
2001 if (isStateSet(STATE_READ_DURING_HANDSHAKE)) {
2002 clearState(STATE_READ_DURING_HANDSHAKE);
2003 if (!ctx.channel().config().isAutoRead()) {
2004 ctx.read();
2005 }
2006 }
2007 return notified;
2008 }
2009
2010 /**
2011 * Notify all the handshake futures about the failure during the handshake.
2012 */
2013 private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) {
2014 setHandshakeFailure(ctx, cause, true, true, false);
2015 }
2016
2017 /**
2018 * Notify all the handshake futures about the failure during the handshake.
2019 */
2020 private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean closeInbound,
2021 boolean notify, boolean alwaysFlushAndClose) {
2022 try {
2023 // Release all resources such as internal buffers that SSLEngine is managing.
2024 setState(STATE_OUTBOUND_CLOSED);
2025 engine.closeOutbound();
2026
2027 if (closeInbound) {
2028 try {
2029 engine.closeInbound();
2030 } catch (SSLException e) {
2031 if (logger.isDebugEnabled()) {
2032 // only log in debug mode as it most likely harmless and latest chrome still trigger
2033 // this all the time.
2034 //
2035 // See https://github.com/netty/netty/issues/1340
2036 String msg = e.getMessage();
2037 if (msg == null || !(msg.contains("possible truncation attack") ||
2038 msg.contains("closing inbound before receiving peer's close_notify"))) {
2039 logger.debug("{} SSLEngine.closeInbound() raised an exception.", ctx.channel(), e);
2040 }
2041 }
2042 }
2043 }
2044 if (handshakePromise.tryFailure(cause) || alwaysFlushAndClose) {
2045 SslUtils.handleHandshakeFailure(ctx, cause, notify);
2046 }
2047 } finally {
2048 // Ensure we remove and fail all pending writes in all cases and so release memory quickly.
2049 releaseAndFailAll(ctx, cause);
2050 }
2051 }
2052
2053 private void setHandshakeFailureTransportFailure(ChannelHandlerContext ctx, Throwable cause) {
2054 // If TLS control frames fail to write we are in an unknown state and may become out of
2055 // sync with our peer. We give up and close the channel. This will also take care of
2056 // cleaning up any outstanding state (e.g. handshake promise, queued unencrypted data).
2057 try {
2058 SSLException transportFailure = new SSLException("failure when writing TLS control frames", cause);
2059 releaseAndFailAll(ctx, transportFailure);
2060 if (handshakePromise.tryFailure(transportFailure)) {
2061 ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(transportFailure));
2062 }
2063 } finally {
2064 ctx.close();
2065 }
2066 }
2067
2068 private void releaseAndFailAll(ChannelHandlerContext ctx, Throwable cause) {
2069 if (resumptionController != null &&
2070 (!engine.getSession().isValid() || cause instanceof SSLHandshakeException)) {
2071 resumptionController.remove(engine());
2072 }
2073 if (pendingUnencryptedWrites != null) {
2074 pendingUnencryptedWrites.releaseAndFailAll(ctx, cause);
2075 }
2076 }
2077
2078 private void notifyClosePromise(Throwable cause) {
2079 if (cause == null) {
2080 if (sslClosePromise.trySuccess(ctx.channel())) {
2081 ctx.fireUserEventTriggered(SslCloseCompletionEvent.SUCCESS);
2082 }
2083 } else {
2084 if (sslClosePromise.tryFailure(cause)) {
2085 ctx.fireUserEventTriggered(new SslCloseCompletionEvent(cause));
2086 }
2087 }
2088 }
2089
2090 private void closeOutboundAndChannel(
2091 final ChannelHandlerContext ctx, final ChannelPromise promise, boolean disconnect) throws Exception {
2092 setState(STATE_OUTBOUND_CLOSED);
2093 engine.closeOutbound();
2094
2095 if (!ctx.channel().isActive()) {
2096 if (disconnect) {
2097 ctx.disconnect(promise);
2098 } else {
2099 ctx.close(promise);
2100 }
2101 return;
2102 }
2103
2104 ChannelPromise closeNotifyPromise = ctx.newPromise();
2105 try {
2106 flush(ctx, closeNotifyPromise);
2107 } finally {
2108 if (!isStateSet(STATE_CLOSE_NOTIFY)) {
2109 setState(STATE_CLOSE_NOTIFY);
2110 // It's important that we do not pass the original ChannelPromise to safeClose(...) as when flush(....)
2111 // throws an Exception it will be propagated to the AbstractChannelHandlerContext which will try
2112 // to fail the promise because of this. This will then fail as it was already completed by
2113 // safeClose(...). We create a new ChannelPromise and try to notify the original ChannelPromise
2114 // once it is complete. If we fail to do so we just ignore it as in this case it was failed already
2115 // because of a propagated Exception.
2116 //
2117 // See https://github.com/netty/netty/issues/5931
2118 safeClose(ctx, closeNotifyPromise, PromiseNotifier.cascade(false, ctx.newPromise(), promise));
2119 } else {
2120 /// We already handling the close_notify so just attach the promise to the sslClosePromise.
2121 sslClosePromise.addListener(new FutureListener<Channel>() {
2122 @Override
2123 public void operationComplete(Future<Channel> future) {
2124 promise.setSuccess();
2125 }
2126 });
2127 }
2128 }
2129 }
2130
2131 private void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
2132 if (pendingUnencryptedWrites != null) {
2133 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, promise);
2134 } else {
2135 promise.setFailure(newPendingWritesNullException());
2136 }
2137 flush(ctx);
2138 }
2139
2140 @Override
2141 public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
2142 this.ctx = ctx;
2143 Channel channel = ctx.channel();
2144 pendingUnencryptedWrites = new SslHandlerCoalescingBufferQueue(channel, 16, engineType.wantsDirectBuffer) {
2145 @Override
2146 protected int wrapDataSize() {
2147 return SslHandler.this.wrapDataSize;
2148 }
2149 };
2150
2151 setOpensslEngineSocketFd(channel);
2152 boolean fastOpen = Boolean.TRUE.equals(channel.config().getOption(ChannelOption.TCP_FASTOPEN_CONNECT));
2153 boolean active = channel.isActive();
2154 if (active || fastOpen) {
2155 // Explicitly flush the handshake only if the channel is already active.
2156 // With TCP Fast Open, we write to the outbound buffer before the TCP connect is established.
2157 // The buffer will then be flushed as part of establishing the connection, saving us a round-trip.
2158 startHandshakeProcessing(active);
2159 // If we weren't able to include client_hello in the TCP SYN (e.g. no token, disabled at the OS) we have to
2160 // flush pending data in the outbound buffer later in channelActive().
2161 final ChannelOutboundBuffer outboundBuffer;
2162 if (fastOpen && ((outboundBuffer = channel.unsafe().outboundBuffer()) == null ||
2163 outboundBuffer.totalPendingWriteBytes() > 0)) {
2164 setState(STATE_NEEDS_FLUSH);
2165 }
2166 }
2167 }
2168
2169 private void startHandshakeProcessing(boolean flushAtEnd) {
2170 if (!isStateSet(STATE_HANDSHAKE_STARTED)) {
2171 setState(STATE_HANDSHAKE_STARTED);
2172 if (engine.getUseClientMode()) {
2173 // Begin the initial handshake.
2174 // channelActive() event has been fired already, which means this.channelActive() will
2175 // not be invoked. We have to initialize here instead.
2176 handshake(flushAtEnd);
2177 }
2178 applyHandshakeTimeout();
2179 } else if (isStateSet(STATE_NEEDS_FLUSH)) {
2180 forceFlush(ctx);
2181 }
2182 }
2183
2184 /**
2185 * Performs TLS renegotiation.
2186 */
2187 public Future<Channel> renegotiate() {
2188 ChannelHandlerContext ctx = this.ctx;
2189 if (ctx == null) {
2190 throw new IllegalStateException();
2191 }
2192
2193 return renegotiate(ctx.executor().<Channel>newPromise());
2194 }
2195
2196 /**
2197 * Performs TLS renegotiation.
2198 */
2199 public Future<Channel> renegotiate(final Promise<Channel> promise) {
2200 ObjectUtil.checkNotNull(promise, "promise");
2201
2202 ChannelHandlerContext ctx = this.ctx;
2203 if (ctx == null) {
2204 throw new IllegalStateException();
2205 }
2206
2207 EventExecutor executor = ctx.executor();
2208 if (!executor.inEventLoop()) {
2209 executor.execute(new Runnable() {
2210 @Override
2211 public void run() {
2212 renegotiateOnEventLoop(promise);
2213 }
2214 });
2215 return promise;
2216 }
2217
2218 renegotiateOnEventLoop(promise);
2219 return promise;
2220 }
2221
2222 private void renegotiateOnEventLoop(final Promise<Channel> newHandshakePromise) {
2223 final Promise<Channel> oldHandshakePromise = handshakePromise;
2224 if (!oldHandshakePromise.isDone()) {
2225 // There's no need to handshake because handshake is in progress already.
2226 // Merge the new promise into the old one.
2227 PromiseNotifier.cascade(oldHandshakePromise, newHandshakePromise);
2228 } else {
2229 handshakePromise = newHandshakePromise;
2230 handshake(true);
2231 applyHandshakeTimeout();
2232 }
2233 }
2234
2235 /**
2236 * Performs TLS (re)negotiation.
2237 * @param flushAtEnd Set to {@code true} if the outbound buffer should be flushed (written to the network) at the
2238 * end. Set to {@code false} if the handshake will be flushed later, e.g. as part of TCP Fast Open
2239 * connect.
2240 */
2241 private void handshake(boolean flushAtEnd) {
2242 if (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
2243 // Not all SSLEngine implementations support calling beginHandshake multiple times while a handshake
2244 // is in progress. See https://github.com/netty/netty/issues/4718.
2245 return;
2246 }
2247 if (handshakePromise.isDone()) {
2248 // If the handshake is done already lets just return directly as there is no need to trigger it again.
2249 // This can happen if the handshake(...) was triggered before we called channelActive(...) by a
2250 // flush() that was triggered by a ChannelFutureListener that was added to the ChannelFuture returned
2251 // from the connect(...) method. In this case we will see the flush() happen before we had a chance to
2252 // call fireChannelActive() on the pipeline.
2253 return;
2254 }
2255
2256 // Begin handshake.
2257 final ChannelHandlerContext ctx = this.ctx;
2258 try {
2259 engine.beginHandshake();
2260 wrapNonAppData(ctx, false);
2261 } catch (Throwable e) {
2262 setHandshakeFailure(ctx, e);
2263 } finally {
2264 if (flushAtEnd) {
2265 forceFlush(ctx);
2266 }
2267 }
2268 }
2269
2270 private void applyHandshakeTimeout() {
2271 final Promise<Channel> localHandshakePromise = this.handshakePromise;
2272
2273 // Set timeout if necessary.
2274 final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
2275 if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
2276 return;
2277 }
2278
2279 final Future<?> timeoutFuture = ctx.executor().schedule(new Runnable() {
2280 @Override
2281 public void run() {
2282 if (localHandshakePromise.isDone()) {
2283 return;
2284 }
2285 SSLException exception =
2286 new SslHandshakeTimeoutException("handshake timed out after " + handshakeTimeoutMillis + "ms");
2287 try {
2288 if (localHandshakePromise.tryFailure(exception)) {
2289 SslUtils.handleHandshakeFailure(ctx, exception, true);
2290 }
2291 } finally {
2292 releaseAndFailAll(ctx, exception);
2293 }
2294 }
2295 }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
2296
2297 // Cancel the handshake timeout when handshake is finished.
2298 localHandshakePromise.addListener(new FutureListener<Channel>() {
2299 @Override
2300 public void operationComplete(Future<Channel> f) throws Exception {
2301 timeoutFuture.cancel(false);
2302 }
2303 });
2304 }
2305
2306 private void forceFlush(ChannelHandlerContext ctx) {
2307 clearState(STATE_NEEDS_FLUSH);
2308 ctx.flush();
2309 }
2310
2311 private void setOpensslEngineSocketFd(Channel c) {
2312 if (c instanceof UnixChannel && engine instanceof ReferenceCountedOpenSslEngine) {
2313 ((ReferenceCountedOpenSslEngine) engine).bioSetFd(((UnixChannel) c).fd().intValue());
2314 }
2315 }
2316
2317 /**
2318 * Issues an initial TLS handshake once connected when used in client-mode
2319 */
2320 @Override
2321 public void channelActive(final ChannelHandlerContext ctx) throws Exception {
2322 setOpensslEngineSocketFd(ctx.channel());
2323 if (!startTls) {
2324 startHandshakeProcessing(true);
2325 }
2326 ctx.fireChannelActive();
2327 }
2328
2329 private void safeClose(
2330 final ChannelHandlerContext ctx, final ChannelFuture flushFuture,
2331 final ChannelPromise promise) {
2332 if (!ctx.channel().isActive()) {
2333 ctx.close(promise);
2334 return;
2335 }
2336
2337 final Future<?> timeoutFuture;
2338 if (!flushFuture.isDone()) {
2339 long closeNotifyTimeout = closeNotifyFlushTimeoutMillis;
2340 if (closeNotifyTimeout > 0) {
2341 // Force-close the connection if close_notify is not fully sent in time.
2342 timeoutFuture = ctx.executor().schedule(new Runnable() {
2343 @Override
2344 public void run() {
2345 // May be done in the meantime as cancel(...) is only best effort.
2346 if (!flushFuture.isDone()) {
2347 logger.warn("{} Last write attempt timed out; force-closing the connection.",
2348 ctx.channel());
2349 addCloseListener(ctx.close(ctx.newPromise()), promise);
2350 }
2351 }
2352 }, closeNotifyTimeout, TimeUnit.MILLISECONDS);
2353 } else {
2354 timeoutFuture = null;
2355 }
2356 } else {
2357 timeoutFuture = null;
2358 }
2359
2360 // Close the connection if close_notify is sent in time.
2361 flushFuture.addListener(new ChannelFutureListener() {
2362 @Override
2363 public void operationComplete(ChannelFuture f) {
2364 if (timeoutFuture != null) {
2365 timeoutFuture.cancel(false);
2366 }
2367 final long closeNotifyReadTimeout = closeNotifyReadTimeoutMillis;
2368 if (closeNotifyReadTimeout <= 0) {
2369 // Trigger the close in all cases to make sure the promise is notified
2370 // See https://github.com/netty/netty/issues/2358
2371 addCloseListener(ctx.close(ctx.newPromise()), promise);
2372 } else {
2373 final Future<?> closeNotifyReadTimeoutFuture;
2374
2375 if (!sslClosePromise.isDone()) {
2376 closeNotifyReadTimeoutFuture = ctx.executor().schedule(new Runnable() {
2377 @Override
2378 public void run() {
2379 if (!sslClosePromise.isDone()) {
2380 logger.debug(
2381 "{} did not receive close_notify in {}ms; force-closing the connection.",
2382 ctx.channel(), closeNotifyReadTimeout);
2383
2384 // Do the close now...
2385 addCloseListener(ctx.close(ctx.newPromise()), promise);
2386 }
2387 }
2388 }, closeNotifyReadTimeout, TimeUnit.MILLISECONDS);
2389 } else {
2390 closeNotifyReadTimeoutFuture = null;
2391 }
2392
2393 // Do the close once the we received the close_notify.
2394 sslClosePromise.addListener(new FutureListener<Channel>() {
2395 @Override
2396 public void operationComplete(Future<Channel> future) throws Exception {
2397 if (closeNotifyReadTimeoutFuture != null) {
2398 closeNotifyReadTimeoutFuture.cancel(false);
2399 }
2400 addCloseListener(ctx.close(ctx.newPromise()), promise);
2401 }
2402 });
2403 }
2404 }
2405 });
2406 }
2407
2408 private static void addCloseListener(ChannelFuture future, ChannelPromise promise) {
2409 // We notify the promise in the ChannelPromiseNotifier as there is a "race" where the close(...) call
2410 // by the timeoutFuture and the close call in the flushFuture listener will be called. Because of
2411 // this we need to use trySuccess() and tryFailure(...) as otherwise we can cause an
2412 // IllegalStateException.
2413 // Also we not want to log if the notification happens as this is expected in some cases.
2414 // See https://github.com/netty/netty/issues/5598
2415 PromiseNotifier.cascade(false, future, promise);
2416 }
2417
2418 /**
2419 * Always prefer a direct buffer when it's pooled, so that we reduce the number of memory copies
2420 * in {@link OpenSslEngine}.
2421 */
2422 private ByteBuf allocate(ChannelHandlerContext ctx, int capacity) {
2423 ByteBufAllocator alloc = ctx.alloc();
2424 if (engineType.wantsDirectBuffer) {
2425 return alloc.directBuffer(capacity);
2426 } else {
2427 return alloc.buffer(capacity);
2428 }
2429 }
2430
2431 /**
2432 * Allocates an outbound network buffer for {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} which can encrypt
2433 * the specified amount of pending bytes.
2434 */
2435 private ByteBuf allocateOutNetBuf(ChannelHandlerContext ctx, int pendingBytes, int numComponents) {
2436 return engineType.allocateWrapBuffer(this, ctx.alloc(), pendingBytes, numComponents);
2437 }
2438
2439 private boolean isStateSet(int bit) {
2440 return (state & bit) == bit;
2441 }
2442
2443 private void setState(int bit) {
2444 state |= bit;
2445 }
2446
2447 private void clearState(int bit) {
2448 state &= ~bit;
2449 }
2450
2451 private final class LazyChannelPromise extends DefaultPromise<Channel> {
2452
2453 @Override
2454 protected EventExecutor executor() {
2455 if (ctx == null) {
2456 throw new IllegalStateException();
2457 }
2458 return ctx.executor();
2459 }
2460
2461 @Override
2462 protected void checkDeadLock() {
2463 if (ctx == null) {
2464 // If ctx is null the handlerAdded(...) callback was not called, in this case the checkDeadLock()
2465 // method was called from another Thread then the one that is used by ctx.executor(). We need to
2466 // guard against this as a user can see a race if handshakeFuture().sync() is called but the
2467 // handlerAdded(..) method was not yet as it is called from the EventExecutor of the
2468 // ChannelHandlerContext. If we not guard against this super.checkDeadLock() would cause an
2469 // IllegalStateException when trying to call executor().
2470 return;
2471 }
2472 super.checkDeadLock();
2473 }
2474 }
2475 }