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