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