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|Datagram|Sctp|Udt)Channel.*$");
188 private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile(
189 "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE);
190
191 private static SslBufferPool defaultBufferPool;
192
193 /**
194 * Returns the default {@link SslBufferPool} used when no pool is
195 * specified in the constructor.
196 */
197 public static synchronized SslBufferPool getDefaultBufferPool() {
198 if (defaultBufferPool == null) {
199 defaultBufferPool = new SslBufferPool();
200 }
201 return defaultBufferPool;
202 }
203
204 private volatile ChannelHandlerContext ctx;
205 private final SSLEngine engine;
206 private final SslBufferPool bufferPool;
207 private final Executor delegatedTaskExecutor;
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 private final AtomicBoolean sentFirstMessage = new AtomicBoolean();
218 private final AtomicBoolean sentCloseNotify = new AtomicBoolean();
219 int ignoreClosedChannelException;
220 final Object ignoreClosedChannelExceptionLock = new Object();
221 private final Queue<PendingWrite> pendingUnencryptedWrites = new LinkedList<PendingWrite>();
222 private final NonReentrantLock pendingUnencryptedWritesLock = new NonReentrantLock();
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
623 pendingUnencryptedWritesLock.lock();
624 try {
625 pendingUnencryptedWrites.add(pendingWrite);
626 } finally {
627 pendingUnencryptedWritesLock.unlock();
628 }
629
630 wrap(context, evt.getChannel());
631 }
632
633 private void cancelHandshakeTimeout() {
634 if (handshakeTimeout != null) {
635 // cancel the task as we will fail the handshake future now
636 handshakeTimeout.cancel();
637 }
638 }
639
640 @Override
641 public void channelDisconnected(ChannelHandlerContext ctx,
642 ChannelStateEvent e) throws Exception {
643
644 // Make sure the handshake future is notified when a connection has
645 // been closed during handshake.
646 synchronized (handshakeLock) {
647 if (handshaking) {
648 cancelHandshakeTimeout();
649 handshakeFuture.setFailure(new ClosedChannelException());
650 }
651 }
652
653 try {
654 super.channelDisconnected(ctx, e);
655 } finally {
656 unwrap(ctx, e.getChannel(), ChannelBuffers.EMPTY_BUFFER, 0, 0);
657 engine.closeOutbound();
658 if (!sentCloseNotify.get() && handshaken) {
659 try {
660 engine.closeInbound();
661 } catch (SSLException ex) {
662 if (logger.isDebugEnabled()) {
663 logger.debug("Failed to clean up SSLEngine.", ex);
664 }
665 }
666 }
667 }
668 }
669 @Override
670 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
671 throws Exception {
672
673 Throwable cause = e.getCause();
674 if (cause instanceof IOException) {
675 if (cause instanceof ClosedChannelException) {
676 synchronized (ignoreClosedChannelExceptionLock) {
677 if (ignoreClosedChannelException > 0) {
678 ignoreClosedChannelException --;
679 if (logger.isDebugEnabled()) {
680 logger.debug(
681 "Swallowing an exception raised while " +
682 "writing non-app data", cause);
683 }
684
685 return;
686 }
687 }
688 } else {
689 if (ignoreException(cause)) {
690 return;
691 }
692 }
693 }
694
695 ctx.sendUpstream(e);
696 }
697
698 /**
699 * Checks if the given {@link Throwable} can be ignore and just "swallowed"
700 *
701 * When an ssl connection is closed a close_notify message is sent.
702 * After that the peer also sends close_notify however, it's not mandatory to receive
703 * the close_notify. The party who sent the initial close_notify can close the connection immediately
704 * then the peer will get connection reset error.
705 *
706 */
707 private boolean ignoreException(Throwable t) {
708 if (!(t instanceof SSLException) && t instanceof IOException && engine.isOutboundDone()) {
709 String message = String.valueOf(t.getMessage()).toLowerCase();
710
711 // first try to match connection reset / broke peer based on the regex. This is the fastest way
712 // but may fail on different jdk impls or OS's
713 if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) {
714 return true;
715 }
716
717 // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not
718 StackTraceElement[] elements = t.getStackTrace();
719 for (StackTraceElement element: elements) {
720 String classname = element.getClassName();
721 String methodname = element.getMethodName();
722
723 // skip all classes that belong to the io.netty package
724 if (classname.startsWith("org.jboss.netty.")) {
725 continue;
726 }
727
728 // check if the method name is read if not skip it
729 if (!"read".equals(methodname)) {
730 continue;
731 }
732
733 // This will also match against SocketInputStream which is used by openjdk 7 and maybe
734 // also others
735 if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) {
736 return true;
737 }
738
739 try {
740 // No match by now.. Try to load the class via classloader and inspect it.
741 // This is mainly done as other JDK implementations may differ in name of
742 // the impl.
743 Class<?> clazz = getClass().getClassLoader().loadClass(classname);
744
745 if (SocketChannel.class.isAssignableFrom(clazz)
746 || DatagramChannel.class.isAssignableFrom(clazz)) {
747 return true;
748 }
749
750 // also match against SctpChannel via String matching as it may not present.
751 if (DetectionUtil.javaVersion() >= 7
752 && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) {
753 return true;
754 }
755 } catch (ClassNotFoundException e) {
756 // This should not happen just ignore
757 }
758 }
759 }
760
761 return false;
762 }
763
764 /**
765 * Returns {@code true} if the given {@link ChannelBuffer} is encrypted. Be aware that this method
766 * will not increase the readerIndex of the given {@link ChannelBuffer}.
767 *
768 * @param buffer
769 * The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
770 * otherwise it will throw an {@link IllegalArgumentException}.
771 * @return encrypted
772 * {@code true} if the {@link ChannelBuffer} is encrypted, {@code false} otherwise.
773 * @throws IllegalArgumentException
774 * Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
775 */
776 public static boolean isEncrypted(ChannelBuffer buffer) {
777 return getEncryptedPacketLength(buffer) != -1;
778 }
779
780 /**
781 * Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase
782 * the readerIndex of the given {@link ChannelBuffer}.
783 *
784 * @param buffer
785 * The {@link ChannelBuffer} to read from. Be aware that it must have at least 5 bytes to read,
786 * otherwise it will throw an {@link IllegalArgumentException}.
787 * @return length
788 * The length of the encrypted packet that is included in the buffer. This will
789 * return {@code -1} if the given {@link ChannelBuffer} is not encrypted at all.
790 * @throws IllegalArgumentException
791 * Is thrown if the given {@link ChannelBuffer} has not at least 5 bytes to read.
792 */
793 private static int getEncryptedPacketLength(ChannelBuffer buffer) {
794 if (buffer.readableBytes() < 5) {
795 throw new IllegalArgumentException("buffer must have at least 5 readable bytes");
796 }
797
798 int packetLength = 0;
799
800 // SSLv3 or TLS - Check ContentType
801 boolean tls;
802 switch (buffer.getUnsignedByte(buffer.readerIndex())) {
803 case 20: // change_cipher_spec
804 case 21: // alert
805 case 22: // handshake
806 case 23: // application_data
807 tls = true;
808 break;
809 default:
810 // SSLv2 or bad data
811 tls = false;
812 }
813
814 if (tls) {
815 // SSLv3 or TLS - Check ProtocolVersion
816 int majorVersion = buffer.getUnsignedByte(buffer.readerIndex() + 1);
817 if (majorVersion == 3) {
818 // SSLv3 or TLS
819 packetLength = (getShort(buffer, buffer.readerIndex() + 3) & 0xFFFF) + 5;
820 if (packetLength <= 5) {
821 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
822 tls = false;
823 }
824 } else {
825 // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
826 tls = false;
827 }
828 }
829
830 if (!tls) {
831 // SSLv2 or bad data - Check the version
832 boolean sslv2 = true;
833 int headerLength = (buffer.getUnsignedByte(
834 buffer.readerIndex()) & 0x80) != 0 ? 2 : 3;
835 int majorVersion = buffer.getUnsignedByte(
836 buffer.readerIndex() + headerLength + 1);
837 if (majorVersion == 2 || majorVersion == 3) {
838 // SSLv2
839 if (headerLength == 2) {
840 packetLength = (getShort(buffer, buffer.readerIndex()) & 0x7FFF) + 2;
841 } else {
842 packetLength = (getShort(buffer, buffer.readerIndex()) & 0x3FFF) + 3;
843 }
844 if (packetLength <= headerLength) {
845 sslv2 = false;
846 }
847 } else {
848 sslv2 = false;
849 }
850
851 if (!sslv2) {
852 return -1;
853 }
854 }
855 return packetLength;
856 }
857
858 @Override
859 protected Object decode(
860 final ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
861
862 // Check if the packet length was parsed yet, if so we can skip the parsing
863 if (packetLength == Integer.MIN_VALUE) {
864 if (buffer.readableBytes() < 5) {
865 return null;
866 }
867 int packetLength = getEncryptedPacketLength(buffer);
868
869 if (packetLength == -1) {
870 // Bad data - discard the buffer and raise an exception.
871 NotSslRecordException e = new NotSslRecordException(
872 "not an SSL/TLS record: " + ChannelBuffers.hexDump(buffer));
873 buffer.skipBytes(buffer.readableBytes());
874
875 if (closeOnSSLException) {
876 // first trigger the exception and then close the channel
877 fireExceptionCaught(ctx, e);
878 Channels.close(ctx, future(channel));
879
880 // just return null as we closed the channel before, that
881 // will take care of cleanup etc
882 return null;
883 } else {
884 throw e;
885 }
886 }
887
888 assert packetLength > 0;
889 this.packetLength = packetLength;
890 }
891
892 if (buffer.readableBytes() < packetLength) {
893 return null;
894 }
895
896 // We advance the buffer's readerIndex before calling unwrap() because
897 // unwrap() can trigger FrameDecoder call decode(), this method, recursively.
898 // The recursive call results in decoding the same packet twice if
899 // the readerIndex is advanced *after* decode().
900 //
901 // Here's an example:
902 // 1) An SSL packet is received from the wire.
903 // 2) SslHandler.decode() deciphers the packet and calls the user code.
904 // 3) The user closes the channel in the same thread.
905 // 4) The same thread triggers a channelDisconnected() event.
906 // 5) FrameDecoder.cleanup() is called, and it calls SslHandler.decode().
907 // 6) SslHandler.decode() will feed the same packet with what was
908 // deciphered at the step 2 again if the readerIndex was not advanced
909 // before calling the user code.
910 final int packetOffset = buffer.readerIndex();
911 buffer.skipBytes(packetLength);
912 try {
913 return unwrap(ctx, channel, buffer, packetOffset, packetLength);
914 } finally {
915 // reset the packet length so it will be parsed again on the next call
916 packetLength = Integer.MIN_VALUE;
917 }
918 }
919
920 /**
921 * Reads a big-endian short integer from the buffer. Please note that we do not use
922 * {@link ChannelBuffer#getShort(int)} because it might be a little-endian buffer.
923 */
924 private static short getShort(ChannelBuffer buf, int offset) {
925 return (short) (buf.getByte(offset) << 8 | buf.getByte(offset + 1) & 0xFF);
926 }
927
928 private void wrap(ChannelHandlerContext context, Channel channel)
929 throws SSLException {
930
931 ChannelBuffer msg;
932 ByteBuffer outNetBuf = bufferPool.acquireBuffer();
933 boolean success = true;
934 boolean offered = false;
935 boolean needsUnwrap = false;
936 PendingWrite pendingWrite = null;
937
938 try {
939 loop:
940 for (;;) {
941 // Acquire a lock to make sure unencrypted data is polled
942 // in order and their encrypted counterpart is offered in
943 // order.
944 pendingUnencryptedWritesLock.lock();
945 try {
946 pendingWrite = pendingUnencryptedWrites.peek();
947 if (pendingWrite == null) {
948 break;
949 }
950
951 ByteBuffer outAppBuf = pendingWrite.outAppBuf;
952 if (outAppBuf == null) {
953 // A write request with an empty buffer
954 pendingUnencryptedWrites.remove();
955 offerEncryptedWriteRequest(
956 new DownstreamMessageEvent(
957 channel, pendingWrite.future,
958 ChannelBuffers.EMPTY_BUFFER,
959 channel.getRemoteAddress()));
960 offered = true;
961 } else {
962 synchronized (handshakeLock) {
963 SSLEngineResult result = null;
964 try {
965 result = engine.wrap(outAppBuf, outNetBuf);
966 } finally {
967 if (!outAppBuf.hasRemaining()) {
968 pendingUnencryptedWrites.remove();
969 }
970 }
971
972 if (result.bytesProduced() > 0) {
973 outNetBuf.flip();
974 int remaining = outNetBuf.remaining();
975 msg = ctx.getChannel().getConfig().getBufferFactory().getBuffer(remaining);
976
977 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
978 // work with "non" heap buffers
979 //
980 // See https://github.com/netty/netty/issues/329
981 msg.writeBytes(outNetBuf);
982 outNetBuf.clear();
983
984 ChannelFuture future;
985 if (pendingWrite.outAppBuf.hasRemaining()) {
986 // pendingWrite's future shouldn't be notified if
987 // only partial data is written.
988 future = succeededFuture(channel);
989 } else {
990 future = pendingWrite.future;
991 }
992
993 MessageEvent encryptedWrite = new DownstreamMessageEvent(
994 channel, future, msg, channel.getRemoteAddress());
995 offerEncryptedWriteRequest(encryptedWrite);
996 offered = true;
997 } else if (result.getStatus() == Status.CLOSED) {
998 // SSLEngine has been closed already.
999 // Any further write attempts should be denied.
1000 success = false;
1001 break;
1002 } else {
1003 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1004 handleRenegotiation(handshakeStatus);
1005 switch (handshakeStatus) {
1006 case NEED_WRAP:
1007 if (outAppBuf.hasRemaining()) {
1008 break;
1009 } else {
1010 break loop;
1011 }
1012 case NEED_UNWRAP:
1013 needsUnwrap = true;
1014 break loop;
1015 case NEED_TASK:
1016 runDelegatedTasks();
1017 break;
1018 case FINISHED:
1019 case NOT_HANDSHAKING:
1020 if (handshakeStatus == HandshakeStatus.FINISHED) {
1021 setHandshakeSuccess(channel);
1022 }
1023 if (result.getStatus() == Status.CLOSED) {
1024 success = false;
1025 }
1026 break loop;
1027 default:
1028 throw new IllegalStateException(
1029 "Unknown handshake status: " +
1030 handshakeStatus);
1031 }
1032 }
1033 }
1034 }
1035 } finally {
1036 pendingUnencryptedWritesLock.unlock();
1037 }
1038 }
1039 } catch (SSLException e) {
1040 success = false;
1041 setHandshakeFailure(channel, e);
1042 throw e;
1043 } finally {
1044 bufferPool.releaseBuffer(outNetBuf);
1045
1046 if (offered) {
1047 flushPendingEncryptedWrites(context);
1048 }
1049
1050 if (!success) {
1051 IllegalStateException cause =
1052 new IllegalStateException("SSLEngine already closed");
1053
1054 // Check if we had a pendingWrite in process, if so we need to also notify as otherwise
1055 // the ChannelFuture will never get notified
1056 if (pendingWrite != null) {
1057 pendingWrite.future.setFailure(cause);
1058 }
1059
1060 // Mark all remaining pending writes as failure if anything
1061 // wrong happened before the write requests are wrapped.
1062 // Please note that we do not call setFailure while a lock is
1063 // acquired, to avoid a potential dead lock.
1064 for (;;) {
1065 pendingUnencryptedWritesLock.lock();
1066 try {
1067 pendingWrite = pendingUnencryptedWrites.poll();
1068 if (pendingWrite == null) {
1069 break;
1070 }
1071 } finally {
1072 pendingUnencryptedWritesLock.unlock();
1073 }
1074
1075 pendingWrite.future.setFailure(cause);
1076 }
1077 }
1078 }
1079
1080 if (needsUnwrap) {
1081 unwrap(context, channel, ChannelBuffers.EMPTY_BUFFER, 0, 0);
1082 }
1083 }
1084
1085 private void offerEncryptedWriteRequest(MessageEvent encryptedWrite) {
1086 final boolean locked = pendingEncryptedWritesLock.tryLock();
1087 try {
1088 pendingEncryptedWrites.add(encryptedWrite);
1089 } finally {
1090 if (locked) {
1091 pendingEncryptedWritesLock.unlock();
1092 }
1093 }
1094 }
1095
1096 private void flushPendingEncryptedWrites(ChannelHandlerContext ctx) {
1097 while (!pendingEncryptedWrites.isEmpty()) {
1098 // Avoid possible dead lock and data integrity issue
1099 // which is caused by cross communication between more than one channel
1100 // in the same VM.
1101 if (!pendingEncryptedWritesLock.tryLock()) {
1102 return;
1103 }
1104
1105 try {
1106 MessageEvent e;
1107 while ((e = pendingEncryptedWrites.poll()) != null) {
1108 ctx.sendDownstream(e);
1109 }
1110 } finally {
1111 pendingEncryptedWritesLock.unlock();
1112 }
1113
1114 // Other thread might have added more elements at this point, so we loop again if the queue got unempty.
1115 }
1116 }
1117
1118 private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel) throws SSLException {
1119 ChannelFuture future = null;
1120 ByteBuffer outNetBuf = bufferPool.acquireBuffer();
1121
1122 SSLEngineResult result;
1123 try {
1124 for (;;) {
1125 synchronized (handshakeLock) {
1126 result = engine.wrap(EMPTY_BUFFER, outNetBuf);
1127 }
1128
1129 if (result.bytesProduced() > 0) {
1130 outNetBuf.flip();
1131 ChannelBuffer msg =
1132 ctx.getChannel().getConfig().getBufferFactory().getBuffer(outNetBuf.remaining());
1133
1134 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
1135 // work with "non" heap buffers
1136 //
1137 // See https://github.com/netty/netty/issues/329
1138 msg.writeBytes(outNetBuf);
1139 outNetBuf.clear();
1140
1141 future = future(channel);
1142 future.addListener(new ChannelFutureListener() {
1143 public void operationComplete(ChannelFuture future)
1144 throws Exception {
1145 if (future.getCause() instanceof ClosedChannelException) {
1146 synchronized (ignoreClosedChannelExceptionLock) {
1147 ignoreClosedChannelException ++;
1148 }
1149 }
1150 }
1151 });
1152
1153 write(ctx, future, msg);
1154 }
1155
1156 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1157 handleRenegotiation(handshakeStatus);
1158 switch (handshakeStatus) {
1159 case FINISHED:
1160 setHandshakeSuccess(channel);
1161 runDelegatedTasks();
1162 break;
1163 case NEED_TASK:
1164 runDelegatedTasks();
1165 break;
1166 case NEED_UNWRAP:
1167 if (!Thread.holdsLock(handshakeLock)) {
1168 // unwrap shouldn't be called when this method was
1169 // called by unwrap - unwrap will keep running after
1170 // this method returns.
1171 unwrap(ctx, channel, ChannelBuffers.EMPTY_BUFFER, 0, 0);
1172 }
1173 break;
1174 case NOT_HANDSHAKING:
1175 case NEED_WRAP:
1176 break;
1177 default:
1178 throw new IllegalStateException(
1179 "Unexpected handshake status: " + handshakeStatus);
1180 }
1181
1182 if (result.bytesProduced() == 0) {
1183 break;
1184 }
1185 }
1186 } catch (SSLException e) {
1187 setHandshakeFailure(channel, e);
1188 throw e;
1189 } finally {
1190 bufferPool.releaseBuffer(outNetBuf);
1191 }
1192
1193 if (future == null) {
1194 future = succeededFuture(channel);
1195 }
1196
1197 return future;
1198 }
1199
1200 private ChannelBuffer unwrap(
1201 ChannelHandlerContext ctx, Channel channel,
1202 ChannelBuffer buffer, int offset, int length) throws SSLException {
1203 ByteBuffer inNetBuf = buffer.toByteBuffer(offset, length);
1204 ByteBuffer outAppBuf = bufferPool.acquireBuffer();
1205
1206 try {
1207 boolean needsWrap = false;
1208 loop:
1209 for (;;) {
1210 SSLEngineResult result;
1211 boolean needsHandshake = false;
1212 synchronized (handshakeLock) {
1213 if (!handshaken && !handshaking &&
1214 !engine.getUseClientMode() &&
1215 !engine.isInboundDone() && !engine.isOutboundDone()) {
1216 needsHandshake = true;
1217 }
1218 }
1219
1220 if (needsHandshake) {
1221 handshake();
1222 }
1223
1224 synchronized (handshakeLock) {
1225 result = engine.unwrap(inNetBuf, outAppBuf);
1226
1227 switch (result.getStatus()) {
1228 case CLOSED:
1229 // notify about the CLOSED state of the SSLEngine. See #137
1230 sslEngineCloseFuture.setClosed();
1231 break;
1232 case BUFFER_OVERFLOW:
1233 throw new SSLException("SSLEngine.unwrap() reported an impossible buffer overflow.");
1234 }
1235
1236 final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
1237 handleRenegotiation(handshakeStatus);
1238 switch (handshakeStatus) {
1239 case NEED_UNWRAP:
1240 if (inNetBuf.hasRemaining() && !engine.isInboundDone()) {
1241 break;
1242 } else {
1243 break loop;
1244 }
1245 case NEED_WRAP:
1246 wrapNonAppData(ctx, channel);
1247 break;
1248 case NEED_TASK:
1249 runDelegatedTasks();
1250 break;
1251 case FINISHED:
1252 setHandshakeSuccess(channel);
1253 needsWrap = true;
1254 break loop;
1255 case NOT_HANDSHAKING:
1256 needsWrap = true;
1257 break loop;
1258 default:
1259 throw new IllegalStateException(
1260 "Unknown handshake status: " + handshakeStatus);
1261 }
1262 }
1263 }
1264 if (needsWrap) {
1265 // wrap() acquires pendingUnencryptedWrites first and then
1266 // handshakeLock. If handshakeLock is already hold by the
1267 // current thread, calling wrap() will lead to a dead lock
1268 // i.e. pendingUnencryptedWrites -> handshakeLock vs.
1269 // handshakeLock -> pendingUnencryptedLock -> handshakeLock
1270 //
1271 // There is also the same issue between pendingEncryptedWrites
1272 // and pendingUnencryptedWrites.
1273 if (!Thread.holdsLock(handshakeLock) &&
1274 !pendingEncryptedWritesLock.isHeldByCurrentThread()) {
1275 wrap(ctx, channel);
1276 }
1277 }
1278 outAppBuf.flip();
1279
1280 if (outAppBuf.hasRemaining()) {
1281 ChannelBuffer frame = ctx.getChannel().getConfig().getBufferFactory().getBuffer(outAppBuf.remaining());
1282 // Transfer the bytes to the new ChannelBuffer using some safe method that will also
1283 // work with "non" heap buffers
1284 //
1285 // See https://github.com/netty/netty/issues/329
1286 frame.writeBytes(outAppBuf);
1287
1288 return frame;
1289 } else {
1290 return null;
1291 }
1292 } catch (SSLException e) {
1293 setHandshakeFailure(channel, e);
1294 throw e;
1295 } finally {
1296 bufferPool.releaseBuffer(outAppBuf);
1297 }
1298 }
1299
1300 private void handleRenegotiation(HandshakeStatus handshakeStatus) {
1301 synchronized (handshakeLock) {
1302 if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING ||
1303 handshakeStatus == HandshakeStatus.FINISHED) {
1304 // Not handshaking
1305 return;
1306 }
1307
1308 if (!handshaken) {
1309 // Not renegotiation
1310 return;
1311 }
1312
1313 final boolean renegotiate;
1314 if (handshaking) {
1315 // Renegotiation in progress or failed already.
1316 // i.e. Renegotiation check has been done already below.
1317 return;
1318 }
1319
1320 if (engine.isInboundDone() || engine.isOutboundDone()) {
1321 // Not handshaking but closing.
1322 return;
1323 }
1324
1325 if (isEnableRenegotiation()) {
1326 // Continue renegotiation.
1327 renegotiate = true;
1328 } else {
1329 // Do not renegotiate.
1330 renegotiate = false;
1331 // Prevent reentrance of this method.
1332 handshaking = true;
1333 }
1334
1335 if (renegotiate) {
1336 // Renegotiate.
1337 handshake();
1338 } else {
1339 // Raise an exception.
1340 fireExceptionCaught(
1341 ctx, new SSLException(
1342 "renegotiation attempted by peer; " +
1343 "closing the connection"));
1344
1345 // Close the connection to stop renegotiation.
1346 Channels.close(ctx, succeededFuture(ctx.getChannel()));
1347 }
1348 }
1349 }
1350
1351 private void runDelegatedTasks() {
1352 for (;;) {
1353 final Runnable task;
1354 synchronized (handshakeLock) {
1355 task = engine.getDelegatedTask();
1356 }
1357
1358 if (task == null) {
1359 break;
1360 }
1361
1362 delegatedTaskExecutor.execute(new Runnable() {
1363 public void run() {
1364 synchronized (handshakeLock) {
1365 task.run();
1366 }
1367 }
1368 });
1369 }
1370 }
1371
1372 private void setHandshakeSuccess(Channel channel) {
1373 synchronized (handshakeLock) {
1374 handshaking = false;
1375 handshaken = true;
1376
1377 if (handshakeFuture == null) {
1378 handshakeFuture = future(channel);
1379 }
1380 cancelHandshakeTimeout();
1381 }
1382
1383 handshakeFuture.setSuccess();
1384 }
1385
1386 private void setHandshakeFailure(Channel channel, SSLException cause) {
1387 synchronized (handshakeLock) {
1388 if (!handshaking) {
1389 return;
1390 }
1391 handshaking = false;
1392 handshaken = false;
1393
1394 if (handshakeFuture == null) {
1395 handshakeFuture = future(channel);
1396 }
1397
1398 // cancel the timeout now
1399 cancelHandshakeTimeout();
1400
1401 // Release all resources such as internal buffers that SSLEngine
1402 // is managing.
1403
1404 engine.closeOutbound();
1405
1406 try {
1407 engine.closeInbound();
1408 } catch (SSLException e) {
1409 if (logger.isDebugEnabled()) {
1410 logger.debug(
1411 "SSLEngine.closeInbound() raised an exception after " +
1412 "a handshake failure.", e);
1413 }
1414 }
1415 }
1416
1417 handshakeFuture.setFailure(cause);
1418 if (closeOnSSLException) {
1419 Channels.close(ctx, future(channel));
1420 }
1421 }
1422
1423 private void closeOutboundAndChannel(
1424 final ChannelHandlerContext context, final ChannelStateEvent e) {
1425 if (!e.getChannel().isConnected()) {
1426 context.sendDownstream(e);
1427 return;
1428 }
1429
1430 boolean passthrough = true;
1431 try {
1432 try {
1433 unwrap(context, e.getChannel(), ChannelBuffers.EMPTY_BUFFER, 0, 0);
1434 } catch (SSLException ex) {
1435 if (logger.isDebugEnabled()) {
1436 logger.debug("Failed to unwrap before sending a close_notify message", ex);
1437 }
1438 }
1439
1440 if (!engine.isOutboundDone()) {
1441 if (sentCloseNotify.compareAndSet(false, true)) {
1442 engine.closeOutbound();
1443 try {
1444 ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel());
1445 closeNotifyFuture.addListener(
1446 new ClosingChannelFutureListener(context, e));
1447 passthrough = false;
1448 } catch (SSLException ex) {
1449 if (logger.isDebugEnabled()) {
1450 logger.debug("Failed to encode a close_notify message", ex);
1451 }
1452 }
1453 }
1454 }
1455 } finally {
1456 if (passthrough) {
1457 context.sendDownstream(e);
1458 }
1459 }
1460 }
1461
1462 private static final class PendingWrite {
1463 final ChannelFuture future;
1464 final ByteBuffer outAppBuf;
1465
1466 PendingWrite(ChannelFuture future, ByteBuffer outAppBuf) {
1467 this.future = future;
1468 this.outAppBuf = outAppBuf;
1469 }
1470 }
1471
1472 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1473
1474 private final ChannelHandlerContext context;
1475 private final ChannelStateEvent e;
1476
1477 ClosingChannelFutureListener(
1478 ChannelHandlerContext context, ChannelStateEvent e) {
1479 this.context = context;
1480 this.e = e;
1481 }
1482
1483 public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception {
1484 if (!(closeNotifyFuture.getCause() instanceof ClosedChannelException)) {
1485 Channels.close(context, e.getFuture());
1486 } else {
1487 e.getFuture().setSuccess();
1488 }
1489 }
1490 }
1491
1492 @Override
1493 public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
1494 super.beforeAdd(ctx);
1495 this.ctx = ctx;
1496 }
1497
1498 /**
1499 * Fail all pending writes which we were not able to flush out
1500 */
1501 @Override
1502 public void afterRemove(ChannelHandlerContext ctx) throws Exception {
1503
1504 // there is no need for synchronization here as we do not receive downstream events anymore
1505 Throwable cause = null;
1506 for (;;) {
1507 PendingWrite pw = pendingUnencryptedWrites.poll();
1508 if (pw == null) {
1509 break;
1510 }
1511 if (cause == null) {
1512 cause = new IOException("Unable to write data");
1513 }
1514 pw.future.setFailure(cause);
1515 }
1516
1517 for (;;) {
1518 MessageEvent ev = pendingEncryptedWrites.poll();
1519 if (ev == null) {
1520 break;
1521 }
1522 if (cause == null) {
1523 cause = new IOException("Unable to write data");
1524 }
1525 ev.getFuture().setFailure(cause);
1526 }
1527
1528 if (cause != null) {
1529 fireExceptionCaughtLater(ctx, cause);
1530 }
1531 }
1532
1533 /**
1534 * Calls {@link #handshake()} once the {@link Channel} is connected
1535 */
1536 @Override
1537 public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception {
1538 if (issueHandshake) {
1539 // issue and handshake and add a listener to it which will fire an exception event if
1540 // an exception was thrown while doing the handshake
1541 handshake().addListener(new ChannelFutureListener() {
1542
1543 public void operationComplete(ChannelFuture future) throws Exception {
1544 if (future.isSuccess()) {
1545 // Send the event upstream after the handshake was completed without an error.
1546 //
1547 // See https://github.com/netty/netty/issues/358
1548 ctx.sendUpstream(e);
1549 }
1550 }
1551 });
1552 } else {
1553 super.channelConnected(ctx, e);
1554 }
1555 }
1556
1557 /**
1558 * Loop over all the pending writes and fail them.
1559 *
1560 * See <a href="https://github.com/netty/netty/issues/305">#305</a> for more details.
1561 */
1562 @Override
1563 public void channelClosed(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
1564 // Move the fail of the writes to the IO-Thread to prevent possible deadlock
1565 // See https://github.com/netty/netty/issues/989
1566 ctx.getPipeline().execute(new Runnable() {
1567 public void run() {
1568 if (!pendingUnencryptedWritesLock.tryLock()) {
1569 return;
1570 }
1571
1572 Throwable cause = null;
1573 try {
1574 for (;;) {
1575 PendingWrite pw = pendingUnencryptedWrites.poll();
1576 if (pw == null) {
1577 break;
1578 }
1579 if (cause == null) {
1580 cause = new ClosedChannelException();
1581 }
1582 pw.future.setFailure(cause);
1583 }
1584
1585 for (;;) {
1586 MessageEvent ev = pendingEncryptedWrites.poll();
1587 if (ev == null) {
1588 break;
1589 }
1590 if (cause == null) {
1591 cause = new ClosedChannelException();
1592 }
1593 ev.getFuture().setFailure(cause);
1594 }
1595 } finally {
1596 pendingUnencryptedWritesLock.unlock();
1597 }
1598
1599 if (cause != null) {
1600 fireExceptionCaught(ctx, cause);
1601 }
1602 }
1603 });
1604
1605 super.channelClosed(ctx, e);
1606 }
1607
1608 private final class SSLEngineInboundCloseFuture extends DefaultChannelFuture {
1609 public SSLEngineInboundCloseFuture() {
1610 super(null, true);
1611 }
1612
1613 void setClosed() {
1614 super.setSuccess();
1615 }
1616
1617 @Override
1618 public Channel getChannel() {
1619 if (ctx == null) {
1620 // Maybe we should better throw an IllegalStateException() ?
1621 return null;
1622 } else {
1623 return ctx.getChannel();
1624 }
1625 }
1626
1627 @Override
1628 public boolean setSuccess() {
1629 return false;
1630 }
1631
1632 @Override
1633 public boolean setFailure(Throwable cause) {
1634 return false;
1635 }
1636 }
1637 }