1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.io.ssl;
20
21 import java.io.IOException;
22 import java.net.SocketException;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.ClosedChannelException;
25 import java.util.Arrays;
26 import java.util.concurrent.Executor;
27
28 import javax.net.ssl.SSLEngine;
29 import javax.net.ssl.SSLEngineResult;
30 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
31 import javax.net.ssl.SSLEngineResult.Status;
32 import javax.net.ssl.SSLException;
33
34 import org.eclipse.jetty.io.AbstractConnection;
35 import org.eclipse.jetty.io.AbstractEndPoint;
36 import org.eclipse.jetty.io.ByteBufferPool;
37 import org.eclipse.jetty.io.Connection;
38 import org.eclipse.jetty.io.EndPoint;
39 import org.eclipse.jetty.io.EofException;
40 import org.eclipse.jetty.io.FillInterest;
41 import org.eclipse.jetty.io.RuntimeIOException;
42 import org.eclipse.jetty.io.SelectChannelEndPoint;
43 import org.eclipse.jetty.io.SocketBased;
44 import org.eclipse.jetty.io.WriteFlusher;
45 import org.eclipse.jetty.util.BufferUtil;
46 import org.eclipse.jetty.util.Callback;
47 import org.eclipse.jetty.util.log.Log;
48 import org.eclipse.jetty.util.log.Logger;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public class SslConnection extends AbstractConnection
82 {
83 private static final Logger LOG = Log.getLogger(SslConnection.class);
84 private static final boolean DEBUG = LOG.isDebugEnabled();
85 private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
86 private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
87 private final ByteBufferPool _bufferPool;
88 private final SSLEngine _sslEngine;
89 private final DecryptedEndPoint _decryptedEndPoint;
90 private ByteBuffer _decryptedInput;
91 private ByteBuffer _encryptedInput;
92 private ByteBuffer _encryptedOutput;
93 private final boolean _encryptedDirectBuffers = false;
94 private final boolean _decryptedDirectBuffers = false;
95 private final Runnable _runCompletWrite = new Runnable()
96 {
97 @Override
98 public void run()
99 {
100 _decryptedEndPoint.getWriteFlusher().completeWrite();
101 }
102 };
103
104 public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
105 {
106
107
108 super(endPoint, executor, !EXECUTE_ONFILLABLE);
109 this._bufferPool = byteBufferPool;
110 this._sslEngine = sslEngine;
111 this._decryptedEndPoint = newDecryptedEndPoint();
112
113 if (endPoint instanceof SocketBased)
114 {
115 try
116 {
117 ((SocketBased) endPoint).getSocket().setSoLinger(true,30000);
118 }
119 catch (SocketException e)
120 {
121 throw new RuntimeIOException(e);
122 }
123 }
124 }
125
126 protected DecryptedEndPoint newDecryptedEndPoint()
127 {
128 return new DecryptedEndPoint();
129 }
130
131 public SSLEngine getSSLEngine()
132 {
133 return _sslEngine;
134 }
135
136 public DecryptedEndPoint getDecryptedEndPoint()
137 {
138 return _decryptedEndPoint;
139 }
140
141 @Override
142 public void onOpen()
143 {
144 try
145 {
146
147 _sslEngine.beginHandshake();
148 super.onOpen();
149 getDecryptedEndPoint().getConnection().onOpen();
150 }
151 catch (SSLException x)
152 {
153 getEndPoint().close();
154 throw new RuntimeIOException(x);
155 }
156 }
157
158 @Override
159 public void onClose()
160 {
161 _decryptedEndPoint.getConnection().onClose();
162 super.onClose();
163 }
164
165
166
167
168
169
170
171
172
173
174
175
176
177 @Override
178 public void close()
179 {
180 getDecryptedEndPoint().getConnection().close();
181 }
182
183
184 @Override
185 public void onFillable()
186 {
187
188
189
190
191
192
193 if (DEBUG)
194 LOG.debug("onFillable enter {}", getEndPoint());
195
196
197
198 _decryptedEndPoint.getFillInterest().fillable();
199
200
201 synchronized(_decryptedEndPoint)
202 {
203 if (_decryptedEndPoint._flushRequiresFillToProgress)
204 {
205 _decryptedEndPoint._flushRequiresFillToProgress = false;
206 getExecutor().execute(_runCompletWrite);
207 }
208 }
209
210 if (DEBUG)
211 LOG.debug("onFillable exit {}", getEndPoint());
212 }
213
214
215 @Override
216 public void onFillInterestedFailed(Throwable cause)
217 {
218
219
220
221
222
223 synchronized(_decryptedEndPoint)
224 {
225 _decryptedEndPoint.getFillInterest().onFail(cause);
226
227 if (_decryptedEndPoint._flushRequiresFillToProgress)
228 {
229 _decryptedEndPoint._flushRequiresFillToProgress = false;
230 _decryptedEndPoint.getWriteFlusher().onFail(cause);
231 }
232 }
233 }
234
235
236 @Override
237 public String toString()
238 {
239 ByteBuffer b = _encryptedInput;
240 int ei=b==null?-1:b.remaining();
241 b = _encryptedOutput;
242 int eo=b==null?-1:b.remaining();
243 b = _decryptedInput;
244 int di=b==null?-1:b.remaining();
245
246 return String.format("SslConnection@%x{%s,eio=%d/%d,di=%d} -> %s",
247 hashCode(),
248 _sslEngine.getHandshakeStatus(),
249 ei,eo,di,
250 _decryptedEndPoint.getConnection());
251 }
252
253
254 public class DecryptedEndPoint extends AbstractEndPoint
255 {
256 private boolean _fillRequiresFlushToProgress;
257 private boolean _flushRequiresFillToProgress;
258 private boolean _cannotAcceptMoreAppDataToFlush;
259 private boolean _underFlown;
260
261 private final Callback _writeCallback = new Callback()
262 {
263 @Override
264 public void succeeded()
265 {
266
267
268
269 synchronized (DecryptedEndPoint.this)
270 {
271 if (DEBUG)
272 LOG.debug("write.complete {}", SslConnection.this.getEndPoint());
273
274 releaseEncryptedOutputBuffer();
275
276 _cannotAcceptMoreAppDataToFlush = false;
277
278 if (_fillRequiresFlushToProgress)
279 {
280 _fillRequiresFlushToProgress = false;
281 getFillInterest().fillable();
282 }
283
284 getExecutor().execute(_runCompletWrite);
285 }
286 }
287
288 @Override
289 public void failed(Throwable x)
290 {
291
292
293
294 synchronized (DecryptedEndPoint.this)
295 {
296 if (DEBUG)
297 LOG.debug("{} write.failed", SslConnection.this, x);
298 if (_encryptedOutput != null)
299 BufferUtil.clear(_encryptedOutput);
300 releaseEncryptedOutputBuffer();
301
302 _cannotAcceptMoreAppDataToFlush = false;
303
304 if (_fillRequiresFlushToProgress)
305 {
306 _fillRequiresFlushToProgress = false;
307 getFillInterest().onFail(x);
308 }
309
310 getWriteFlusher().onFail(x);
311 }
312 }
313 };
314
315 public DecryptedEndPoint()
316 {
317 super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
318 setIdleTimeout(getEndPoint().getIdleTimeout());
319 }
320
321 @Override
322 protected FillInterest getFillInterest()
323 {
324 return super.getFillInterest();
325 }
326
327 @Override
328 public void setIdleTimeout(long idleTimeout)
329 {
330 super.setIdleTimeout(idleTimeout);
331 getEndPoint().setIdleTimeout(idleTimeout);
332 }
333
334 @Override
335 protected WriteFlusher getWriteFlusher()
336 {
337 return super.getWriteFlusher();
338 }
339
340 @Override
341 protected void onIncompleteFlush()
342 {
343
344
345
346
347 synchronized (DecryptedEndPoint.this)
348 {
349 if (DEBUG)
350 LOG.debug("onIncompleteFlush {}", getEndPoint());
351
352 if (BufferUtil.hasContent(_encryptedOutput))
353 {
354
355 _cannotAcceptMoreAppDataToFlush = true;
356 getEndPoint().write(_writeCallback, _encryptedOutput);
357 }
358 else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
359 {
360
361 _flushRequiresFillToProgress=true;
362 SslConnection.this.fillInterested();
363 }
364 else if (isOutputShutdown())
365 {
366 getWriteFlusher().onClose();
367 }
368 else
369 {
370
371 getWriteFlusher().completeWrite();
372 }
373 }
374 }
375
376 @Override
377 protected boolean needsFill() throws IOException
378 {
379
380
381
382
383
384 synchronized (DecryptedEndPoint.this)
385 {
386
387 if (BufferUtil.hasContent(_decryptedInput))
388 return true;
389
390
391 if (BufferUtil.isEmpty(_encryptedInput) || _underFlown)
392 {
393
394
395
396 if (_fillRequiresFlushToProgress)
397 {
398
399
400
401 if (BufferUtil.hasContent(_encryptedOutput))
402 {
403
404 _cannotAcceptMoreAppDataToFlush = true;
405 getEndPoint().write(_writeCallback, _encryptedOutput);
406 }
407 else
408 {
409
410
411 _fillRequiresFlushToProgress = false;
412 return true;
413 }
414 }
415 else
416
417
418 SslConnection.this.fillInterested();
419
420 return false;
421 }
422 else
423 {
424
425 return true;
426 }
427 }
428 }
429
430 @Override
431 public void setConnection(Connection connection)
432 {
433 if (connection instanceof AbstractConnection)
434 {
435 AbstractConnection a = (AbstractConnection)connection;
436 if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize())
437 a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize());
438 }
439 super.setConnection(connection);
440 }
441
442 public SslConnection getSslConnection()
443 {
444 return SslConnection.this;
445 }
446
447 @Override
448 public synchronized int fill(ByteBuffer buffer) throws IOException
449 {
450 if (DEBUG)
451 LOG.debug("{} fill enter", SslConnection.this);
452 try
453 {
454
455 if (BufferUtil.hasContent(_decryptedInput))
456 return BufferUtil.flipPutFlip(_decryptedInput, buffer);
457
458
459 if (_encryptedInput == null)
460 _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
461 else
462 BufferUtil.compact(_encryptedInput);
463
464
465 ByteBuffer app_in;
466 if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
467 app_in = buffer;
468 else if (_decryptedInput == null)
469 app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
470 else
471 app_in = _decryptedInput;
472
473
474 while (true)
475 {
476
477 int net_filled = getEndPoint().fill(_encryptedInput);
478 if (DEBUG)
479 LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
480 if (net_filled > 0)
481 _underFlown = false;
482
483
484
485 int pos = BufferUtil.flipToFill(app_in);
486
487 SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
488
489 BufferUtil.flipToFlush(app_in, pos);
490 if (DEBUG)
491 LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
492
493
494 switch (unwrapResult.getStatus())
495 {
496 case BUFFER_OVERFLOW:
497 throw new IllegalStateException();
498
499 case CLOSED:
500
501 switch (_sslEngine.getHandshakeStatus())
502 {
503 case NOT_HANDSHAKING:
504
505 return -1;
506
507 case NEED_TASK:
508
509 _sslEngine.getDelegatedTask().run();
510 continue;
511
512 case NEED_WRAP:
513
514
515
516 return -1;
517 }
518 throw new IllegalStateException();
519
520 default:
521 if (unwrapResult.getStatus()==Status.BUFFER_UNDERFLOW)
522 _underFlown=true;
523
524
525 if (unwrapResult.bytesProduced() > 0)
526 {
527 if (app_in == buffer)
528 return unwrapResult.bytesProduced();
529 return BufferUtil.flipPutFlip(_decryptedInput, buffer);
530 }
531
532
533 switch (_sslEngine.getHandshakeStatus())
534 {
535 case NOT_HANDSHAKING:
536
537 if (net_filled < 0)
538 _sslEngine.closeInbound();
539
540 return 0;
541
542 case NEED_TASK:
543
544 _sslEngine.getDelegatedTask().run();
545 continue;
546
547 case NEED_WRAP:
548
549
550
551 if (buffer==__FLUSH_CALLED_FILL)
552 return 0;
553
554 _fillRequiresFlushToProgress = true;
555 flush(__FILL_CALLED_FLUSH);
556 if (BufferUtil.isEmpty(_encryptedOutput))
557 {
558
559 _fillRequiresFlushToProgress = false;
560 continue;
561 }
562 return 0;
563
564 case NEED_UNWRAP:
565
566 if (net_filled < 0)
567 {
568
569
570
571
572
573
574 if (isOpen())
575 _sslEngine.closeInbound();
576 }
577 else if (net_filled > 0)
578 {
579
580 continue;
581 }
582
583 return 0;
584
585 case FINISHED:
586 throw new IllegalStateException();
587 }
588 }
589 }
590 }
591 catch (SSLException e)
592 {
593 getEndPoint().close();
594 throw new EofException(e);
595 }
596 catch (Exception e)
597 {
598 getEndPoint().close();
599 throw e;
600 }
601 finally
602 {
603
604 if (_flushRequiresFillToProgress)
605 {
606 _flushRequiresFillToProgress = false;
607 getExecutor().execute(_runCompletWrite);
608 }
609
610 if (_encryptedInput != null && !_encryptedInput.hasRemaining())
611 {
612 _bufferPool.release(_encryptedInput);
613 _encryptedInput = null;
614 }
615 if (_decryptedInput != null && !_decryptedInput.hasRemaining())
616 {
617 _bufferPool.release(_decryptedInput);
618 _decryptedInput = null;
619 }
620 if (DEBUG)
621 LOG.debug("{} fill exit", SslConnection.this);
622 }
623 }
624
625 @Override
626 public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
627 {
628
629
630
631
632
633
634
635 if (DEBUG)
636 LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
637 int consumed=0;
638 try
639 {
640 if (_cannotAcceptMoreAppDataToFlush)
641 {
642 if (_sslEngine.isOutboundDone())
643 throw new EofException(new ClosedChannelException());
644 return false;
645 }
646
647
648 if (_encryptedOutput == null)
649 _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
650
651 while (true)
652 {
653
654
655 BufferUtil.compact(_encryptedOutput);
656 int pos = BufferUtil.flipToFill(_encryptedOutput);
657 SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
658 if (DEBUG)
659 LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
660 BufferUtil.flipToFlush(_encryptedOutput, pos);
661 if (wrapResult.bytesConsumed()>0)
662 consumed+=wrapResult.bytesConsumed();
663
664 boolean all_consumed=true;
665
666 for (ByteBuffer b : appOuts)
667 {
668 if (BufferUtil.isEmpty(b))
669 BufferUtil.clear(b);
670 else
671 all_consumed=false;
672 }
673
674
675 switch (wrapResult.getStatus())
676 {
677 case CLOSED:
678
679 if (BufferUtil.hasContent(_encryptedOutput))
680 {
681 _cannotAcceptMoreAppDataToFlush = true;
682 getEndPoint().flush(_encryptedOutput);
683
684
685
686 if (BufferUtil.hasContent(_encryptedOutput))
687 return false;
688 }
689
690
691 return all_consumed;
692
693 case BUFFER_UNDERFLOW:
694 throw new IllegalStateException();
695
696 default:
697 if (DEBUG)
698 LOG.debug("{} {} {}", this, wrapResult.getStatus(), BufferUtil.toDetailString(_encryptedOutput));
699
700
701 if (BufferUtil.hasContent(_encryptedOutput))
702 getEndPoint().flush(_encryptedOutput);
703
704
705 switch (_sslEngine.getHandshakeStatus())
706 {
707 case NOT_HANDSHAKING:
708
709 return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
710
711 case NEED_TASK:
712
713 _sslEngine.getDelegatedTask().run();
714 continue;
715
716 case NEED_WRAP:
717
718 continue;
719
720 case NEED_UNWRAP:
721
722
723 if (appOuts[0]!=__FILL_CALLED_FLUSH && !getFillInterest().isInterested())
724 {
725
726 _flushRequiresFillToProgress = true;
727 fill(__FLUSH_CALLED_FILL);
728
729 if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP)
730 continue;
731 }
732 return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
733
734 case FINISHED:
735 throw new IllegalStateException();
736
737 }
738 }
739 }
740 }
741 catch (Exception e)
742 {
743 getEndPoint().close();
744 throw e;
745 }
746 finally
747 {
748 if (DEBUG)
749 LOG.debug("{} flush exit, consumed {}", SslConnection.this, consumed);
750 releaseEncryptedOutputBuffer();
751 }
752 }
753
754 private void releaseEncryptedOutputBuffer()
755 {
756 if (!Thread.holdsLock(DecryptedEndPoint.this))
757 throw new IllegalStateException();
758 if (_encryptedOutput != null && !_encryptedOutput.hasRemaining())
759 {
760 _bufferPool.release(_encryptedOutput);
761 _encryptedOutput = null;
762 if (_sslEngine.isOutboundDone())
763 getEndPoint().shutdownOutput();
764 }
765 }
766
767 @Override
768 public void shutdownOutput()
769 {
770 _sslEngine.closeOutbound();
771 try
772 {
773 flush(BufferUtil.EMPTY_BUFFER);
774 }
775 catch (IOException e)
776 {
777 LOG.ignore(e);
778 getEndPoint().close();
779 }
780 }
781
782 @Override
783 public boolean isOutputShutdown()
784 {
785 return _sslEngine.isOutboundDone() || getEndPoint().isOutputShutdown();
786 }
787
788 @Override
789 public void close()
790 {
791 getEndPoint().close();
792 }
793
794 @Override
795 public boolean isOpen()
796 {
797 return getEndPoint().isOpen();
798 }
799
800 @Override
801 public Object getTransport()
802 {
803 return getEndPoint();
804 }
805
806 @Override
807 public boolean isInputShutdown()
808 {
809 return _sslEngine.isInboundDone();
810 }
811
812 @Override
813 public String toString()
814 {
815 return super.toString()+"->"+getEndPoint().toString();
816 }
817 }
818 }