1 /*
2 * Copyright 2012 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.handler.ssl;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.buffer.ByteBufUtil;
21 import io.netty.buffer.CompositeByteBuf;
22 import io.netty.buffer.Unpooled;
23 import io.netty.channel.Channel;
24 import io.netty.channel.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.ChannelOutboundHandler;
31 import io.netty.channel.ChannelPipeline;
32 import io.netty.channel.ChannelPromise;
33 import io.netty.channel.ChannelPromiseNotifier;
34 import io.netty.channel.PendingWriteQueue;
35 import io.netty.handler.codec.ByteToMessageDecoder;
36 import io.netty.handler.codec.UnsupportedMessageTypeException;
37 import io.netty.util.ReferenceCountUtil;
38 import io.netty.util.ReferenceCounted;
39 import io.netty.util.concurrent.DefaultPromise;
40 import io.netty.util.concurrent.EventExecutor;
41 import io.netty.util.concurrent.Future;
42 import io.netty.util.concurrent.FutureListener;
43 import io.netty.util.concurrent.ImmediateExecutor;
44 import io.netty.util.concurrent.Promise;
45 import io.netty.util.internal.PlatformDependent;
46 import io.netty.util.internal.ThrowableUtil;
47 import io.netty.util.internal.logging.InternalLogger;
48 import io.netty.util.internal.logging.InternalLoggerFactory;
49
50 import java.io.IOException;
51 import java.net.SocketAddress;
52 import java.nio.ByteBuffer;
53 import java.nio.channels.ClosedChannelException;
54 import java.nio.channels.DatagramChannel;
55 import java.nio.channels.SocketChannel;
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.Executor;
60 import java.util.concurrent.ScheduledFuture;
61 import java.util.concurrent.TimeUnit;
62 import java.util.regex.Pattern;
63 import javax.net.ssl.SSLEngine;
64 import javax.net.ssl.SSLEngineResult;
65 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
66 import javax.net.ssl.SSLEngineResult.Status;
67 import javax.net.ssl.SSLException;
68 import javax.net.ssl.SSLSession;
69
70 import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
71
72 /**
73 * Adds <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">SSL
74 * · TLS</a> and StartTLS support to a {@link Channel}. Please refer
75 * to the <strong>"SecureChat"</strong> example in the distribution or the web
76 * site for the detailed usage.
77 *
78 * <h3>Beginning the handshake</h3>
79 * <p>
80 * Beside using the handshake {@link ChannelFuture} to get notified about the completion of the handshake it's
81 * also possible to detect it by implement the
82 * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)}
83 * method and check for a {@link SslHandshakeCompletionEvent}.
84 *
85 * <h3>Handshake</h3>
86 * <p>
87 * The handshake will be automatically issued for you once the {@link Channel} is active and
88 * {@link SSLEngine#getUseClientMode()} returns {@code true}.
89 * So no need to bother with it by your self.
90 *
91 * <h3>Closing the session</h3>
92 * <p>
93 * To close the SSL session, the {@link #close()} method should be
94 * called to send the {@code close_notify} message to the remote peer. One
95 * exception is when you close the {@link Channel} - {@link SslHandler}
96 * intercepts the close request and send the {@code close_notify} message
97 * before the channel closure automatically. Once the SSL session is closed,
98 * it is not reusable, and consequently you should create a new
99 * {@link SslHandler} with a new {@link SSLEngine} as explained in the
100 * following section.
101 *
102 * <h3>Restarting the session</h3>
103 * <p>
104 * To restart the SSL session, you must remove the existing closed
105 * {@link SslHandler} from the {@link ChannelPipeline}, insert a new
106 * {@link SslHandler} with a new {@link SSLEngine} into the pipeline,
107 * and start the handshake process as described in the first section.
108 *
109 * <h3>Implementing StartTLS</h3>
110 * <p>
111 * <a href="http://en.wikipedia.org/wiki/STARTTLS">StartTLS</a> is the
112 * communication pattern that secures the wire in the middle of the plaintext
113 * connection. Please note that it is different from SSL · TLS, that
114 * secures the wire from the beginning of the connection. Typically, StartTLS
115 * is composed of three steps:
116 * <ol>
117 * <li>Client sends a StartTLS request to server.</li>
118 * <li>Server sends a StartTLS response to client.</li>
119 * <li>Client begins SSL handshake.</li>
120 * </ol>
121 * If you implement a server, you need to:
122 * <ol>
123 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
124 * to {@code true},</li>
125 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
126 * <li>write a StartTLS response.</li>
127 * </ol>
128 * Please note that you must insert {@link SslHandler} <em>before</em> sending
129 * the StartTLS response. Otherwise the client can send begin SSL handshake
130 * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing
131 * data corruption.
132 * <p>
133 * The client-side implementation is much simpler.
134 * <ol>
135 * <li>Write a StartTLS request,</li>
136 * <li>wait for the StartTLS response,</li>
137 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
138 * to {@code false},</li>
139 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
140 * <li>Initiate SSL handshake.</li>
141 * </ol>
142 *
143 * <h3>Known issues</h3>
144 * <p>
145 * Because of a known issue with the current implementation of the SslEngine that comes
146 * with Java it may be possible that you see blocked IO-Threads while a full GC is done.
147 * <p>
148 * So if you are affected you can workaround this problem by adjust the cache settings
149 * like shown below:
150 *
151 * <pre>
152 * SslContext context = ...;
153 * context.getServerSessionContext().setSessionCacheSize(someSaneSize);
154 * context.getServerSessionContext().setSessionTime(someSameTimeout);
155 * </pre>
156 * <p>
157 * What values to use here depends on the nature of your application and should be set
158 * based on monitoring and debugging of it.
159 * For more details see
160 * <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
161 */
162 public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundHandler {
163
164 private static final InternalLogger logger =
165 InternalLoggerFactory.getInstance(SslHandler.class);
166
167 private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
168 "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
169 private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
170 "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
171
172 /**
173 * Used in {@link #unwrapNonAppData(ChannelHandlerContext)} as input for
174 * {@link #unwrap(ChannelHandlerContext, ByteBuf, int, int)}. Using this static instance reduce object
175 * creation as {@link Unpooled#EMPTY_BUFFER#nioBuffer()} creates a new {@link ByteBuffer} everytime.
176 */
177 private static final SSLException SSLENGINE_CLOSED = ThrowableUtil.unknownStackTrace(
178 new SSLException("SSLEngine closed already"), SslHandler.class, "wrap(...)");
179 private static final SSLException HANDSHAKE_TIMED_OUT = ThrowableUtil.unknownStackTrace(
180 new SSLException("handshake timed out"), SslHandler.class, "handshake(...)");
181 private static final ClosedChannelException CHANNEL_CLOSED = ThrowableUtil.unknownStackTrace(
182 new ClosedChannelException(), SslHandler.class, "channelInactive(...)");
183
184 private enum SslEngineType {
185 TCNATIVE(true, COMPOSITE_CUMULATOR) {
186 @Override
187 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
188 throws SSLException {
189 int nioBufferCount = in.nioBufferCount();
190 int writerIndex = out.writerIndex();
191 final SSLEngineResult result;
192 if (nioBufferCount > 1) {
193 /*
194 * If {@link OpenSslEngine} is in use,
195 * we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method
196 * that accepts multiple {@link ByteBuffer}s without additional memory copies.
197 */
198 ReferenceCountedOpenSslEngine opensslEngine = (ReferenceCountedOpenSslEngine) handler.engine;
199 try {
200 handler.singleBuffer[0] = toByteBuffer(out, writerIndex,
201 out.writableBytes());
202 result = opensslEngine.unwrap(in.nioBuffers(readerIndex, len), handler.singleBuffer);
203 } finally {
204 handler.singleBuffer[0] = null;
205 }
206 } else {
207 result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
208 toByteBuffer(out, writerIndex, out.writableBytes()));
209 }
210 out.writerIndex(writerIndex + result.bytesProduced());
211 return result;
212 }
213
214 @Override
215 int getPacketBufferSize(SslHandler handler) {
216 return ((ReferenceCountedOpenSslEngine) handler.engine).maxEncryptedPacketLength0();
217 }
218
219 @Override
220 int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
221 return ((ReferenceCountedOpenSslEngine) handler.engine).calculateMaxLengthForWrap(pendingBytes,
222 numComponents);
223 }
224
225 @Override
226 int calculatePendingData(SslHandler handler, int guess) {
227 int sslPending = ((ReferenceCountedOpenSslEngine) handler.engine).sslPending();
228 return sslPending > 0 ? sslPending : guess;
229 }
230
231 @Override
232 boolean jdkCompatibilityMode(SSLEngine engine) {
233 return ((ReferenceCountedOpenSslEngine) engine).jdkCompatibilityMode;
234 }
235 },
236 CONSCRYPT(true, COMPOSITE_CUMULATOR) {
237 @Override
238 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
239 throws SSLException {
240 int nioBufferCount = in.nioBufferCount();
241 int writerIndex = out.writerIndex();
242 final SSLEngineResult result;
243 if (nioBufferCount > 1) {
244 /*
245 * Use a special unwrap method without additional memory copies.
246 */
247 try {
248 handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes());
249 result = ((ConscryptAlpnSslEngine) handler.engine).unwrap(
250 in.nioBuffers(readerIndex, len),
251 handler.singleBuffer);
252 } finally {
253 handler.singleBuffer[0] = null;
254 }
255 } else {
256 result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
257 toByteBuffer(out, writerIndex, out.writableBytes()));
258 }
259 out.writerIndex(writerIndex + result.bytesProduced());
260 return result;
261 }
262
263 @Override
264 int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
265 return ((ConscryptAlpnSslEngine) handler.engine).calculateOutNetBufSize(pendingBytes, numComponents);
266 }
267
268 @Override
269 int calculatePendingData(SslHandler handler, int guess) {
270 return guess;
271 }
272
273 @Override
274 boolean jdkCompatibilityMode(SSLEngine engine) {
275 return true;
276 }
277 },
278 JDK(false, MERGE_CUMULATOR) {
279 @Override
280 SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
281 throws SSLException {
282 int writerIndex = out.writerIndex();
283 final SSLEngineResult result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len),
284 toByteBuffer(out, writerIndex, out.writableBytes()));
285 out.writerIndex(writerIndex + result.bytesProduced());
286 return result;
287 }
288
289 @Override
290 int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
291 return handler.engine.getSession().getPacketBufferSize();
292 }
293
294 @Override
295 int calculatePendingData(SslHandler handler, int guess) {
296 return guess;
297 }
298
299 @Override
300 boolean jdkCompatibilityMode(SSLEngine engine) {
301 return true;
302 }
303 };
304
305 static SslEngineType forEngine(SSLEngine engine) {
306 return engine instanceof ReferenceCountedOpenSslEngine ? TCNATIVE :
307 engine instanceof ConscryptAlpnSslEngine ? CONSCRYPT : JDK;
308 }
309
310 SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) {
311 this.wantsDirectBuffer = wantsDirectBuffer;
312 this.cumulator = cumulator;
313 }
314
315 int getPacketBufferSize(SslHandler handler) {
316 return handler.engine.getSession().getPacketBufferSize();
317 }
318
319 abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
320 throws SSLException;
321
322 abstract int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents);
323
324 abstract int calculatePendingData(SslHandler handler, int guess);
325
326 abstract boolean jdkCompatibilityMode(SSLEngine engine);
327
328 // BEGIN Platform-dependent flags
329
330 /**
331 * {@code true} if and only if {@link SSLEngine} expects a direct buffer.
332 */
333 final boolean wantsDirectBuffer;
334
335 // END Platform-dependent flags
336
337 /**
338 * When using JDK {@link SSLEngine}, we use {@link #MERGE_CUMULATOR} because it works only with
339 * one {@link ByteBuffer}.
340 *
341 * When using {@link OpenSslEngine}, we can use {@link #COMPOSITE_CUMULATOR} because it has
342 * {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} which works with multiple {@link ByteBuffer}s
343 * and which does not need to do extra memory copies.
344 */
345 final Cumulator cumulator;
346 }
347
348 private volatile ChannelHandlerContext ctx;
349 private final SSLEngine engine;
350 private final SslEngineType engineType;
351 private final Executor delegatedTaskExecutor;
352 private final boolean jdkCompatibilityMode;
353
354 /**
355 * Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])}
356 * should be called with a {@link ByteBuf} that is only backed by one {@link ByteBuffer} to reduce the object
357 * creation.
358 */
359 private final ByteBuffer[] singleBuffer = new ByteBuffer[1];
360
361 private final boolean startTls;
362 private boolean sentFirstMessage;
363 private boolean flushedBeforeHandshake;
364 private boolean readDuringHandshake;
365 private boolean handshakeStarted;
366 private PendingWriteQueue pendingUnencryptedWrites;
367
368 private Promise<Channel> handshakePromise = new LazyChannelPromise();
369 private final LazyChannelPromise sslClosePromise = new LazyChannelPromise();
370
371 /**
372 * Set by wrap*() methods when something is produced.
373 * {@link #channelReadComplete(ChannelHandlerContext)} will check this flag, clear it, and call ctx.flush().
374 */
375 private boolean needsFlush;
376
377 private boolean outboundClosed;
378
379 private int packetLength;
380
381 /**
382 * This flag is used to determine if we need to call {@link ChannelHandlerContext#read()} to consume more data
383 * when {@link ChannelConfig#isAutoRead()} is {@code false}.
384 */
385 private boolean firedChannelRead;
386
387 private volatile long handshakeTimeoutMillis = 10000;
388 private volatile long closeNotifyFlushTimeoutMillis = 3000;
389 private volatile long closeNotifyReadTimeoutMillis;
390
391 /**
392 * Creates a new instance.
393 *
394 * @param engine the {@link SSLEngine} this handler will use
395 */
396 public SslHandler(SSLEngine engine) {
397 this(engine, false);
398 }
399
400 /**
401 * Creates a new instance.
402 *
403 * @param engine the {@link SSLEngine} this handler will use
404 * @param startTls {@code true} if the first write request shouldn't be
405 * encrypted by the {@link SSLEngine}
406 */
407 @SuppressWarnings("deprecation")
408 public SslHandler(SSLEngine engine, boolean startTls) {
409 this(engine, startTls, ImmediateExecutor.INSTANCE);
410 }
411
412 /**
413 * @deprecated Use {@link #SslHandler(SSLEngine)} instead.
414 */
415 @Deprecated
416 public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) {
417 this(engine, false, delegatedTaskExecutor);
418 }
419
420 /**
421 * @deprecated Use {@link #SslHandler(SSLEngine, boolean)} instead.
422 */
423 @Deprecated
424 public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) {
425 if (engine == null) {
426 throw new NullPointerException("engine");
427 }
428 if (delegatedTaskExecutor == null) {
429 throw new NullPointerException("delegatedTaskExecutor");
430 }
431 this.engine = engine;
432 engineType = SslEngineType.forEngine(engine);
433 this.delegatedTaskExecutor = delegatedTaskExecutor;
434 this.startTls = startTls;
435 this.jdkCompatibilityMode = engineType.jdkCompatibilityMode(engine);
436 setCumulator(engineType.cumulator);
437 }
438
439 public long getHandshakeTimeoutMillis() {
440 return handshakeTimeoutMillis;
441 }
442
443 public void setHandshakeTimeout(long handshakeTimeout, TimeUnit unit) {
444 if (unit == null) {
445 throw new NullPointerException("unit");
446 }
447
448 setHandshakeTimeoutMillis(unit.toMillis(handshakeTimeout));
449 }
450
451 public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) {
452 if (handshakeTimeoutMillis < 0) {
453 throw new IllegalArgumentException(
454 "handshakeTimeoutMillis: " + handshakeTimeoutMillis + " (expected: >= 0)");
455 }
456 this.handshakeTimeoutMillis = handshakeTimeoutMillis;
457 }
458
459 /**
460 * @deprecated use {@link #getCloseNotifyFlushTimeoutMillis()}
461 */
462 @Deprecated
463 public long getCloseNotifyTimeoutMillis() {
464 return getCloseNotifyFlushTimeoutMillis();
465 }
466
467 /**
468 * @deprecated use {@link #setCloseNotifyFlushTimeout(long, TimeUnit)}
469 */
470 @Deprecated
471 public void setCloseNotifyTimeout(long closeNotifyTimeout, TimeUnit unit) {
472 setCloseNotifyFlushTimeout(closeNotifyTimeout, unit);
473 }
474
475 /**
476 * @deprecated use {@link #setCloseNotifyFlushTimeoutMillis(long)}
477 */
478 @Deprecated
479 public void setCloseNotifyTimeoutMillis(long closeNotifyFlushTimeoutMillis) {
480 setCloseNotifyFlushTimeoutMillis(closeNotifyFlushTimeoutMillis);
481 }
482
483 /**
484 * Gets the timeout for flushing the close_notify that was triggered by closing the
485 * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed
486 * forcibly.
487 */
488 public final long getCloseNotifyFlushTimeoutMillis() {
489 return closeNotifyFlushTimeoutMillis;
490 }
491
492 /**
493 * Sets the timeout for flushing the close_notify that was triggered by closing the
494 * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed
495 * forcibly.
496 */
497 public final void setCloseNotifyFlushTimeout(long closeNotifyFlushTimeout, TimeUnit unit) {
498 setCloseNotifyFlushTimeoutMillis(unit.toMillis(closeNotifyFlushTimeout));
499 }
500
501 /**
502 * See {@link #setCloseNotifyFlushTimeout(long, TimeUnit)}.
503 */
504 public final void setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis) {
505 if (closeNotifyFlushTimeoutMillis < 0) {
506 throw new IllegalArgumentException(
507 "closeNotifyFlushTimeoutMillis: " + closeNotifyFlushTimeoutMillis + " (expected: >= 0)");
508 }
509 this.closeNotifyFlushTimeoutMillis = closeNotifyFlushTimeoutMillis;
510 }
511
512 /**
513 * Gets the timeout (in ms) for receiving the response for the close_notify that was triggered by closing the
514 * {@link Channel}. This timeout starts after the close_notify message was successfully written to the
515 * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response.
516 */
517 public final long getCloseNotifyReadTimeoutMillis() {
518 return closeNotifyReadTimeoutMillis;
519 }
520
521 /**
522 * Sets the timeout for receiving the response for the close_notify that was triggered by closing the
523 * {@link Channel}. This timeout starts after the close_notify message was successfully written to the
524 * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response.
525 */
526 public final void setCloseNotifyReadTimeout(long closeNotifyReadTimeout, TimeUnit unit) {
527 setCloseNotifyReadTimeoutMillis(unit.toMillis(closeNotifyReadTimeout));
528 }
529
530 /**
531 * See {@link #setCloseNotifyReadTimeout(long, TimeUnit)}.
532 */
533 public final void setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis) {
534 if (closeNotifyReadTimeoutMillis < 0) {
535 throw new IllegalArgumentException(
536 "closeNotifyReadTimeoutMillis: " + closeNotifyReadTimeoutMillis + " (expected: >= 0)");
537 }
538 this.closeNotifyReadTimeoutMillis = closeNotifyReadTimeoutMillis;
539 }
540
541 /**
542 * Returns the {@link SSLEngine} which is used by this handler.
543 */
544 public SSLEngine engine() {
545 return engine;
546 }
547
548 /**
549 * Returns the name of the current application-level protocol.
550 *
551 * @return the protocol name or {@code null} if application-level protocol has not been negotiated
552 */
553 public String applicationProtocol() {
554 SSLEngine engine = engine();
555 if (!(engine instanceof ApplicationProtocolAccessor)) {
556 return null;
557 }
558
559 return ((ApplicationProtocolAccessor) engine).getNegotiatedApplicationProtocol();
560 }
561
562 /**
563 * Returns a {@link Future} that will get notified once the current TLS handshake completes.
564 *
565 * @return the {@link Future} for the initial TLS handshake if {@link #renegotiate()} was not invoked.
566 * The {@link Future} for the most recent {@linkplain #renegotiate() TLS renegotiation} otherwise.
567 */
568 public Future<Channel> handshakeFuture() {
569 return handshakePromise;
570 }
571
572 /**
573 * Sends an SSL {@code close_notify} message to the specified channel and
574 * destroys the underlying {@link SSLEngine}.
575 *
576 * @deprecated use {@link Channel#close()} or {@link ChannelHandlerContext#close()}
577 */
578 @Deprecated
579 public ChannelFuture close() {
580 return close(ctx.newPromise());
581 }
582
583 /**
584 * See {@link #close()}
585 *
586 * @deprecated use {@link Channel#close()} or {@link ChannelHandlerContext#close()}
587 */
588 @Deprecated
589 public ChannelFuture close(final ChannelPromise promise) {
590 final ChannelHandlerContext ctx = this.ctx;
591 ctx.executor().execute(new Runnable() {
592 @Override
593 public void run() {
594 outboundClosed = true;
595 engine.closeOutbound();
596 try {
597 flush(ctx, promise);
598 } catch (Exception e) {
599 if (!promise.tryFailure(e)) {
600 logger.warn("{} flush() raised a masked exception.", ctx.channel(), e);
601 }
602 }
603 }
604 });
605
606 return promise;
607 }
608
609 /**
610 * Return the {@link Future} that will get notified if the inbound of the {@link SSLEngine} is closed.
611 *
612 * This method will return the same {@link Future} all the time.
613 *
614 * @see SSLEngine
615 */
616 public Future<Channel> sslCloseFuture() {
617 return sslClosePromise;
618 }
619
620 @Override
621 public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
622 if (!pendingUnencryptedWrites.isEmpty()) {
623 // Check if queue is not empty first because create a new ChannelException is expensive
624 pendingUnencryptedWrites.removeAndFailAll(new ChannelException("Pending write on removal of SslHandler"));
625 }
626 pendingUnencryptedWrites = null;
627 if (engine instanceof ReferenceCounted) {
628 ((ReferenceCounted) engine).release();
629 }
630 }
631
632 @Override
633 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
634 ctx.bind(localAddress, promise);
635 }
636
637 @Override
638 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
639 ChannelPromise promise) throws Exception {
640 ctx.connect(remoteAddress, localAddress, promise);
641 }
642
643 @Override
644 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
645 ctx.deregister(promise);
646 }
647
648 @Override
649 public void disconnect(final ChannelHandlerContext ctx,
650 final ChannelPromise promise) throws Exception {
651 closeOutboundAndChannel(ctx, promise, true);
652 }
653
654 @Override
655 public void close(final ChannelHandlerContext ctx,
656 final ChannelPromise promise) throws Exception {
657 closeOutboundAndChannel(ctx, promise, false);
658 }
659
660 @Override
661 public void read(ChannelHandlerContext ctx) throws Exception {
662 if (!handshakePromise.isDone()) {
663 readDuringHandshake = true;
664 }
665
666 ctx.read();
667 }
668
669 private static IllegalStateException newPendingWritesNullException() {
670 return new IllegalStateException("pendingUnencryptedWrites is null, handlerRemoved0 called?");
671 }
672
673 @Override
674 public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
675 if (!(msg instanceof ByteBuf)) {
676 UnsupportedMessageTypeException exception = new UnsupportedMessageTypeException(msg, ByteBuf.class);
677 ReferenceCountUtil.safeRelease(msg);
678 promise.setFailure(exception);
679 } else if (pendingUnencryptedWrites == null) {
680 ReferenceCountUtil.safeRelease(msg);
681 promise.setFailure(newPendingWritesNullException());
682 } else {
683 pendingUnencryptedWrites.add(msg, promise);
684 }
685 }
686
687 @Override
688 public void flush(ChannelHandlerContext ctx) throws Exception {
689 // Do not encrypt the first write request if this handler is
690 // created with startTLS flag turned on.
691 if (startTls && !sentFirstMessage) {
692 sentFirstMessage = true;
693 pendingUnencryptedWrites.removeAndWriteAll();
694 forceFlush(ctx);
695 return;
696 }
697
698 try {
699 wrapAndFlush(ctx);
700 } catch (Throwable cause) {
701 setHandshakeFailure(ctx, cause);
702 PlatformDependent.throwException(cause);
703 }
704 }
705
706 private void wrapAndFlush(ChannelHandlerContext ctx) throws SSLException {
707 if (pendingUnencryptedWrites.isEmpty()) {
708 // It's important to NOT use a voidPromise here as the user
709 // may want to add a ChannelFutureListener to the ChannelPromise later.
710 //
711 // See https://github.com/netty/netty/issues/3364
712 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise());
713 }
714 if (!handshakePromise.isDone()) {
715 flushedBeforeHandshake = true;
716 }
717 try {
718 wrap(ctx, false);
719 } finally {
720 // We may have written some parts of data before an exception was thrown so ensure we always flush.
721 // See https://github.com/netty/netty/issues/3900#issuecomment-172481830
722 forceFlush(ctx);
723 }
724 }
725
726 // This method will not call setHandshakeFailure(...) !
727 private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
728 ByteBuf out = null;
729 ChannelPromise promise = null;
730 ByteBufAllocator alloc = ctx.alloc();
731 boolean needUnwrap = false;
732 try {
733 // Only continue to loop if the handler was not removed in the meantime.
734 // See https://github.com/netty/netty/issues/5860
735 while (!ctx.isRemoved()) {
736 Object msg = pendingUnencryptedWrites.current();
737 if (msg == null) {
738 break;
739 }
740
741 ByteBuf buf = (ByteBuf) msg;
742 if (out == null) {
743 out = allocateOutNetBuf(ctx, buf.readableBytes(), buf.nioBufferCount());
744 }
745
746 SSLEngineResult result = wrap(alloc, engine, buf, out);
747
748 if (result.getStatus() == Status.CLOSED) {
749 // SSLEngine has been closed already.
750 // Any further write attempts should be denied.
751 pendingUnencryptedWrites.removeAndFailAll(SSLENGINE_CLOSED);
752 return;
753 } else {
754 if (!buf.isReadable()) {
755 promise = pendingUnencryptedWrites.remove();
756 } else {
757 promise = null;
758 }
759
760 switch (result.getHandshakeStatus()) {
761 case NEED_TASK:
762 runDelegatedTasks();
763 break;
764 case FINISHED:
765 setHandshakeSuccess();
766 // deliberate fall-through
767 case NOT_HANDSHAKING:
768 setHandshakeSuccessIfStillHandshaking();
769 // deliberate fall-through
770 case NEED_WRAP:
771 finishWrap(ctx, out, promise, inUnwrap, false);
772 promise = null;
773 out = null;
774 break;
775 case NEED_UNWRAP:
776 needUnwrap = true;
777 return;
778 default:
779 throw new IllegalStateException(
780 "Unknown handshake status: " + result.getHandshakeStatus());
781 }
782 }
783 }
784 } finally {
785 finishWrap(ctx, out, promise, inUnwrap, needUnwrap);
786 }
787 }
788
789 private void finishWrap(ChannelHandlerContext ctx, ByteBuf out, ChannelPromise promise, boolean inUnwrap,
790 boolean needUnwrap) {
791 if (out == null) {
792 out = Unpooled.EMPTY_BUFFER;
793 } else if (!out.isReadable()) {
794 out.release();
795 out = Unpooled.EMPTY_BUFFER;
796 }
797
798 if (promise != null) {
799 ctx.write(out, promise);
800 } else {
801 ctx.write(out);
802 }
803
804 if (inUnwrap) {
805 needsFlush = true;
806 }
807
808 if (needUnwrap) {
809 // The underlying engine is starving so we need to feed it with more data.
810 // See https://github.com/netty/netty/pull/5039
811 readIfNeeded(ctx);
812 }
813 }
814
815 /**
816 * This method will not call
817 * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean, boolean)} or
818 * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}.
819 * @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}.
820 */
821 private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException {
822 ByteBuf out = null;
823 ByteBufAllocator alloc = ctx.alloc();
824 try {
825 // Only continue to loop if the handler was not removed in the meantime.
826 // See https://github.com/netty/netty/issues/5860
827 while (!ctx.isRemoved()) {
828 if (out == null) {
829 // As this is called for the handshake we have no real idea how big the buffer needs to be.
830 // That said 2048 should give us enough room to include everything like ALPN / NPN data.
831 // If this is not enough we will increase the buffer in wrap(...).
832 out = allocateOutNetBuf(ctx, 2048, 1);
833 }
834 SSLEngineResult result = wrap(alloc, engine, Unpooled.EMPTY_BUFFER, out);
835
836 if (result.bytesProduced() > 0) {
837 ctx.write(out);
838 if (inUnwrap) {
839 needsFlush = true;
840 }
841 out = null;
842 }
843
844 switch (result.getHandshakeStatus()) {
845 case FINISHED:
846 setHandshakeSuccess();
847 return false;
848 case NEED_TASK:
849 runDelegatedTasks();
850 break;
851 case NEED_UNWRAP:
852 if (inUnwrap) {
853 // If we asked for a wrap, the engine requested an unwrap, and we are in unwrap there is
854 // no use in trying to call wrap again because we have already attempted (or will after we
855 // return) to feed more data to the engine.
856 return false;
857 }
858
859 unwrapNonAppData(ctx);
860 break;
861 case NEED_WRAP:
862 break;
863 case NOT_HANDSHAKING:
864 setHandshakeSuccessIfStillHandshaking();
865 // Workaround for TLS False Start problem reported at:
866 // https://github.com/netty/netty/issues/1108#issuecomment-14266970
867 if (!inUnwrap) {
868 unwrapNonAppData(ctx);
869 }
870 return true;
871 default:
872 throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus());
873 }
874
875 if (result.bytesProduced() == 0) {
876 break;
877 }
878
879 // It should not consume empty buffers when it is not handshaking
880 // Fix for Android, where it was encrypting empty buffers even when not handshaking
881 if (result.bytesConsumed() == 0 && result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
882 break;
883 }
884 }
885 } finally {
886 if (out != null) {
887 out.release();
888 }
889 }
890 return false;
891 }
892
893 private SSLEngineResult wrap(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out)
894 throws SSLException {
895 ByteBuf newDirectIn = null;
896 try {
897 int readerIndex = in.readerIndex();
898 int readableBytes = in.readableBytes();
899
900 // We will call SslEngine.wrap(ByteBuffer[], ByteBuffer) to allow efficient handling of
901 // CompositeByteBuf without force an extra memory copy when CompositeByteBuffer.nioBuffer() is called.
902 final ByteBuffer[] in0;
903 if (in.isDirect() || !engineType.wantsDirectBuffer) {
904 // As CompositeByteBuf.nioBufferCount() can be expensive (as it needs to check all composed ByteBuf
905 // to calculate the count) we will just assume a CompositeByteBuf contains more then 1 ByteBuf.
906 // The worst that can happen is that we allocate an extra ByteBuffer[] in CompositeByteBuf.nioBuffers()
907 // which is better then walking the composed ByteBuf in most cases.
908 if (!(in instanceof CompositeByteBuf) && in.nioBufferCount() == 1) {
909 in0 = singleBuffer;
910 // We know its only backed by 1 ByteBuffer so use internalNioBuffer to keep object allocation
911 // to a minimum.
912 in0[0] = in.internalNioBuffer(readerIndex, readableBytes);
913 } else {
914 in0 = in.nioBuffers();
915 }
916 } else {
917 // We could even go further here and check if its a CompositeByteBuf and if so try to decompose it and
918 // only replace the ByteBuffer that are not direct. At the moment we just will replace the whole
919 // CompositeByteBuf to keep the complexity to a minimum
920 newDirectIn = alloc.directBuffer(readableBytes);
921 newDirectIn.writeBytes(in, readerIndex, readableBytes);
922 in0 = singleBuffer;
923 in0[0] = newDirectIn.internalNioBuffer(newDirectIn.readerIndex(), readableBytes);
924 }
925
926 for (;;) {
927 ByteBuffer out0 = out.nioBuffer(out.writerIndex(), out.writableBytes());
928 SSLEngineResult result = engine.wrap(in0, out0);
929 in.skipBytes(result.bytesConsumed());
930 out.writerIndex(out.writerIndex() + result.bytesProduced());
931
932 switch (result.getStatus()) {
933 case BUFFER_OVERFLOW:
934 out.ensureWritable(engine.getSession().getPacketBufferSize());
935 break;
936 default:
937 return result;
938 }
939 }
940 } finally {
941 // Null out to allow GC of ByteBuffer
942 singleBuffer[0] = null;
943
944 if (newDirectIn != null) {
945 newDirectIn.release();
946 }
947 }
948 }
949
950 @Override
951 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
952 // Make sure to release SSLEngine,
953 // and notify the handshake future if the connection has been closed during handshake.
954 setHandshakeFailure(ctx, CHANNEL_CLOSED, !outboundClosed, handshakeStarted);
955
956 // Ensure we always notify the sslClosePromise as well
957 notifyClosePromise(CHANNEL_CLOSED);
958
959 super.channelInactive(ctx);
960 }
961
962 @Override
963 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
964 if (ignoreException(cause)) {
965 // It is safe to ignore the 'connection reset by peer' or
966 // 'broken pipe' error after sending close_notify.
967 if (logger.isDebugEnabled()) {
968 logger.debug(
969 "{} Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred " +
970 "while writing close_notify in response to the peer's close_notify", ctx.channel(), cause);
971 }
972
973 // Close the connection explicitly just in case the transport
974 // did not close the connection automatically.
975 if (ctx.channel().isActive()) {
976 ctx.close();
977 }
978 } else {
979 ctx.fireExceptionCaught(cause);
980 }
981 }
982
983 /**
984 * Checks if the given {@link Throwable} can be ignore and just "swallowed"
985 *
986 * When an ssl connection is closed a close_notify message is sent.
987 * After that the peer also sends close_notify however, it's not mandatory to receive
988 * the close_notify. The party who sent the initial close_notify can close the connection immediately
989 * then the peer will get connection reset error.
990 *
991 */
992 private boolean ignoreException(Throwable t) {
993 if (!(t instanceof SSLException) && t instanceof IOException && sslClosePromise.isDone()) {
994 String message = t.getMessage();
995
996 // first try to match connection reset / broke peer based on the regex. This is the fastest way
997 // but may fail on different jdk impls or OS's
998 if (message != null && IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
999 return true;
1000 }
1001
1002 // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
1003 StackTraceElement[] elements = t.getStackTrace();
1004 for (StackTraceElement element: elements) {
1005 String classname = element.getClassName();
1006 String methodname = element.getMethodName();
1007
1008 // skip all classes that belong to the io.netty package
1009 if (classname.startsWith("io.netty.")) {
1010 continue;
1011 }
1012
1013 // check if the method name is read if not skip it
1014 if (!"read".equals(methodname)) {
1015 continue;
1016 }
1017
1018 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
1019 // also others
1020 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
1021 return true;
1022 }
1023
1024 try {
1025 // No match by now.. Try to load the class via classloader and inspect it.
1026 // This is mainly done as other JDK implementations may differ in name of
1027 // the impl.
1028 Class<?> clazz = PlatformDependent.getClassLoader(getClass()).loadClass(classname);
1029
1030 if (SocketChannel.class.isAssignableFrom(clazz)
1031 || DatagramChannel.class.isAssignableFrom(clazz)) {
1032 return true;
1033 }
1034
1035 // also match against SctpChannel via String matching as it may not present.
1036 if (PlatformDependent.javaVersion() >= 7
1037 && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
1038 return true;
1039 }
1040 } catch (Throwable cause) {
1041 logger.debug("Unexpected exception while loading class {} classname {}",
1042 getClass(), classname, cause);
1043 }
1044 }
1045 }
1046
1047 return false;
1048 }
1049
1050 /**
1051 * Returns {@code true} if the given {@link ByteBuf} is encrypted. Be aware that this method
1052 * will not increase the readerIndex of the given {@link ByteBuf}.
1053 *
1054 * @param buffer
1055 * The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read,
1056 * otherwise it will throw an {@link IllegalArgumentException}.
1057 * @return encrypted
1058 * {@code true} if the {@link ByteBuf} is encrypted, {@code false} otherwise.
1059 * @throws IllegalArgumentException
1060 * Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read.
1061 */
1062 public static boolean isEncrypted(ByteBuf buffer) {
1063 if (buffer.readableBytes() < SslUtils.SSL_RECORD_HEADER_LENGTH) {
1064 throw new IllegalArgumentException(
1065 "buffer must have at least " + SslUtils.SSL_RECORD_HEADER_LENGTH + " readable bytes");
1066 }
1067 return getEncryptedPacketLength(buffer, buffer.readerIndex()) != SslUtils.NOT_ENCRYPTED;
1068 }
1069
1070 private void decodeJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) throws NotSslRecordException {
1071 int packetLength = this.packetLength;
1072 // If we calculated the length of the current SSL record before, use that information.
1073 if (packetLength > 0) {
1074 if (in.readableBytes() < packetLength) {
1075 return;
1076 }
1077 } else {
1078 // Get the packet length and wait until we get a packets worth of data to unwrap.
1079 final int readableBytes = in.readableBytes();
1080 if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) {
1081 return;
1082 }
1083 packetLength = getEncryptedPacketLength(in, in.readerIndex());
1084 if (packetLength == SslUtils.NOT_ENCRYPTED) {
1085 // Not an SSL/TLS packet
1086 NotSslRecordException e = new NotSslRecordException(
1087 "not an SSL/TLS record: " + ByteBufUtil.hexDump(in));
1088 in.skipBytes(in.readableBytes());
1089
1090 // First fail the handshake promise as we may need to have access to the SSLEngine which may
1091 // be released because the user will remove the SslHandler in an exceptionCaught(...) implementation.
1092 setHandshakeFailure(ctx, e);
1093
1094 throw e;
1095 }
1096 assert packetLength > 0;
1097 if (packetLength > readableBytes) {
1098 // wait until the whole packet can be read
1099 this.packetLength = packetLength;
1100 return;
1101 }
1102 }
1103
1104 // Reset the state of this class so we can get the length of the next packet. We assume the entire packet will
1105 // be consumed by the SSLEngine.
1106 this.packetLength = 0;
1107 try {
1108 int bytesConsumed = unwrap(ctx, in, in.readerIndex(), packetLength);
1109 assert bytesConsumed == packetLength || engine.isInboundDone() :
1110 "we feed the SSLEngine a packets worth of data: " + packetLength + " but it only consumed: " +
1111 bytesConsumed;
1112 in.skipBytes(bytesConsumed);
1113 } catch (Throwable cause) {
1114 handleUnwrapThrowable(ctx, cause);
1115 }
1116 }
1117
1118 private void decodeNonJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) {
1119 try {
1120 in.skipBytes(unwrap(ctx, in, in.readerIndex(), in.readableBytes()));
1121 } catch (Throwable cause) {
1122 handleUnwrapThrowable(ctx, cause);
1123 }
1124 }
1125
1126 private void handleUnwrapThrowable(ChannelHandlerContext ctx, Throwable cause) {
1127 try {
1128 // We should attempt to notify the handshake failure before writing any pending data. If we are in unwrap
1129 // and failed during the handshake process, and we attempt to wrap, then promises will fail, and if
1130 // listeners immediately close the Channel then we may end up firing the handshake event after the Channel
1131 // has been closed.
1132 if (handshakePromise.tryFailure(cause)) {
1133 ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause));
1134 }
1135
1136 // We need to flush one time as there may be an alert that we should send to the remote peer because
1137 // of the SSLException reported here.
1138 wrapAndFlush(ctx);
1139 } catch (SSLException ex) {
1140 logger.debug("SSLException during trying to call SSLEngine.wrap(...)" +
1141 " because of an previous SSLException, ignoring...", ex);
1142 } finally {
1143 setHandshakeFailure(ctx, cause, true, false);
1144 }
1145 PlatformDependent.throwException(cause);
1146 }
1147
1148 @Override
1149 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws SSLException {
1150 if (jdkCompatibilityMode) {
1151 decodeJdkCompatible(ctx, in);
1152 } else {
1153 decodeNonJdkCompatible(ctx, in);
1154 }
1155 }
1156
1157 @Override
1158 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
1159 // Discard bytes of the cumulation buffer if needed.
1160 discardSomeReadBytes();
1161
1162 flushIfNeeded(ctx);
1163 readIfNeeded(ctx);
1164
1165 firedChannelRead = false;
1166 ctx.fireChannelReadComplete();
1167 }
1168
1169 private void readIfNeeded(ChannelHandlerContext ctx) {
1170 // If handshake is not finished yet, we need more data.
1171 if (!ctx.channel().config().isAutoRead() && (!firedChannelRead || !handshakePromise.isDone())) {
1172 // No auto-read used and no message passed through the ChannelPipeline or the handshake was not complete
1173 // yet, which means we need to trigger the read to ensure we not encounter any stalls.
1174 ctx.read();
1175 }
1176 }
1177
1178 private void flushIfNeeded(ChannelHandlerContext ctx) {
1179 if (needsFlush) {
1180 forceFlush(ctx);
1181 }
1182 }
1183
1184 /**
1185 * Calls {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} with an empty buffer to handle handshakes, etc.
1186 */
1187 private void unwrapNonAppData(ChannelHandlerContext ctx) throws SSLException {
1188 unwrap(ctx, Unpooled.EMPTY_BUFFER, 0, 0);
1189 }
1190
1191 /**
1192 * Unwraps inbound SSL records.
1193 */
1194 private int unwrap(
1195 ChannelHandlerContext ctx, ByteBuf packet, int offset, int length) throws SSLException {
1196 final int originalLength = length;
1197 boolean wrapLater = false;
1198 boolean notifyClosure = false;
1199 int overflowReadableBytes = -1;
1200 ByteBuf decodeOut = allocate(ctx, length);
1201 try {
1202 // Only continue to loop if the handler was not removed in the meantime.
1203 // See https://github.com/netty/netty/issues/5860
1204 unwrapLoop: while (!ctx.isRemoved()) {
1205 final SSLEngineResult result = engineType.unwrap(this, packet, offset, length, decodeOut);
1206 final Status status = result.getStatus();
1207 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1208 final int produced = result.bytesProduced();
1209 final int consumed = result.bytesConsumed();
1210
1211 // Update indexes for the next iteration
1212 offset += consumed;
1213 length -= consumed;
1214
1215 switch (status) {
1216 case BUFFER_OVERFLOW:
1217 final int readableBytes = decodeOut.readableBytes();
1218 final int previousOverflowReadableBytes = overflowReadableBytes;
1219 overflowReadableBytes = readableBytes;
1220 int bufferSize = engine.getSession().getApplicationBufferSize() - readableBytes;
1221 if (readableBytes > 0) {
1222 firedChannelRead = true;
1223 ctx.fireChannelRead(decodeOut);
1224
1225 // This buffer was handled, null it out.
1226 decodeOut = null;
1227 if (bufferSize <= 0) {
1228 // It may happen that readableBytes >= engine.getSession().getApplicationBufferSize()
1229 // while there is still more to unwrap, in this case we will just allocate a new buffer
1230 // with the capacity of engine.getSession().getApplicationBufferSize() and call unwrap
1231 // again.
1232 bufferSize = engine.getSession().getApplicationBufferSize();
1233 }
1234 } else {
1235 // This buffer was handled, null it out.
1236 decodeOut.release();
1237 decodeOut = null;
1238 }
1239 if (readableBytes == 0 && previousOverflowReadableBytes == 0) {
1240 // If there is two consecutive loops where we overflow and are not able to consume any data,
1241 // assume the amount of data exceeds the maximum amount for the engine and bail
1242 throw new IllegalStateException("Two consecutive overflows but no content was consumed. " +
1243 SSLSession.class.getSimpleName() + " getApplicationBufferSize: " +
1244 engine.getSession().getApplicationBufferSize() + " maybe too small.");
1245 }
1246 // Allocate a new buffer which can hold all the rest data and loop again.
1247 // TODO: We may want to reconsider how we calculate the length here as we may
1248 // have more then one ssl message to decode.
1249 decodeOut = allocate(ctx, engineType.calculatePendingData(this, bufferSize));
1250 continue;
1251 case CLOSED:
1252 // notify about the CLOSED state of the SSLEngine. See #137
1253 notifyClosure = true;
1254 overflowReadableBytes = -1;
1255 break;
1256 default:
1257 overflowReadableBytes = -1;
1258 break;
1259 }
1260
1261 switch (handshakeStatus) {
1262 case NEED_UNWRAP:
1263 break;
1264 case NEED_WRAP:
1265 // If the wrap operation transitions the status to NOT_HANDSHAKING and there is no more data to
1266 // unwrap then the next call to unwrap will not produce any data. We can avoid the potentially
1267 // costly unwrap operation and break out of the loop.
1268 if (wrapNonAppData(ctx, true) && length == 0) {
1269 break unwrapLoop;
1270 }
1271 break;
1272 case NEED_TASK:
1273 runDelegatedTasks();
1274 break;
1275 case FINISHED:
1276 setHandshakeSuccess();
1277 wrapLater = true;
1278
1279 // We 'break' here and NOT 'continue' as android API version 21 has a bug where they consume
1280 // data from the buffer but NOT correctly set the SSLEngineResult.bytesConsumed().
1281 // Because of this it will raise an exception on the next iteration of the for loop on android
1282 // API version 21. Just doing a break will work here as produced and consumed will both be 0
1283 // and so we break out of the complete for (;;) loop and so call decode(...) again later on.
1284 // On other platforms this will have no negative effect as we will just continue with the
1285 // for (;;) loop if something was either consumed or produced.
1286 //
1287 // See:
1288 // - https://github.com/netty/netty/issues/4116
1289 // - https://code.google.com/p/android/issues/detail?id=198639&thanks=198639&ts=1452501203
1290 break;
1291 case NOT_HANDSHAKING:
1292 if (setHandshakeSuccessIfStillHandshaking()) {
1293 wrapLater = true;
1294 continue;
1295 }
1296 if (flushedBeforeHandshake) {
1297 // We need to call wrap(...) in case there was a flush done before the handshake completed.
1298 //
1299 // See https://github.com/netty/netty/pull/2437
1300 flushedBeforeHandshake = false;
1301 wrapLater = true;
1302 }
1303 // If we are not handshaking and there is no more data to unwrap then the next call to unwrap
1304 // will not produce any data. We can avoid the potentially costly unwrap operation and break
1305 // out of the loop.
1306 if (length == 0) {
1307 break unwrapLoop;
1308 }
1309 break;
1310 default:
1311 throw new IllegalStateException("unknown handshake status: " + handshakeStatus);
1312 }
1313
1314 if (status == Status.BUFFER_UNDERFLOW || consumed == 0 && produced == 0) {
1315 if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) {
1316 // The underlying engine is starving so we need to feed it with more data.
1317 // See https://github.com/netty/netty/pull/5039
1318 readIfNeeded(ctx);
1319 }
1320
1321 break;
1322 }
1323 }
1324
1325 if (wrapLater) {
1326 wrap(ctx, true);
1327 }
1328
1329 if (notifyClosure) {
1330 notifyClosePromise(null);
1331 }
1332 } finally {
1333 if (decodeOut != null) {
1334 if (decodeOut.isReadable()) {
1335 firedChannelRead = true;
1336
1337 ctx.fireChannelRead(decodeOut);
1338 } else {
1339 decodeOut.release();
1340 }
1341 }
1342 }
1343 return originalLength - length;
1344 }
1345
1346 private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) {
1347 return out.nioBufferCount() == 1 ? out.internalNioBuffer(index, len) :
1348 out.nioBuffer(index, len);
1349 }
1350
1351 /**
1352 * Fetches all delegated tasks from the {@link SSLEngine} and runs them via the {@link #delegatedTaskExecutor}.
1353 * If the {@link #delegatedTaskExecutor} is {@link ImmediateExecutor}, just call {@link Runnable#run()} directly
1354 * instead of using {@link Executor#execute(Runnable)}. Otherwise, run the tasks via
1355 * the {@link #delegatedTaskExecutor} and wait until the tasks are finished.
1356 */
1357 private void runDelegatedTasks() {
1358 if (delegatedTaskExecutor == ImmediateExecutor.INSTANCE) {
1359 for (;;) {
1360 Runnable task = engine.getDelegatedTask();
1361 if (task == null) {
1362 break;
1363 }
1364
1365 task.run();
1366 }
1367 } else {
1368 final List<Runnable> tasks = new ArrayList<Runnable>(2);
1369 for (;;) {
1370 final Runnable task = engine.getDelegatedTask();
1371 if (task == null) {
1372 break;
1373 }
1374
1375 tasks.add(task);
1376 }
1377
1378 if (tasks.isEmpty()) {
1379 return;
1380 }
1381
1382 final CountDownLatch latch = new CountDownLatch(1);
1383 delegatedTaskExecutor.execute(new Runnable() {
1384 @Override
1385 public void run() {
1386 try {
1387 for (Runnable task: tasks) {
1388 task.run();
1389 }
1390 } catch (Exception e) {
1391 ctx.fireExceptionCaught(e);
1392 } finally {
1393 latch.countDown();
1394 }
1395 }
1396 });
1397
1398 boolean interrupted = false;
1399 while (latch.getCount() != 0) {
1400 try {
1401 latch.await();
1402 } catch (InterruptedException e) {
1403 // Interrupt later.
1404 interrupted = true;
1405 }
1406 }
1407
1408 if (interrupted) {
1409 Thread.currentThread().interrupt();
1410 }
1411 }
1412 }
1413
1414 /**
1415 * Works around some Android {@link SSLEngine} implementations that skip {@link HandshakeStatus#FINISHED} and
1416 * go straight into {@link HandshakeStatus#NOT_HANDSHAKING} when handshake is finished.
1417 *
1418 * @return {@code true} if and only if the workaround has been applied and thus {@link #handshakeFuture} has been
1419 * marked as success by this method
1420 */
1421 private boolean setHandshakeSuccessIfStillHandshaking() {
1422 if (!handshakePromise.isDone()) {
1423 setHandshakeSuccess();
1424 return true;
1425 }
1426 return false;
1427 }
1428
1429 /**
1430 * Notify all the handshake futures about the successfully handshake
1431 */
1432 private void setHandshakeSuccess() {
1433 handshakePromise.trySuccess(ctx.channel());
1434
1435 if (logger.isDebugEnabled()) {
1436 logger.debug("{} HANDSHAKEN: {}", ctx.channel(), engine.getSession().getCipherSuite());
1437 }
1438 ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS);
1439
1440 if (readDuringHandshake && !ctx.channel().config().isAutoRead()) {
1441 readDuringHandshake = false;
1442 ctx.read();
1443 }
1444 }
1445
1446 /**
1447 * Notify all the handshake futures about the failure during the handshake.
1448 */
1449 private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) {
1450 setHandshakeFailure(ctx, cause, true, true);
1451 }
1452
1453 /**
1454 * Notify all the handshake futures about the failure during the handshake.
1455 */
1456 private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean closeInbound, boolean notify) {
1457 try {
1458 // Release all resources such as internal buffers that SSLEngine
1459 // is managing.
1460 engine.closeOutbound();
1461
1462 if (closeInbound) {
1463 try {
1464 engine.closeInbound();
1465 } catch (SSLException e) {
1466 if (logger.isDebugEnabled()) {
1467 // only log in debug mode as it most likely harmless and latest chrome still trigger
1468 // this all the time.
1469 //
1470 // See https://github.com/netty/netty/issues/1340
1471 String msg = e.getMessage();
1472 if (msg == null || !msg.contains("possible truncation attack")) {
1473 logger.debug("{} SSLEngine.closeInbound() raised an exception.", ctx.channel(), e);
1474 }
1475 }
1476 }
1477 }
1478 notifyHandshakeFailure(cause, notify);
1479 } finally {
1480 if (pendingUnencryptedWrites != null) {
1481 // Ensure we remove and fail all pending writes in all cases and so release memory quickly.
1482 pendingUnencryptedWrites.removeAndFailAll(cause);
1483 }
1484 }
1485 }
1486
1487 private void notifyHandshakeFailure(Throwable cause, boolean notify) {
1488 if (handshakePromise.tryFailure(cause)) {
1489 SslUtils.notifyHandshakeFailure(ctx, cause, notify);
1490 }
1491 }
1492
1493 private void notifyClosePromise(Throwable cause) {
1494 if (cause == null) {
1495 if (sslClosePromise.trySuccess(ctx.channel())) {
1496 ctx.fireUserEventTriggered(SslCloseCompletionEvent.SUCCESS);
1497 }
1498 } else {
1499 if (sslClosePromise.tryFailure(cause)) {
1500 ctx.fireUserEventTriggered(new SslCloseCompletionEvent(cause));
1501 }
1502 }
1503 }
1504
1505 private void closeOutboundAndChannel(
1506 final ChannelHandlerContext ctx, final ChannelPromise promise, boolean disconnect) throws Exception {
1507 if (!ctx.channel().isActive()) {
1508 if (disconnect) {
1509 ctx.disconnect(promise);
1510 } else {
1511 ctx.close(promise);
1512 }
1513 return;
1514 }
1515
1516 outboundClosed = true;
1517 engine.closeOutbound();
1518
1519 ChannelPromise closeNotifyPromise = ctx.newPromise();
1520 try {
1521 flush(ctx, closeNotifyPromise);
1522 } finally {
1523 // It's important that we do not pass the original ChannelPromise to safeClose(...) as when flush(....)
1524 // throws an Exception it will be propagated to the AbstractChannelHandlerContext which will try
1525 // to fail the promise because of this. This will then fail as it was already completed by safeClose(...).
1526 // We create a new ChannelPromise and try to notify the original ChannelPromise
1527 // once it is complete. If we fail to do so we just ignore it as in this case it was failed already
1528 // because of a propagated Exception.
1529 //
1530 // See https://github.com/netty/netty/issues/5931
1531 safeClose(ctx, closeNotifyPromise, ctx.newPromise().addListener(
1532 new ChannelPromiseNotifier(false, promise)));
1533 }
1534 }
1535
1536 private void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
1537 if (pendingUnencryptedWrites != null) {
1538 pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, promise);
1539 } else {
1540 promise.setFailure(newPendingWritesNullException());
1541 }
1542 flush(ctx);
1543 }
1544
1545 @Override
1546 public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
1547 this.ctx = ctx;
1548 pendingUnencryptedWrites = new PendingWriteQueue(ctx);
1549
1550 if (ctx.channel().isActive()) {
1551 startHandshakeProcessing();
1552 }
1553 }
1554
1555 private void startHandshakeProcessing() {
1556 handshakeStarted = true;
1557 if (engine.getUseClientMode()) {
1558 // Begin the initial handshake.
1559 // channelActive() event has been fired already, which means this.channelActive() will
1560 // not be invoked. We have to initialize here instead.
1561 handshake(null);
1562 } else {
1563 applyHandshakeTimeout(null);
1564 }
1565 }
1566
1567 /**
1568 * Performs TLS renegotiation.
1569 */
1570 public Future<Channel> renegotiate() {
1571 ChannelHandlerContext ctx = this.ctx;
1572 if (ctx == null) {
1573 throw new IllegalStateException();
1574 }
1575
1576 return renegotiate(ctx.executor().<Channel>newPromise());
1577 }
1578
1579 /**
1580 * Performs TLS renegotiation.
1581 */
1582 public Future<Channel> renegotiate(final Promise<Channel> promise) {
1583 if (promise == null) {
1584 throw new NullPointerException("promise");
1585 }
1586
1587 ChannelHandlerContext ctx = this.ctx;
1588 if (ctx == null) {
1589 throw new IllegalStateException();
1590 }
1591
1592 EventExecutor executor = ctx.executor();
1593 if (!executor.inEventLoop()) {
1594 executor.execute(new Runnable() {
1595 @Override
1596 public void run() {
1597 handshake(promise);
1598 }
1599 });
1600 return promise;
1601 }
1602
1603 handshake(promise);
1604 return promise;
1605 }
1606
1607 /**
1608 * Performs TLS (re)negotiation.
1609 *
1610 * @param newHandshakePromise if {@code null}, use the existing {@link #handshakePromise},
1611 * assuming that the current negotiation has not been finished.
1612 * Currently, {@code null} is expected only for the initial handshake.
1613 */
1614 private void handshake(final Promise<Channel> newHandshakePromise) {
1615 final Promise<Channel> p;
1616 if (newHandshakePromise != null) {
1617 final Promise<Channel> oldHandshakePromise = handshakePromise;
1618 if (!oldHandshakePromise.isDone()) {
1619 // There's no need to handshake because handshake is in progress already.
1620 // Merge the new promise into the old one.
1621 oldHandshakePromise.addListener(new FutureListener<Channel>() {
1622 @Override
1623 public void operationComplete(Future<Channel> future) throws Exception {
1624 if (future.isSuccess()) {
1625 newHandshakePromise.setSuccess(future.getNow());
1626 } else {
1627 newHandshakePromise.setFailure(future.cause());
1628 }
1629 }
1630 });
1631 return;
1632 }
1633
1634 handshakePromise = p = newHandshakePromise;
1635 } else if (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
1636 // Not all SSLEngine implementations support calling beginHandshake multiple times while a handshake
1637 // is in progress. See https://github.com/netty/netty/issues/4718.
1638 return;
1639 } else {
1640 // Forced to reuse the old handshake.
1641 p = handshakePromise;
1642 assert !p.isDone();
1643 }
1644
1645 // Begin handshake.
1646 final ChannelHandlerContext ctx = this.ctx;
1647 try {
1648 engine.beginHandshake();
1649 wrapNonAppData(ctx, false);
1650 } catch (Throwable e) {
1651 setHandshakeFailure(ctx, e);
1652 } finally {
1653 forceFlush(ctx);
1654 }
1655 applyHandshakeTimeout(p);
1656 }
1657
1658 private void applyHandshakeTimeout(Promise<Channel> p) {
1659 final Promise<Channel> promise = p == null ? handshakePromise : p;
1660 // Set timeout if necessary.
1661 final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
1662 if (handshakeTimeoutMillis <= 0 || promise.isDone()) {
1663 return;
1664 }
1665
1666 final ScheduledFuture<?> timeoutFuture = ctx.executor().schedule(new Runnable() {
1667 @Override
1668 public void run() {
1669 if (promise.isDone()) {
1670 return;
1671 }
1672 notifyHandshakeFailure(HANDSHAKE_TIMED_OUT, true);
1673 }
1674 }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
1675
1676 // Cancel the handshake timeout when handshake is finished.
1677 promise.addListener(new FutureListener<Channel>() {
1678 @Override
1679 public void operationComplete(Future<Channel> f) throws Exception {
1680 timeoutFuture.cancel(false);
1681 }
1682 });
1683 }
1684
1685 private void forceFlush(ChannelHandlerContext ctx) {
1686 needsFlush = false;
1687 ctx.flush();
1688 }
1689
1690 /**
1691 * Issues an initial TLS handshake once connected when used in client-mode
1692 */
1693 @Override
1694 public void channelActive(final ChannelHandlerContext ctx) throws Exception {
1695 if (!startTls) {
1696 startHandshakeProcessing();
1697 }
1698 ctx.fireChannelActive();
1699 }
1700
1701 private void safeClose(
1702 final ChannelHandlerContext ctx, final ChannelFuture flushFuture,
1703 final ChannelPromise promise) {
1704 if (!ctx.channel().isActive()) {
1705 ctx.close(promise);
1706 return;
1707 }
1708
1709 final ScheduledFuture<?> timeoutFuture;
1710 if (!flushFuture.isDone()) {
1711 long closeNotifyTimeout = closeNotifyFlushTimeoutMillis;
1712 if (closeNotifyTimeout > 0) {
1713 // Force-close the connection if close_notify is not fully sent in time.
1714 timeoutFuture = ctx.executor().schedule(new Runnable() {
1715 @Override
1716 public void run() {
1717 // May be done in the meantime as cancel(...) is only best effort.
1718 if (!flushFuture.isDone()) {
1719 logger.warn("{} Last write attempt timed out; force-closing the connection.",
1720 ctx.channel());
1721 addCloseListener(ctx.close(ctx.newPromise()), promise);
1722 }
1723 }
1724 }, closeNotifyTimeout, TimeUnit.MILLISECONDS);
1725 } else {
1726 timeoutFuture = null;
1727 }
1728 } else {
1729 timeoutFuture = null;
1730 }
1731
1732 // Close the connection if close_notify is sent in time.
1733 flushFuture.addListener(new ChannelFutureListener() {
1734 @Override
1735 public void operationComplete(ChannelFuture f)
1736 throws Exception {
1737 if (timeoutFuture != null) {
1738 timeoutFuture.cancel(false);
1739 }
1740 final long closeNotifyReadTimeout = closeNotifyReadTimeoutMillis;
1741 if (closeNotifyReadTimeout <= 0) {
1742 // Trigger the close in all cases to make sure the promise is notified
1743 // See https://github.com/netty/netty/issues/2358
1744 addCloseListener(ctx.close(ctx.newPromise()), promise);
1745 } else {
1746 final ScheduledFuture<?> closeNotifyReadTimeoutFuture;
1747
1748 if (!sslClosePromise.isDone()) {
1749 closeNotifyReadTimeoutFuture = ctx.executor().schedule(new Runnable() {
1750 @Override
1751 public void run() {
1752 if (!sslClosePromise.isDone()) {
1753 logger.debug(
1754 "{} did not receive close_notify in {}ms; force-closing the connection.",
1755 ctx.channel(), closeNotifyReadTimeout);
1756
1757 // Do the close now...
1758 addCloseListener(ctx.close(ctx.newPromise()), promise);
1759 }
1760 }
1761 }, closeNotifyReadTimeout, TimeUnit.MILLISECONDS);
1762 } else {
1763 closeNotifyReadTimeoutFuture = null;
1764 }
1765
1766 // Do the close once the we received the close_notify.
1767 sslClosePromise.addListener(new FutureListener<Channel>() {
1768 @Override
1769 public void operationComplete(Future<Channel> future) throws Exception {
1770 if (closeNotifyReadTimeoutFuture != null) {
1771 closeNotifyReadTimeoutFuture.cancel(false);
1772 }
1773 addCloseListener(ctx.close(ctx.newPromise()), promise);
1774 }
1775 });
1776 }
1777 }
1778 });
1779 }
1780
1781 private static void addCloseListener(ChannelFuture future, ChannelPromise promise) {
1782 // We notify the promise in the ChannelPromiseNotifier as there is a "race" where the close(...) call
1783 // by the timeoutFuture and the close call in the flushFuture listener will be called. Because of
1784 // this we need to use trySuccess() and tryFailure(...) as otherwise we can cause an
1785 // IllegalStateException.
1786 // Also we not want to log if the notification happens as this is expected in some cases.
1787 // See https://github.com/netty/netty/issues/5598
1788 future.addListener(new ChannelPromiseNotifier(false, promise));
1789 }
1790
1791 /**
1792 * Always prefer a direct buffer when it's pooled, so that we reduce the number of memory copies
1793 * in {@link OpenSslEngine}.
1794 */
1795 private ByteBuf allocate(ChannelHandlerContext ctx, int capacity) {
1796 ByteBufAllocator alloc = ctx.alloc();
1797 if (engineType.wantsDirectBuffer) {
1798 return alloc.directBuffer(capacity);
1799 } else {
1800 return alloc.buffer(capacity);
1801 }
1802 }
1803
1804 /**
1805 * Allocates an outbound network buffer for {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} which can encrypt
1806 * the specified amount of pending bytes.
1807 */
1808 private ByteBuf allocateOutNetBuf(ChannelHandlerContext ctx, int pendingBytes, int numComponents) {
1809 return allocate(ctx, engineType.calculateWrapBufferCapacity(this, pendingBytes, numComponents));
1810 }
1811
1812 private final class LazyChannelPromise extends DefaultPromise<Channel> {
1813
1814 @Override
1815 protected EventExecutor executor() {
1816 if (ctx == null) {
1817 throw new IllegalStateException();
1818 }
1819 return ctx.executor();
1820 }
1821
1822 @Override
1823 protected void checkDeadLock() {
1824 if (ctx == null) {
1825 // If ctx is null the handlerAdded(...) callback was not called, in this case the checkDeadLock()
1826 // method was called from another Thread then the one that is used by ctx.executor(). We need to
1827 // guard against this as a user can see a race if handshakeFuture().sync() is called but the
1828 // handlerAdded(..) method was not yet as it is called from the EventExecutor of the
1829 // ChannelHandlerContext. If we not guard against this super.checkDeadLock() would cause an
1830 // IllegalStateException when trying to call executor().
1831 return;
1832 }
1833 super.checkDeadLock();
1834 }
1835 }
1836 }