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