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 org.jboss.netty.handler.ssl;
17
18 import org.jboss.netty.buffer.ChannelBuffer;
19 import org.jboss.netty.buffer.ChannelBufferFactory;
20 import org.jboss.netty.buffer.ChannelBuffers;
21 import org.jboss.netty.channel.Channel;
22 import org.jboss.netty.channel.ChannelDownstreamHandler;
23 import org.jboss.netty.channel.ChannelEvent;
24 import org.jboss.netty.channel.ChannelFuture;
25 import org.jboss.netty.channel.ChannelFutureListener;
26 import org.jboss.netty.channel.ChannelHandlerContext;
27 import org.jboss.netty.channel.ChannelPipeline;
28 import org.jboss.netty.channel.ChannelStateEvent;
29 import org.jboss.netty.channel.Channels;
30 import org.jboss.netty.channel.DefaultChannelFuture;
31 import org.jboss.netty.channel.DownstreamMessageEvent;
32 import org.jboss.netty.channel.ExceptionEvent;
33 import org.jboss.netty.channel.MessageEvent;
34 import org.jboss.netty.handler.codec.frame.FrameDecoder;
35 import org.jboss.netty.logging.InternalLogger;
36 import org.jboss.netty.logging.InternalLoggerFactory;
37 import org.jboss.netty.util.Timeout;
38 import org.jboss.netty.util.Timer;
39 import org.jboss.netty.util.TimerTask;
40 import org.jboss.netty.util.internal.DetectionUtil;
41 import org.jboss.netty.util.internal.NonReentrantLock;
42
43 import javax.net.ssl.SSLEngine;
44 import javax.net.ssl.SSLEngineResult;
45 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
46 import javax.net.ssl.SSLEngineResult.Status;
47 import javax.net.ssl.SSLException;
48 import java.io.IOException;
49 import java.nio.ByteBuffer;
50 import java.nio.channels.ClosedChannelException;
51 import java.nio.channels.DatagramChannel;
52 import java.nio.channels.SocketChannel;
53 import java.util.ArrayList;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Queue;
57 import java.util.concurrent.ConcurrentLinkedQueue;
58 import java.util.concurrent.TimeUnit;
59 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
60 import java.util.regex.Pattern;
61
62 import static org.jboss.netty.channel.Channels.*;
63
64 /**
65 * Adds <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">SSL
66 * · TLS</a> and StartTLS support to a {@link Channel}. Please refer
67 * to the <strong>"SecureChat"</strong> example in the distribution or the web
68 * site for the detailed usage.
69 *
70 * <h3>Beginning the handshake</h3>
71 * <p>
72 * You must make sure not to write a message while the
73 * {@linkplain #handshake() handshake} is in progress unless you are
74 * renegotiating. You will be notified by the {@link ChannelFuture} which is
75 * returned by the {@link #handshake()} method when the handshake
76 * process succeeds or fails.
77 *
78 * <h3>Handshake</h3>
79 * <p>
80 * If {@link #isIssueHandshake()} is {@code false}
81 * (default) you will need to take care of calling {@link #handshake()} by your own. In most
82 * situations were {@link SslHandler} is used in 'client mode' you want to issue a handshake once
83 * the connection was established. if {@link #setIssueHandshake(boolean)} is set to {@code true}
84 * you don't need to worry about this as the {@link SslHandler} will take care of it.
85 * <p>
86 *
87 * <h3>Renegotiation</h3>
88 * <p>
89 * If {@link #isEnableRenegotiation() enableRenegotiation} is {@code true}
90 * (default) and the initial handshake has been done successfully, you can call
91 * {@link #handshake()} to trigger the renegotiation.
92 * <p>
93 * If {@link #isEnableRenegotiation() enableRenegotiation} is {@code false},
94 * an attempt to trigger renegotiation will result in the connection closure.
95 * <p>
96 * Please note that TLS renegotiation had a security issue before. If your
97 * runtime environment did not fix it, please make sure to disable TLS
98 * renegotiation by calling {@link #setEnableRenegotiation(boolean)} with
99 * {@code false}. For more information, please refer to the following documents:
100 * <ul>
101 * <li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555">CVE-2009-3555</a></li>
102 * <li><a href="http://www.ietf.org/rfc/rfc5746.txt">RFC5746</a></li>
103 * <li><a href="http://www.oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html">Phased
104 * Approach to Fixing the TLS Renegotiation Issue</a></li>
105 * </ul>
106 *
107 * <h3>Closing the session</h3>
108 * <p>
109 * To close the SSL session, the {@link #close()} method should be
110 * called to send the {@code close_notify} message to the remote peer. One
111 * exception is when you close the {@link Channel} - {@link SslHandler}
112 * intercepts the close request and send the {@code close_notify} message
113 * before the channel closure automatically. Once the SSL session is closed,
114 * it is not reusable, and consequently you should create a new
115 * {@link SslHandler} with a new {@link SSLEngine} as explained in the
116 * following section.
117 *
118 * <h3>Restarting the session</h3>
119 * <p>
120 * To restart the SSL session, you must remove the existing closed
121 * {@link SslHandler} from the {@link ChannelPipeline}, insert a new
122 * {@link SslHandler} with a new {@link SSLEngine} into the pipeline,
123 * and start the handshake process as described in the first section.
124 *
125 * <h3>Implementing StartTLS</h3>
126 * <p>
127 * <a href="http://en.wikipedia.org/wiki/STARTTLS">StartTLS</a> is the
128 * communication pattern that secures the wire in the middle of the plaintext
129 * connection. Please note that it is different from SSL · TLS, that
130 * secures the wire from the beginning of the connection. Typically, StartTLS
131 * is composed of three steps:
132 * <ol>
133 * <li>Client sends a StartTLS request to server.</li>
134 * <li>Server sends a StartTLS response to client.</li>
135 * <li>Client begins SSL handshake.</li>
136 * </ol>
137 * If you implement a server, you need to:
138 * <ol>
139 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
140 * to {@code true},</li>
141 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
142 * <li>write a StartTLS response.</li>
143 * </ol>
144 * Please note that you must insert {@link SslHandler} <em>before</em> sending
145 * the StartTLS response. Otherwise the client can send begin SSL handshake
146 * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing
147 * data corruption.
148 * <p>
149 * The client-side implementation is much simpler.
150 * <ol>
151 * <li>Write a StartTLS request,</li>
152 * <li>wait for the StartTLS response,</li>
153 * <li>create a new {@link SslHandler} instance with {@code startTls} flag set
154 * to {@code false},</li>
155 * <li>insert the {@link SslHandler} to the {@link ChannelPipeline}, and</li>
156 * <li>Initiate SSL handshake by calling {@link SslHandler#handshake()}.</li>
157 * </ol>
158 *
159 * <h3>Known issues</h3>
160 * <p>
161 * Because of a known issue with the current implementation of the SslEngine that comes
162 * with Java it may be possible that you see blocked IO-Threads while a full GC is done.
163 * <p>
164 * So if you are affected you can workaround this problem by adjust the cache settings
165 * like shown below:
166 *
167 * <pre>
168 * SslContext context = ...;
169 * context.getServerSessionContext().setSessionCacheSize(someSaneSize);
170 * context.getServerSessionContext().setSessionTime(someSameTimeout);
171 * </pre>
172 * <p>
173 * What values to use here depends on the nature of your application and should be set
174 * based on monitoring and debugging of it.
175 * For more details see
176 * <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
177 * @apiviz.landmark
178 * @apiviz.uses org.jboss.netty.handler.ssl.SslBufferPool
179 */
180 public class SslHandler extends FrameDecoder
181 implements ChannelDownstreamHandler {
182
183 private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslHandler.class);
184
185 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
186
187 private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
188 "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
189 private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
190 "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
191
192 private static SslBufferPool defaultBufferPool;
193
194 /**
195 * Returns the default {@link SslBufferPool} used when no pool is
196 * specified in the constructor.
197 */
198 public static synchronized SslBufferPool getDefaultBufferPool() {
199 if (defaultBufferPool == null) {
200 defaultBufferPool = new SslBufferPool();
201 }
202 return defaultBufferPool;
203 }
204
205 private volatile ChannelHandlerContext ctx;
206 private final SSLEngine engine;
207 private final SslBufferPool bufferPool;
208 private final boolean startTls;
209
210 private volatile boolean enableRenegotiation = true;
211
212 final Object handshakeLock = new Object();
213 private boolean handshaking;
214 private volatile boolean handshaken;
215 private volatile ChannelFuture handshakeFuture;
216
217 @SuppressWarnings("UnusedDeclaration")
218 private volatile int sentFirstMessage;
219 @SuppressWarnings("UnusedDeclaration")
220 private volatile int sentCloseNotify;
221 @SuppressWarnings("UnusedDeclaration")
222 private volatile int closedOutboundAndChannel;
223
224 private static final AtomicIntegerFieldUpdater<SslHandler> SENT_FIRST_MESSAGE_UPDATER =
225 AtomicIntegerFieldUpdater.newUpdater(SslHandler.class, "sentFirstMessage");
226 private static final AtomicIntegerFieldUpdater<SslHandler> SENT_CLOSE_NOTIFY_UPDATER =
227 AtomicIntegerFieldUpdater.newUpdater(SslHandler.class, "sentCloseNotify");
228 private static final AtomicIntegerFieldUpdater<SslHandler> CLOSED_OUTBOUND_AND_CHANNEL_UPDATER =
229 AtomicIntegerFieldUpdater.newUpdater(SslHandler.class, "closedOutboundAndChannel");
230
231 int ignoreClosedChannelException;
232 final Object ignoreClosedChannelExceptionLock = new Object();
233 private final Queue<PendingWrite> pendingUnencryptedWrites = new LinkedList<PendingWrite>();
234 private final NonReentrantLock pendingUnencryptedWritesLock = new NonReentrantLock();
235 private final Queue<MessageEvent> pendingEncryptedWrites = new ConcurrentLinkedQueue<MessageEvent>();
236 private final NonReentrantLock pendingEncryptedWritesLock = new NonReentrantLock();
237
238 private volatile boolean issueHandshake;
239 private volatile boolean writeBeforeHandshakeDone;
240 private final SSLEngineInboundCloseFuture sslEngineCloseFuture = new SSLEngineInboundCloseFuture();
241
242 private boolean closeOnSslException;
243
244 private int packetLength;
245
246 private final Timer timer;
247 private final long handshakeTimeoutInMillis;
248 private Timeout handshakeTimeout;
249
250 /**
251 * Creates a new instance.
252 *
253 * @param engine the {@link SSLEngine} this handler will use
254 */
255 public SslHandler(SSLEngine engine) {
256 this(engine, getDefaultBufferPool(), false, null, 0);
257 }
258
259 /**
260 * Creates a new instance.
261 *
262 * @param engine the {@link SSLEngine} this handler will use
263 * @param bufferPool the {@link SslBufferPool} where this handler will
264 * acquire the buffers required by the {@link SSLEngine}
265 */
266 public SslHandler(SSLEngine engine, SslBufferPool bufferPool) {
267 this(engine, bufferPool, false, null, 0);
268 }
269
270 /**
271 * Creates a new instance.
272 *
273 * @param engine the {@link SSLEngine} this handler will use
274 * @param startTls {@code true} if the first write request shouldn't be
275 * encrypted by the {@link SSLEngine}
276 */
277 public SslHandler(SSLEngine engine, boolean startTls) {
278 this(engine, getDefaultBufferPool(), startTls);
279 }
280
281 /**
282 * Creates a new instance.
283 *
284 * @param engine the {@link SSLEngine} this handler will use
285 * @param bufferPool the {@link SslBufferPool} where this handler will
286 * acquire the buffers required by the {@link SSLEngine}
287 * @param startTls {@code true} if the first write request shouldn't be
288 * encrypted by the {@link SSLEngine}
289 */
290 public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls) {
291 this(engine, bufferPool, startTls, null, 0);
292 }
293
294 /**
295 * Creates a new instance.
296 *
297 * @param engine
298 * the {@link SSLEngine} this handler will use
299 * @param bufferPool
300 * the {@link SslBufferPool} where this handler will acquire
301 * the buffers required by the {@link SSLEngine}
302 * @param startTls
303 * {@code true} if the first write request shouldn't be encrypted
304 * by the {@link SSLEngine}
305 * @param timer
306 * the {@link Timer} which will be used to process the timeout of the {@link #handshake()}.
307 * Be aware that the given {@link Timer} will not get stopped automaticly, so it is up to you to cleanup
308 * once you not need it anymore
309 * @param handshakeTimeoutInMillis
310 * the time in milliseconds after whic the {@link #handshake()} will be failed, and so the future notified
311 */
312 public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls, Timer timer,
313 long handshakeTimeoutInMillis) {
314 if (engine == null) {
315 throw new NullPointerException("engine");
316 }
317 if (bufferPool == null) {
318 throw new NullPointerException("bufferPool");
319 }
320 if (timer == null && handshakeTimeoutInMillis > 0) {
321 throw new IllegalArgumentException("No Timer was given but a handshakeTimeoutInMillis, need both or none");
322 }
323
324 this.engine = engine;
325 this.bufferPool = bufferPool;
326 this.startTls = startTls;
327 this.timer = timer;
328 this.handshakeTimeoutInMillis = handshakeTimeoutInMillis;
329 }
330
331 /**
332 * Returns the {@link SSLEngine} which is used by this handler.
333 */
334 public SSLEngine getEngine() {
335 return engine;
336 }
337
338 /**
339 * Starts an SSL / TLS handshake for the specified channel.
340 *
341 * @return a {@link ChannelFuture} which is notified when the handshake
342 * succeeds or fails.
343 */
344 public ChannelFuture handshake() {
345 synchronized (handshakeLock) {
346 if (handshaken && !isEnableRenegotiation()) {
347 throw new IllegalStateException("renegotiation disabled");
348 }
349
350 final ChannelHandlerContext ctx = this.ctx;
351 final Channel channel = ctx.getChannel();
352 ChannelFuture handshakeFuture;
353 Exception exception = null;
354
355 if (handshaking) {
356 return this.handshakeFuture;
357 }
358
359 handshaking = true;
360 try {
361 engine.beginHandshake();
362 runDelegatedTasks();
363 handshakeFuture = this.handshakeFuture = future(channel);
364 if (handshakeTimeoutInMillis > 0) {
365 handshakeTimeout = timer.newTimeout(new TimerTask() {
366 public void run(Timeout timeout) throws Exception {
367 ChannelFuture future = SslHandler.this.handshakeFuture;
368 if (future != null && future.isDone()) {
369 return;
370 }
371
372 setHandshakeFailure(channel, new SSLException("Handshake did not complete within " +
373 handshakeTimeoutInMillis + "ms"));
374 }
375 }, handshakeTimeoutInMillis, TimeUnit.MILLISECONDS);
376 }
377 } catch (Exception e) {
378 handshakeFuture = this.handshakeFuture = failedFuture(channel, e);
379 exception = e;
380 }
381
382 if (exception == null) { // Began handshake successfully.
383 try {
384 final ChannelFuture hsFuture = handshakeFuture;
385 wrapNonAppData(ctx, channel).addListener(new ChannelFutureListener() {
386 public void operationComplete(ChannelFuture future) throws Exception {
387 if (!future.isSuccess()) {
388 Throwable cause = future.getCause();
389 hsFuture.setFailure(cause);
390
391 fireExceptionCaught(ctx, cause);
392 if (closeOnSslException) {
393 Channels.close(ctx, future(channel));
394 }
395 }
396 }
397 });
398 } catch (SSLException e) {
399 handshakeFuture.setFailure(e);
400
401 fireExceptionCaught(ctx, e);
402 if (closeOnSslException) {
403 Channels.close(ctx, future(channel));
404 }
405 }
406 } else { // Failed to initiate handshake.
407 fireExceptionCaught(ctx, exception);
408 if (closeOnSslException) {
409 Channels.close(ctx, future(channel));
410 }
411 }
412 return handshakeFuture;
413 }
414 }
415
416 /**
417 * Sends an SSL {@code close_notify} message to the specified channel and
418 * destroys the underlying {@link SSLEngine}.
419 */
420 public ChannelFuture close() {
421 ChannelHandlerContext ctx = this.ctx;
422 Channel channel = ctx.getChannel();
423 try {
424 engine.closeOutbound();
425 return wrapNonAppData(ctx, channel);
426 } catch (SSLException e) {
427 fireExceptionCaught(ctx, e);
428 if (closeOnSslException) {
429 Channels.close(ctx, future(channel));
430 }
431 return failedFuture(channel, e);
432 }
433 }
434
435 /**
436 * Returns {@code true} if and only if TLS renegotiation is enabled.
437 */
438 public boolean isEnableRenegotiation() {
439 return enableRenegotiation;
440 }
441
442 /**
443 * Enables or disables TLS renegotiation.
444 */
445 public void setEnableRenegotiation(boolean enableRenegotiation) {
446 this.enableRenegotiation = enableRenegotiation;
447 }
448
449 /**
450 * Enables or disables the automatic handshake once the {@link Channel} is
451 * connected. The value will only have affect if its set before the
452 * {@link Channel} is connected.
453 */
454 public void setIssueHandshake(boolean issueHandshake) {
455 this.issueHandshake = issueHandshake;
456 }
457
458 /**
459 * Returns {@code true} if the automatic handshake is enabled
460 */
461 public boolean isIssueHandshake() {
462 return issueHandshake;
463 }
464
465 /**
466 * Return the {@link ChannelFuture} that will get notified if the inbound of the {@link SSLEngine} will get closed.
467 *
468 * This method will return the same {@link ChannelFuture} all the time.
469 *
470 * For more informations see the apidocs of {@link SSLEngine}
471 *
472 */
473 public ChannelFuture getSSLEngineInboundCloseFuture() {
474 return sslEngineCloseFuture;
475 }
476
477 /**
478 * Return the timeout (in ms) after which the {@link ChannelFuture} of {@link #handshake()} will be failed, while
479 * a handshake is in progress
480 */
481 public long getHandshakeTimeout() {
482 return handshakeTimeoutInMillis;
483 }
484
485 /**
486 * If set to {@code true}, the {@link Channel} will automatically get closed
487 * one a {@link SSLException} was caught. This is most times what you want, as after this
488 * its almost impossible to recover.
489 *
490 * Anyway the default is {@code false} to not break compatibility with older releases. This
491 * will be changed to {@code true} in the next major release.
492 *
493 */
494 public void setCloseOnSSLException(boolean closeOnSslException) {
495 if (ctx != null) {
496 throw new IllegalStateException("Can only get changed before attached to ChannelPipeline");
497 }
498 this.closeOnSslException = closeOnSslException;
499 }
500
501 public boolean getCloseOnSSLException() {
502 return closeOnSslException;
503 }
504
505 public void handleDownstream(
506 final ChannelHandlerContext context, final ChannelEvent evt) throws Exception {
507 if (evt instanceof ChannelStateEvent) {
508 ChannelStateEvent e = (ChannelStateEvent) evt;
509 switch (e.getState()) {
510 case OPEN:
511 case CONNECTED:
512 case BOUND:
513 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
514 closeOutboundAndChannel(context, e);
515 return;
516 }
517 }
518 }
519 if (!(evt instanceof MessageEvent)) {
520 context.sendDownstream(evt);
521 return;
522 }
523
524 MessageEvent e = (MessageEvent) evt;
525 if (!(e.getMessage() instanceof ChannelBuffer)) {
526 context.sendDownstream(evt);
527 return;
528 }
529
530 // Do not encrypt the first write request if this handler is
531 // created with startTLS flag turned on.
532 if (startTls && SENT_FIRST_MESSAGE_UPDATER.compareAndSet(this, 0, 1)) {
533 context.sendDownstream(evt);
534 return;
535 }
536
537 // Otherwise, all messages are encrypted.
538 ChannelBuffer msg = (ChannelBuffer) e.getMessage();
539 PendingWrite pendingWrite;
540
541 if (msg.readable()) {
542 pendingWrite = new PendingWrite(evt.getFuture(), msg.toByteBuffer(msg.readerIndex(), msg.readableBytes()));
543 } else {
544 pendingWrite = new PendingWrite(evt.getFuture(), null);
545 }
546
547 pendingUnencryptedWritesLock.lock();
548 try {
549 pendingUnencryptedWrites.add(pendingWrite);
550 } finally {
551 pendingUnencryptedWritesLock.unlock();
552 }
553
554 if (handshakeFuture == null || !handshakeFuture.isDone()) {
555 writeBeforeHandshakeDone = true;
556 }
557 wrap(context, evt.getChannel());
558 }
559
560 private void cancelHandshakeTimeout() {
561 if (handshakeTimeout != null) {
562 // cancel the task as we will fail the handshake future now
563 handshakeTimeout.cancel();
564 }
565 }
566
567 @Override
568 public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
569
570 // Make sure the handshake future is notified when a connection has
571 // been closed during handshake.
572 synchronized (handshakeLock) {
573 if (handshaking) {
574 cancelHandshakeTimeout();
575 handshakeFuture.setFailure(new ClosedChannelException());
576 }
577 }
578
579 try {
580 super.channelDisconnected(ctx, e);
581 } finally {
582 unwrapNonAppData(ctx, e.getChannel(), false);
583 closeEngine();
584 }
585 }
586
587 private void closeEngine() {
588 engine.closeOutbound();
589 if (sentCloseNotify == 0 && handshaken) {
590 try {
591 engine.closeInbound();
592 } catch (SSLException ex) {
593 if (logger.isDebugEnabled()) {
594 logger.debug("Failed to clean up SSLEngine.", ex);
595 }
596 }
597 }
598 }
599
600 @Override
601 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
602 throws Exception {
603
604 Throwable cause = e.getCause();
605 if (cause instanceof IOException) {
606 if (cause instanceof ClosedChannelException) {
607 synchronized (ignoreClosedChannelExceptionLock) {
608 if (ignoreClosedChannelException > 0) {
609 ignoreClosedChannelException --;
610 if (logger.isDebugEnabled()) {
611 logger.debug(
612 "Swallowing an exception raised while " +
613 "writing non-app data", cause);
614 }
615
616 return;
617 }
618 }
619 } else {
620 if (ignoreException(cause)) {
621 return;
622 }
623 }
624 }
625
626 ctx.sendUpstream(e);
627 }
628
629 /**
630 * Checks if the given {@link Throwable} can be ignore and just "swallowed"
631 *
632 * When an ssl connection is closed a close_notify message is sent.
633 * After that the peer also sends close_notify however, it's not mandatory to receive
634 * the close_notify. The party who sent the initial close_notify can close the connection immediately
635 * then the peer will get connection reset error.
636 *
637 */
638 private boolean ignoreException(Throwable t) {
639 if (!(t instanceof SSLException) && t instanceof IOException && engine.isOutboundDone()) {
640 String message = String.valueOf(t.getMessage()).toLowerCase();
641
642 // first try to match connection reset / broke peer based on the regex. This is the fastest way
643 // but may fail on different jdk impls or OS's
644 if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
645 return true;
646 }
647
648 // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
649 StackTraceElement[] elements = t.getStackTrace();
650 for (StackTraceElement element: elements) {
651 String classname = element.getClassName();
652 String methodname = element.getMethodName();
653
654 // skip all classes that belong to the io.netty package
655 if (classname.startsWith("org.jboss.netty.")) {
656 continue;
657 }
658
659 // check if the method name is read if not skip it
660 if (!"read".equals(methodname)) {
661 continue;
662 }
663
664 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
665 // also others
666 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
667 return true;
668 }
669
670 try {
671 // No match by now.. Try to load the class via classloader and inspect it.
672 // This is mainly done as other JDK implementations may differ in name of
673 // the impl.
674 Class<?> clazz = getClass().getClassLoader().loadClass(classname);
675
676 if (SocketChannel.class.isAssignableFrom(clazz)
677 || DatagramChannel.class.isAssignableFrom(clazz)) {
678 return true;
679 }
680
681 // also match against SctpChannel via String matching as it may not present.
682 if (DetectionUtil.javaVersion() >= 7
683 && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
684 return true;
685 }
686 } catch (ClassNotFoundException e) {
687 // This should not happen just ignore
688 }
689 }
690 }
691
692 return false;
693 }
694
695 /**
696 * Returns {@code true} if the given {@link ChannelBuffer} is encrypted. Be aware that this method
697 * will not increase the readerIndex of the given {@link ChannelBuffer}.
698 *
699 * @param buffer
700 * The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
701 * otherwise it will throw an {@link IllegalArgumentException}.
702 * @return encrypted
703 * {@code true} if the {@link ChannelBuffer} is encrypted, {@code false} otherwise.
704 * @throws IllegalArgumentException
705 * Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
706 */
707 public static boolean isEncrypted(ChannelBuffer buffer) {
708 return getEncryptedPacketLength(buffer, buffer.readerIndex()) != -1;
709 }
710
711 /**
712 * Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
713 * the readerIndex of the given {@link ChannelBuffer}.
714 *
715 * @param buffer
716 * The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
717 * otherwise it will throw an {@link IllegalArgumentException}.
718 * @return length
719 * The length of the encrypted packet that is included in the buffer. This will
720 * return {@code -1} if the given {@link ChannelBuffer} is not encrypted at all.
721 * @throws IllegalArgumentException
722 * Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
723 */
724 private static int getEncryptedPacketLength(ChannelBuffer buffer, int offset) {
725 int packetLength = 0;
726
727 // SSLv3 or TLS - Check ContentType
728 boolean tls;
729 switch (buffer.getUnsignedByte(offset)) {
730 case 20: // change_cipher_spec
731 case 21: // alert
732 case 22: // handshake
733 case 23: // application_data
734 tls = true;
735 break;
736 default:
737 // SSLv2 or bad data
738 tls = false;
739 }
740
741 if (tls) {
742 // SSLv3 or TLS - Check ProtocolVersion
743 int majorVersion = buffer.getUnsignedByte(offset + 1);
744 if (majorVersion == 3) {
745 // SSLv3 or TLS
746 packetLength = (getShort(buffer, offset + 3) & 0xFFFF) + 5;
747 if (packetLength <= 5) {
748 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
749 tls = false;
750 }
751 } else {
752 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
753 tls = false;
754 }
755 }
756
757 if (!tls) {
758 // SSLv2 or bad data - Check the version
759 boolean sslv2 = true;
760 int headerLength = (buffer.getUnsignedByte(offset) & 0x80) != 0 ? 2 : 3;
761 int majorVersion = buffer.getUnsignedByte(offset + headerLength + 1);
762 if (majorVersion == 2 || majorVersion == 3) {
763 // SSLv2
764 if (headerLength == 2) {
765 packetLength = (getShort(buffer, offset) & 0x7FFF) + 2;
766 } else {
767 packetLength = (getShort(buffer, offset) & 0x3FFF) + 3;
768 }
769 if (packetLength <= headerLength) {
770 sslv2 = false;
771 }
772 } else {
773 sslv2 = false;
774 }
775
776 if (!sslv2) {
777 return -1;
778 }
779 }
780 return packetLength;
781 }
782
783 @Override
784 protected Object decode(
785 final ChannelHandlerContext ctx, Channel channel, ChannelBuffer in) throws Exception {
786
787 final int startOffset = in.readerIndex();
788 final int endOffset = in.writerIndex();
789 int offset = startOffset;
790 int totalLength = 0;
791
792 // If we calculated the length of the current SSL record before, use that information.
793 if (packetLength > 0) {
794 if (endOffset - startOffset < packetLength) {
795 return null;
796 } else {
797 offset += packetLength;
798 totalLength = packetLength;
799 packetLength = 0;
800 }
801 }
802
803 boolean nonSslRecord = false;
804
805 while (totalLength < OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
806 final int readableBytes = endOffset - offset;
807 if (readableBytes < 5) {
808 break;
809 }
810
811 final int packetLength = getEncryptedPacketLength(in, offset);
812 if (packetLength == -1) {
813 nonSslRecord = true;
814 break;
815 }
816
817 assert packetLength > 0;
818
819 if (packetLength > readableBytes) {
820 // wait until the whole packet can be read
821 this.packetLength = packetLength;
822 break;
823 }
824
825 int newTotalLength = totalLength + packetLength;
826 if (newTotalLength > OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
827 // Don't read too much.
828 break;
829 }
830
831 // We have a whole packet.
832 // Increment the offset to handle the next packet.
833 offset += packetLength;
834 totalLength = newTotalLength;
835 }
836
837 ChannelBuffer unwrapped = null;
838 if (totalLength > 0) {
839 // The buffer contains one or more full SSL records.
840 // Slice out the whole packet so unwrap will only be called with complete packets.
841 // Also directly reset the packetLength. This is needed as unwrap(..) may trigger
842 // decode(...) again via:
843 // 1) unwrap(..) is called
844 // 2) wrap(...) is called from within unwrap(...)
845 // 3) wrap(...) calls unwrapLater(...)
846 // 4) unwrapLater(...) calls decode(...)
847 //
848 // See https://github.com/netty/netty/issues/1534
849
850 in.skipBytes(totalLength);
851 final ByteBuffer inNetBuf = in.toByteBuffer(startOffset, totalLength);
852 unwrapped = unwrap(ctx, channel, inNetBuf, totalLength, true);
853 }
854
855 if (nonSslRecord) {
856 // Not an SSL/TLS packet
857 NotSslRecordException e = new NotSslRecordException(
858 "not an SSL/TLS record: " + ChannelBuffers.hexDump(in));
859 in.skipBytes(in.readableBytes());
860 if (closeOnSslException) {
861 // first trigger the exception and then close the channel
862 fireExceptionCaught(ctx, e);
863 Channels.close(ctx, future(channel));
864
865 // just return null as we closed the channel before, that
866 // will take care of cleanup etc
867 return null;
868 } else {
869 throw e;
870 }
871 }
872
873 return unwrapped;
874 }
875
876 /**
877 * Reads a big-endian short integer from the buffer. Please note that we do not use
878 * {@link ChannelBuffer#getShort(int)} because it might be a little-endian buffer.
879 */
880 private static short getShort(ChannelBuffer buf, int offset) {
881 return (short) (buf.getByte(offset) << 8 | buf.getByte(offset + 1) & 0xFF);
882 }
883
884 private void wrap(ChannelHandlerContext context, Channel channel) throws SSLException {
885 ChannelBuffer msg;
886 ByteBuffer outNetBuf = bufferPool.acquireBuffer();
887 boolean success = true;
888 boolean offered = false;
889 boolean needsUnwrap = false;
890 PendingWrite pendingWrite = null;
891
892 try {
893 loop:
894 for (;;) {
895 // Acquire a lock to make sure unencrypted data is polled
896 // in order and their encrypted counterpart is offered in
897 // order.
898 pendingUnencryptedWritesLock.lock();
899 try {
900 pendingWrite = pendingUnencryptedWrites.peek();
901 if (pendingWrite == null) {
902 break;
903 }
904
905 ByteBuffer outAppBuf = pendingWrite.outAppBuf;
906 if (outAppBuf == null) {
907 // A write request with an empty buffer
908 pendingUnencryptedWrites.remove();
909 offerEncryptedWriteRequest(
910 new DownstreamMessageEvent(
911 channel, pendingWrite.future,
912 ChannelBuffers.EMPTY_BUFFER,
913 channel.getRemoteAddress()));
914 offered = true;
915 } else {
916 synchronized (handshakeLock) {
917 SSLEngineResult result = null;
918 try {
919 result = engine.wrap(outAppBuf, outNetBuf);
920 } finally {
921 if (!outAppBuf.hasRemaining()) {
922 pendingUnencryptedWrites.remove();
923 }
924 }
925
926 if (result.bytesProduced() > 0) {
927 outNetBuf.flip();
928 int remaining = outNetBuf.remaining();
929 msg = ctx.getChannel().getConfig().getBufferFactory().getBuffer(remaining);
930
931 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
932 // work with "non" heap buffers
933 //
934 // See https://github.com/netty/netty/issues/329
935 msg.writeBytes(outNetBuf);
936 outNetBuf.clear();
937
938 ChannelFuture future;
939 if (pendingWrite.outAppBuf.hasRemaining()) {
940 // pendingWrite's future shouldn't be notified if
941 // only partial data is written.
942 future = succeededFuture(channel);
943 } else {
944 future = pendingWrite.future;
945 }
946
947 MessageEvent encryptedWrite = new DownstreamMessageEvent(
948 channel, future, msg, channel.getRemoteAddress());
949 offerEncryptedWriteRequest(encryptedWrite);
950 offered = true;
951 } else if (result.getStatus() == Status.CLOSED) {
952 // SSLEngine has been closed already.
953 // Any further write attempts should be denied.
954 success = false;
955 break;
956 } else {
957 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
958 handleRenegotiation(handshakeStatus);
959 switch (handshakeStatus) {
960 case NEED_WRAP:
961 if (outAppBuf.hasRemaining()) {
962 break;
963 } else {
964 break loop;
965 }
966 case NEED_UNWRAP:
967 needsUnwrap = true;
968 break loop;
969 case NEED_TASK:
970 runDelegatedTasks();
971 break;
972 case FINISHED:
973 setHandshakeSuccess(channel);
974 if (result.getStatus() == Status.CLOSED) {
975 success = false;
976 }
977 break loop;
978 case NOT_HANDSHAKING:
979 setHandshakeSuccessIfStillHandshaking(channel);
980 if (result.getStatus() == Status.CLOSED) {
981 success = false;
982 }
983 break loop;
984 default:
985 throw new IllegalStateException(
986 "Unknown handshake status: " +
987 handshakeStatus);
988 }
989 }
990 }
991 }
992 } finally {
993 pendingUnencryptedWritesLock.unlock();
994 }
995 }
996 } catch (SSLException e) {
997 success = false;
998 setHandshakeFailure(channel, e);
999 throw e;
1000 } finally {
1001 bufferPool.releaseBuffer(outNetBuf);
1002
1003 if (offered) {
1004 flushPendingEncryptedWrites(context);
1005 }
1006
1007 if (!success) {
1008 IllegalStateException cause =
1009 new IllegalStateException("SSLEngine already closed");
1010
1011 // Check if we had a pendingWrite in process, if so we need to also notify as otherwise
1012 // the ChannelFuture will never get notified
1013 if (pendingWrite != null) {
1014 pendingWrite.future.setFailure(cause);
1015 }
1016
1017 // Mark all remaining pending writes as failure if anything
1018 // wrong happened before the write requests are wrapped.
1019 // Please note that we do not call setFailure while a lock is
1020 // acquired, to avoid a potential dead lock.
1021 for (;;) {
1022 pendingUnencryptedWritesLock.lock();
1023 try {
1024 pendingWrite = pendingUnencryptedWrites.poll();
1025 if (pendingWrite == null) {
1026 break;
1027 }
1028 } finally {
1029 pendingUnencryptedWritesLock.unlock();
1030 }
1031
1032 pendingWrite.future.setFailure(cause);
1033 }
1034 }
1035 }
1036
1037 if (needsUnwrap) {
1038 unwrapNonAppData(ctx, channel, true);
1039 }
1040 }
1041
1042 private void offerEncryptedWriteRequest(MessageEvent encryptedWrite) {
1043 final boolean locked = pendingEncryptedWritesLock.tryLock();
1044 try {
1045 pendingEncryptedWrites.add(encryptedWrite);
1046 } finally {
1047 if (locked) {
1048 pendingEncryptedWritesLock.unlock();
1049 }
1050 }
1051 }
1052
1053 private void flushPendingEncryptedWrites(ChannelHandlerContext ctx) {
1054 while (!pendingEncryptedWrites.isEmpty()) {
1055 // Avoid possible dead lock and data integrity issue
1056 // which is caused by cross communication between more than one channel
1057 // in the same VM.
1058 if (!pendingEncryptedWritesLock.tryLock()) {
1059 return;
1060 }
1061
1062 try {
1063 MessageEvent e;
1064 while ((e = pendingEncryptedWrites.poll()) != null) {
1065 ctx.sendDownstream(e);
1066 }
1067 } finally {
1068 pendingEncryptedWritesLock.unlock();
1069 }
1070
1071 // Other thread might have added more elements at this point, so we loop again if the queue got unempty.
1072 }
1073 }
1074
1075 private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel) throws SSLException {
1076 ChannelFuture future = null;
1077 ByteBuffer outNetBuf = bufferPool.acquireBuffer();
1078
1079 SSLEngineResult result;
1080 try {
1081 for (;;) {
1082 synchronized (handshakeLock) {
1083 result = engine.wrap(EMPTY_BUFFER, outNetBuf);
1084 }
1085
1086 if (result.bytesProduced() > 0) {
1087 outNetBuf.flip();
1088 ChannelBuffer msg =
1089 ctx.getChannel().getConfig().getBufferFactory().getBuffer(outNetBuf.remaining());
1090
1091 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
1092 // work with "non" heap buffers
1093 //
1094 // See https://github.com/netty/netty/issues/329
1095 msg.writeBytes(outNetBuf);
1096 outNetBuf.clear();
1097
1098 future = future(channel);
1099 future.addListener(new ChannelFutureListener() {
1100 public void operationComplete(ChannelFuture future)
1101 throws Exception {
1102 if (future.getCause() instanceof ClosedChannelException) {
1103 synchronized (ignoreClosedChannelExceptionLock) {
1104 ignoreClosedChannelException ++;
1105 }
1106 }
1107 }
1108 });
1109
1110 write(ctx, future, msg);
1111 }
1112
1113 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1114 handleRenegotiation(handshakeStatus);
1115 switch (handshakeStatus) {
1116 case FINISHED:
1117 setHandshakeSuccess(channel);
1118 runDelegatedTasks();
1119 break;
1120 case NEED_TASK:
1121 runDelegatedTasks();
1122 break;
1123 case NEED_UNWRAP:
1124 if (!Thread.holdsLock(handshakeLock)) {
1125 // unwrap shouldn't be called when this method was
1126 // called by unwrap - unwrap will keep running after
1127 // this method returns.
1128 unwrapNonAppData(ctx, channel, true);
1129 }
1130 break;
1131 case NOT_HANDSHAKING:
1132 if (setHandshakeSuccessIfStillHandshaking(channel)) {
1133 runDelegatedTasks();
1134 }
1135 break;
1136 case NEED_WRAP:
1137 break;
1138 default:
1139 throw new IllegalStateException(
1140 "Unexpected handshake status: " + handshakeStatus);
1141 }
1142
1143 if (result.bytesProduced() == 0) {
1144 break;
1145 }
1146 }
1147 } catch (SSLException e) {
1148 setHandshakeFailure(channel, e);
1149 throw e;
1150 } finally {
1151 bufferPool.releaseBuffer(outNetBuf);
1152 }
1153
1154 if (future == null) {
1155 future = succeededFuture(channel);
1156 }
1157
1158 return future;
1159 }
1160
1161 /**
1162 * Calls {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} with an empty buffer to handle handshakes, etc.
1163 */
1164 private void unwrapNonAppData(
1165 ChannelHandlerContext ctx, Channel channel, boolean mightNeedHandshake) throws SSLException {
1166 unwrap(ctx, channel, EMPTY_BUFFER, -1, mightNeedHandshake);
1167 }
1168
1169 /**
1170 * Unwraps inbound SSL records.
1171 */
1172 private ChannelBuffer unwrap(
1173 ChannelHandlerContext ctx, Channel channel,
1174 ByteBuffer nioInNetBuf,
1175 int initialNettyOutAppBufCapacity, boolean mightNeedHandshake) throws SSLException {
1176
1177 final int nioInNetBufStartOffset = nioInNetBuf.position();
1178 final ByteBuffer nioOutAppBuf = bufferPool.acquireBuffer();
1179
1180 ChannelBuffer nettyOutAppBuf = null;
1181
1182 try {
1183 boolean needsWrap = false;
1184 for (;;) {
1185 SSLEngineResult result;
1186 boolean needsHandshake = false;
1187 if (mightNeedHandshake) {
1188 synchronized (handshakeLock) {
1189 if (!handshaken && !handshaking &&
1190 !engine.getUseClientMode() &&
1191 !engine.isInboundDone() && !engine.isOutboundDone()) {
1192 needsHandshake = true;
1193 }
1194 }
1195 }
1196
1197 if (needsHandshake) {
1198 handshake();
1199 }
1200
1201 synchronized (handshakeLock) {
1202 // Decrypt at least one record in the inbound network buffer.
1203 // It is impossible to consume no record here because we made sure the inbound network buffer
1204 // always contain at least one record in decode(). Therefore, if SSLEngine.unwrap() returns
1205 // BUFFER_OVERFLOW, it is always resolved by retrying after emptying the application buffer.
1206 for (;;) {
1207 final int outAppBufSize = engine.getSession().getApplicationBufferSize();
1208 final ByteBuffer outAppBuf;
1209 if (nioOutAppBuf.capacity() < outAppBufSize) {
1210 // SSLEngine wants a buffer larger than what the pool can provide.
1211 // Allocate a temporary heap buffer.
1212 outAppBuf = ByteBuffer.allocate(outAppBufSize);
1213 } else {
1214 outAppBuf = nioOutAppBuf;
1215 }
1216
1217 try {
1218 result = engine.unwrap(nioInNetBuf, outAppBuf);
1219 switch (result.getStatus()) {
1220 case CLOSED:
1221 // notify about the CLOSED state of the SSLEngine. See #137
1222 sslEngineCloseFuture.setClosed();
1223 break;
1224 case BUFFER_OVERFLOW:
1225 // Flush the unwrapped data in the outAppBuf into frame and try again.
1226 // See the finally block.
1227 continue;
1228 }
1229
1230 break;
1231 } finally {
1232 outAppBuf.flip();
1233
1234 // Copy the unwrapped data into a smaller buffer.
1235 if (outAppBuf.hasRemaining()) {
1236 if (nettyOutAppBuf == null) {
1237 ChannelBufferFactory factory = ctx.getChannel().getConfig().getBufferFactory();
1238 nettyOutAppBuf = factory.getBuffer(initialNettyOutAppBufCapacity);
1239 }
1240 nettyOutAppBuf.writeBytes(outAppBuf);
1241 }
1242 outAppBuf.clear();
1243 }
1244 }
1245
1246 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1247 handleRenegotiation(handshakeStatus);
1248 switch (handshakeStatus) {
1249 case NEED_UNWRAP:
1250 break;
1251 case NEED_WRAP:
1252 wrapNonAppData(ctx, channel);
1253 break;
1254 case NEED_TASK:
1255 runDelegatedTasks();
1256 break;
1257 case FINISHED:
1258 setHandshakeSuccess(channel);
1259 needsWrap = true;
1260 continue;
1261 case NOT_HANDSHAKING:
1262 if (setHandshakeSuccessIfStillHandshaking(channel)) {
1263 needsWrap = true;
1264 continue;
1265 }
1266 if (writeBeforeHandshakeDone) {
1267 // We need to call wrap(...) in case there was a flush done before the handshake completed.
1268 //
1269 // See https://github.com/netty/netty/pull/2437
1270 writeBeforeHandshakeDone = false;
1271 needsWrap = true;
1272 }
1273 break;
1274 default:
1275 throw new IllegalStateException(
1276 "Unknown handshake status: " + handshakeStatus);
1277 }
1278
1279 if (result.getStatus() == Status.BUFFER_UNDERFLOW ||
1280 result.bytesConsumed() == 0 && result.bytesProduced() == 0) {
1281 if (nioInNetBuf.hasRemaining() && !engine.isInboundDone()) {
1282 // We expect SSLEngine to consume all the bytes we feed it, but
1283 // empirical evidence indicates that we sometimes end up with leftovers
1284 // Log when this happens to get a better understanding of this corner
1285 // case.
1286 // See https://github.com/netty/netty/pull/3584
1287 logger.warn("Unexpected leftover data after SSLEngine.unwrap():"
1288 + " status=" + result.getStatus()
1289 + " handshakeStatus=" + result.getHandshakeStatus()
1290 + " consumed=" + result.bytesConsumed()
1291 + " produced=" + result.bytesProduced()
1292 + " remaining=" + nioInNetBuf.remaining()
1293 + " data=" + ChannelBuffers.hexDump(ChannelBuffers.wrappedBuffer(nioInNetBuf)));
1294 }
1295 break;
1296 }
1297 }
1298 }
1299
1300 if (needsWrap) {
1301 // wrap() acquires pendingUnencryptedWrites first and then
1302 // handshakeLock. If handshakeLock is already hold by the
1303 // current thread, calling wrap() will lead to a dead lock
1304 // i.e. pendingUnencryptedWrites -> handshakeLock vs.
1305 // handshakeLock -> pendingUnencryptedLock -> handshakeLock
1306 //
1307 // There is also the same issue between pendingEncryptedWrites
1308 // and pendingUnencryptedWrites.
1309 if (!Thread.holdsLock(handshakeLock) && !pendingEncryptedWritesLock.isHeldByCurrentThread()) {
1310 wrap(ctx, channel);
1311 }
1312 }
1313 } catch (SSLException e) {
1314 setHandshakeFailure(channel, e);
1315 throw e;
1316 } finally {
1317 bufferPool.releaseBuffer(nioOutAppBuf);
1318 }
1319
1320 if (nettyOutAppBuf != null && nettyOutAppBuf.readable()) {
1321 return nettyOutAppBuf;
1322 } else {
1323 return null;
1324 }
1325 }
1326
1327 private void handleRenegotiation(HandshakeStatus handshakeStatus) {
1328 synchronized (handshakeLock) {
1329 if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING ||
1330 handshakeStatus == HandshakeStatus.FINISHED) {
1331 // Not handshaking
1332 return;
1333 }
1334
1335 if (!handshaken) {
1336 // Not renegotiation
1337 return;
1338 }
1339
1340 final boolean renegotiate;
1341 if (handshaking) {
1342 // Renegotiation in progress or failed already.
1343 // i.e. Renegotiation check has been done already below.
1344 return;
1345 }
1346
1347 if (engine.isInboundDone() || engine.isOutboundDone()) {
1348 // Not handshaking but closing.
1349 return;
1350 }
1351
1352 if (isEnableRenegotiation()) {
1353 // Continue renegotiation.
1354 renegotiate = true;
1355 } else {
1356 // Do not renegotiate.
1357 renegotiate = false;
1358 // Prevent reentrance of this method.
1359 handshaking = true;
1360 }
1361
1362 if (renegotiate) {
1363 // Renegotiate.
1364 handshake();
1365 } else {
1366 // Raise an exception.
1367 fireExceptionCaught(
1368 ctx, new SSLException(
1369 "renegotiation attempted by peer; " +
1370 "closing the connection"));
1371
1372 // Close the connection to stop renegotiation.
1373 Channels.close(ctx, succeededFuture(ctx.getChannel()));
1374 }
1375 }
1376 }
1377
1378 /**
1379 * Fetches all delegated tasks from the {@link SSLEngine} and runs them immediately.
1380 */
1381 private void runDelegatedTasks() {
1382 for (;;) {
1383 final Runnable task;
1384 synchronized (handshakeLock) {
1385 task = engine.getDelegatedTask();
1386 }
1387
1388 if (task == null) {
1389 break;
1390 }
1391
1392 task.run();
1393 }
1394 }
1395
1396 /**
1397 * Works around some Android {@link SSLEngine} implementations that skip {@link HandshakeStatus#FINISHED} and
1398 * go straight into {@link HandshakeStatus#NOT_HANDSHAKING} when handshake is finished.
1399 *
1400 * @return {@code true} if and only if the workaround has been applied and thus {@link #handshakeFuture} has been
1401 * marked as success by this method
1402 */
1403 private boolean setHandshakeSuccessIfStillHandshaking(Channel channel) {
1404 if (handshaking && !handshakeFuture.isDone()) {
1405 setHandshakeSuccess(channel);
1406 return true;
1407 }
1408 return false;
1409 }
1410
1411 private void setHandshakeSuccess(Channel channel) {
1412 synchronized (handshakeLock) {
1413 handshaking = false;
1414 handshaken = true;
1415
1416 if (handshakeFuture == null) {
1417 handshakeFuture = future(channel);
1418 }
1419 cancelHandshakeTimeout();
1420 }
1421
1422 if (logger.isDebugEnabled()) {
1423 logger.debug(channel + " HANDSHAKEN: " + engine.getSession().getCipherSuite());
1424 }
1425
1426 handshakeFuture.setSuccess();
1427 }
1428
1429 private void setHandshakeFailure(Channel channel, SSLException cause) {
1430 synchronized (handshakeLock) {
1431 if (!handshaking) {
1432 return;
1433 }
1434 handshaking = false;
1435 handshaken = false;
1436
1437 if (handshakeFuture == null) {
1438 handshakeFuture = future(channel);
1439 }
1440
1441 // cancel the timeout now
1442 cancelHandshakeTimeout();
1443
1444 // Release all resources such as internal buffers that SSLEngine
1445 // is managing.
1446
1447 engine.closeOutbound();
1448
1449 try {
1450 engine.closeInbound();
1451 } catch (SSLException e) {
1452 if (logger.isDebugEnabled()) {
1453 logger.debug(
1454 "SSLEngine.closeInbound() raised an exception after " +
1455 "a handshake failure.", e);
1456 }
1457 }
1458 }
1459
1460 handshakeFuture.setFailure(cause);
1461 if (closeOnSslException) {
1462 Channels.close(ctx, future(channel));
1463 }
1464 }
1465
1466 private void closeOutboundAndChannel(
1467 final ChannelHandlerContext context, final ChannelStateEvent e) {
1468 if (!e.getChannel().isConnected()) {
1469 context.sendDownstream(e);
1470 return;
1471 }
1472
1473 // Ensure that the tear-down logic beyond this point is never invoked concurrently nor multiple times.
1474 if (!CLOSED_OUTBOUND_AND_CHANNEL_UPDATER.compareAndSet(this, 0, 1)) {
1475 // The other thread called this method already, and thus the connection will be closed eventually.
1476 // So, just wait until the connection is closed, and then forward the event so that the sink handles
1477 // the duplicate close attempt.
1478 e.getChannel().getCloseFuture().addListener(new ChannelFutureListener() {
1479 public void operationComplete(ChannelFuture future) throws Exception {
1480 context.sendDownstream(e);
1481 }
1482 });
1483 return;
1484 }
1485
1486 boolean passthrough = true;
1487 try {
1488 try {
1489 unwrapNonAppData(ctx, e.getChannel(), false);
1490 } catch (SSLException ex) {
1491 if (logger.isDebugEnabled()) {
1492 logger.debug("Failed to unwrap before sending a close_notify message", ex);
1493 }
1494 }
1495
1496 if (!engine.isOutboundDone()) {
1497 if (SENT_CLOSE_NOTIFY_UPDATER.compareAndSet(this, 0, 1)) {
1498 engine.closeOutbound();
1499 try {
1500 ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel());
1501 closeNotifyFuture.addListener(
1502 new ClosingChannelFutureListener(context, e));
1503 passthrough = false;
1504 } catch (SSLException ex) {
1505 if (logger.isDebugEnabled()) {
1506 logger.debug("Failed to encode a close_notify message", ex);
1507 }
1508 }
1509 }
1510 }
1511 } finally {
1512 if (passthrough) {
1513 context.sendDownstream(e);
1514 }
1515 }
1516 }
1517
1518 private static final class PendingWrite {
1519 final ChannelFuture future;
1520 final ByteBuffer outAppBuf;
1521
1522 PendingWrite(ChannelFuture future, ByteBuffer outAppBuf) {
1523 this.future = future;
1524 this.outAppBuf = outAppBuf;
1525 }
1526 }
1527
1528 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1529
1530 private final ChannelHandlerContext context;
1531 private final ChannelStateEvent e;
1532
1533 ClosingChannelFutureListener(
1534 ChannelHandlerContext context, ChannelStateEvent e) {
1535 this.context = context;
1536 this.e = e;
1537 }
1538
1539 public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception {
1540 if (!(closeNotifyFuture.getCause() instanceof ClosedChannelException)) {
1541 Channels.close(context, e.getFuture());
1542 } else {
1543 e.getFuture().setSuccess();
1544 }
1545 }
1546 }
1547
1548 @Override
1549 public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
1550 super.beforeAdd(ctx);
1551 this.ctx = ctx;
1552 }
1553
1554 /**
1555 * Fail all pending writes which we were not able to flush out
1556 */
1557 @Override
1558 public void afterRemove(ChannelHandlerContext ctx) throws Exception {
1559 closeEngine();
1560
1561 // there is no need for synchronization here as we do not receive downstream events anymore
1562 Throwable cause = null;
1563 for (;;) {
1564 PendingWrite pw = pendingUnencryptedWrites.poll();
1565 if (pw == null) {
1566 break;
1567 }
1568 if (cause == null) {
1569 cause = new IOException("Unable to write data");
1570 }
1571 pw.future.setFailure(cause);
1572 }
1573
1574 for (;;) {
1575 MessageEvent ev = pendingEncryptedWrites.poll();
1576 if (ev == null) {
1577 break;
1578 }
1579 if (cause == null) {
1580 cause = new IOException("Unable to write data");
1581 }
1582 ev.getFuture().setFailure(cause);
1583 }
1584
1585 if (cause != null) {
1586 fireExceptionCaughtLater(ctx, cause);
1587 }
1588 }
1589
1590 /**
1591 * Calls {@link #handshake()} once the {@link Channel} is connected
1592 */
1593 @Override
1594 public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception {
1595 if (issueHandshake) {
1596 // issue and handshake and add a listener to it which will fire an exception event if
1597 // an exception was thrown while doing the handshake
1598 handshake().addListener(new ChannelFutureListener() {
1599
1600 public void operationComplete(ChannelFuture future) throws Exception {
1601 if (future.isSuccess()) {
1602 // Send the event upstream after the handshake was completed without an error.
1603 //
1604 // See https://github.com/netty/netty/issues/358
1605 ctx.sendUpstream(e);
1606 }
1607 }
1608 });
1609 } else {
1610 super.channelConnected(ctx, e);
1611 }
1612 }
1613
1614 /**
1615 * Loop over all the pending writes and fail them.
1616 *
1617 * See <a href="https://github.com/netty/netty/issues/305">#305</a> for more details.
1618 */
1619 @Override
1620 public void channelClosed(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
1621 // Move the fail of the writes to the IO-Thread to prevent possible deadlock
1622 // See https://github.com/netty/netty/issues/989
1623 ctx.getPipeline().execute(new Runnable() {
1624 public void run() {
1625 if (!pendingUnencryptedWritesLock.tryLock()) {
1626 return;
1627 }
1628
1629 List<ChannelFuture> futures = null;
1630 try {
1631 for (;;) {
1632 PendingWrite pw = pendingUnencryptedWrites.poll();
1633 if (pw == null) {
1634 break;
1635 }
1636 if (futures == null) {
1637 futures = new ArrayList<ChannelFuture>();
1638 }
1639 futures.add(pw.future);
1640 }
1641
1642 for (;;) {
1643 MessageEvent ev = pendingEncryptedWrites.poll();
1644 if (ev == null) {
1645 break;
1646 }
1647 if (futures == null) {
1648 futures = new ArrayList<ChannelFuture>();
1649 }
1650 futures.add(ev.getFuture());
1651 }
1652 } finally {
1653 pendingUnencryptedWritesLock.unlock();
1654 }
1655
1656 if (futures != null) {
1657 final ClosedChannelException cause = new ClosedChannelException();
1658 final int size = futures.size();
1659 for (int i = 0; i < size; i ++) {
1660 futures.get(i).setFailure(cause);
1661 }
1662 fireExceptionCaught(ctx, cause);
1663 }
1664 }
1665 });
1666
1667 super.channelClosed(ctx, e);
1668 }
1669
1670 private final class SSLEngineInboundCloseFuture extends DefaultChannelFuture {
1671 SSLEngineInboundCloseFuture() {
1672 super(null, true);
1673 }
1674
1675 void setClosed() {
1676 super.setSuccess();
1677 }
1678
1679 @Override
1680 public Channel getChannel() {
1681 if (ctx == null) {
1682 // Maybe we should better throw an IllegalStateException() ?
1683 return null;
1684 } else {
1685 return ctx.getChannel();
1686 }
1687 }
1688
1689 @Override
1690 public boolean setSuccess() {
1691 return false;
1692 }
1693
1694 @Override
1695 public boolean setFailure(Throwable cause) {
1696 return false;
1697 }
1698 }
1699 }