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