1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.io.nio;
15
16 import java.io.IOException;
17 import java.nio.ByteBuffer;
18 import java.nio.channels.SelectionKey;
19 import java.nio.channels.SocketChannel;
20
21 import javax.net.ssl.SSLEngine;
22 import javax.net.ssl.SSLEngineResult;
23 import javax.net.ssl.SSLException;
24 import javax.net.ssl.SSLSession;
25 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
26
27 import org.eclipse.jetty.io.Buffer;
28 import org.eclipse.jetty.io.Buffers;
29 import org.eclipse.jetty.io.EofException;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32
33
34
35
36
37
38
39
40
41
42
43 public class SslSelectChannelEndPoint extends SelectChannelEndPoint
44 {
45 static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl");
46
47 private static final ByteBuffer[] __NO_BUFFERS={};
48
49 private final Buffers _buffers;
50
51 private final SSLEngine _engine;
52 private final SSLSession _session;
53 private volatile NIOBuffer _inNIOBuffer;
54 private volatile NIOBuffer _outNIOBuffer;
55
56 private final ByteBuffer[] _gather=new ByteBuffer[2];
57
58 private boolean _closing=false;
59 private SSLEngineResult _result;
60
61 private boolean _handshook=false;
62 private boolean _allowRenegotiate=false;
63
64 private final boolean _debug = __log.isDebugEnabled();
65
66
67 public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime)
68 throws IOException
69 {
70 super(channel,selectSet,key, maxIdleTime);
71 _buffers=buffers;
72
73
74 _engine=engine;
75 _session=engine.getSession();
76
77 if (_debug) __log.debug(_session+" channel="+channel);
78 }
79
80
81 public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
82 throws IOException
83 {
84 super(channel,selectSet,key);
85 _buffers=buffers;
86
87
88 _engine=engine;
89 _session=engine.getSession();
90
91 if (_debug) __log.debug(_session+" channel="+channel);
92 }
93
94 int _outCount;
95
96
97 private void needOutBuffer()
98 {
99 synchronized (this)
100 {
101 _outCount++;
102 if (_outNIOBuffer==null)
103 _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
104 }
105 }
106
107
108 private void freeOutBuffer()
109 {
110 synchronized (this)
111 {
112 if (--_outCount<=0 && _outNIOBuffer!=null && _outNIOBuffer.length()==0)
113 {
114 _buffers.returnBuffer(_outNIOBuffer);
115 _outNIOBuffer=null;
116 _outCount=0;
117 }
118 }
119 }
120
121 int _inCount;
122
123 private void needInBuffer()
124 {
125 synchronized (this)
126 {
127 _inCount++;
128 if(_inNIOBuffer==null)
129 _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
130 }
131 }
132
133
134 private void freeInBuffer()
135 {
136 synchronized (this)
137 {
138 if (--_inCount<=0 &&_inNIOBuffer!=null && _inNIOBuffer.length()==0)
139 {
140 _buffers.returnBuffer(_inNIOBuffer);
141 _inNIOBuffer=null;
142 _inCount=0;
143 }
144 }
145 }
146
147
148
149
150
151 public boolean isAllowRenegotiate()
152 {
153 return _allowRenegotiate;
154 }
155
156
157
158
159
160
161
162
163
164 public void setAllowRenegotiate(boolean allowRenegotiate)
165 {
166 _allowRenegotiate = allowRenegotiate;
167 }
168
169
170
171 public void dump()
172 {
173 Log.info(""+_result);
174 }
175
176 @Override
177 public void shutdownOutput() throws IOException
178 {
179
180 long end=System.currentTimeMillis()+((SocketChannel)_channel).socket().getSoTimeout();
181 try
182 {
183 while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end)
184 {
185 flush();
186 if (isBufferingOutput())
187 {
188 Thread.sleep(100);
189 flush();
190 }
191 }
192
193 _engine.closeOutbound();
194
195 loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
196 {
197 while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
198 {
199 flush();
200 if (isBufferingOutput())
201 Thread.sleep(100);
202 }
203
204 if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
205 switch(_engine.getHandshakeStatus())
206 {
207 case FINISHED:
208 case NOT_HANDSHAKING:
209 _handshook=true;
210 break loop;
211
212 case NEED_UNWRAP:
213 Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
214 try
215 {
216 ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
217 if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
218 {
219 break loop;
220 }
221 }
222 catch(SSLException e)
223 {
224 Log.ignore(e);
225 }
226 finally
227 {
228 _buffers.returnBuffer(buffer);
229 }
230 break;
231
232 case NEED_TASK:
233 {
234 Runnable task;
235 while ((task=_engine.getDelegatedTask())!=null)
236 {
237 task.run();
238 }
239 break;
240 }
241
242 case NEED_WRAP:
243 {
244 needOutBuffer();
245 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
246 try
247 {
248 if (_outNIOBuffer.length()>0)
249 flush();
250 _outNIOBuffer.compact();
251 int put=_outNIOBuffer.putIndex();
252 out_buffer.position(put);
253 _result=null;
254 _result=_engine.wrap(__NO_BUFFERS,out_buffer);
255 if (_debug) __log.debug(_session+" close wrap "+_result);
256 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
257 }
258 finally
259 {
260 out_buffer.position(0);
261 freeOutBuffer();
262 }
263
264 break;
265 }
266 }
267 }
268 }
269 catch (Exception e)
270 {
271 Log.debug(e);
272 super.close();
273 }
274 }
275
276
277
278 @Override
279 public void close() throws IOException
280 {
281 try
282 {
283 _closing=true;
284 shutdownOutput();
285 }
286 catch(IOException e)
287 {
288 Log.ignore(e);
289 }
290 finally
291 {
292 super.close();
293 }
294 }
295
296
297
298
299
300
301
302
303 @Override
304 public int fill(Buffer buffer) throws IOException
305 {
306
307
308
309 final ByteBuffer bbuf=extractInputBuffer(buffer);
310
311
312 int size=buffer.length();
313
314 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
315
316 synchronized (bbuf)
317 {
318 try
319 {
320
321
322
323 unwrap(bbuf);
324
325
326
327 int wraps=0;
328 loop: while (true)
329 {
330
331 if (isBufferingOutput())
332 {
333
334
335
336 flush();
337
338
339
340
341
342 if (isBufferingOutput())
343 break loop;
344 }
345
346
347 switch(_engine.getHandshakeStatus())
348 {
349 case FINISHED:
350 case NOT_HANDSHAKING:
351
352
353 if (_closing)
354 return -1;
355
356
357 break loop;
358
359 case NEED_UNWRAP:
360 checkRenegotiate();
361
362 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
363 {
364
365
366
367 break loop;
368 }
369
370 break;
371
372 case NEED_TASK:
373 {
374
375
376 Runnable task;
377 while ((task=_engine.getDelegatedTask())!=null)
378 {
379 task.run();
380 }
381
382
383 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
384 _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
385 {
386
387
388
389
390 if (_debug) __log.warn(_session+" JETTY-567");
391 return -1;
392 }
393 break;
394 }
395
396 case NEED_WRAP:
397 {
398 checkRenegotiate();
399
400
401 wraps++;
402 needOutBuffer();
403 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
404 synchronized(out_buffer)
405 {
406 try
407 {
408
409
410 _outNIOBuffer.compact();
411 int put=_outNIOBuffer.putIndex();
412 out_buffer.position();
413 _result=null;
414 _result=_engine.wrap(__NO_BUFFERS,out_buffer);
415 if (_debug) __log.debug(_session+" fill wrap "+_result);
416 switch(_result.getStatus())
417 {
418 case BUFFER_OVERFLOW:
419 case BUFFER_UNDERFLOW:
420 Log.warn("wrap {}",_result);
421 case CLOSED:
422 _closing=true;
423 }
424
425 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
426 }
427 finally
428 {
429 out_buffer.position(0);
430 }
431 }
432
433
434 flush();
435 freeOutBuffer();
436
437 break;
438 }
439 }
440 }
441 }
442 catch(SSLException e)
443 {
444 Log.warn(e.toString());
445 Log.debug(e);
446 throw e;
447 }
448 finally
449 {
450
451 buffer.setPutIndex(bbuf.position());
452 bbuf.position(0);
453 }
454
455
456 int filled=buffer.length()-size;
457 if (filled>0)
458 _handshook=true;
459 return filled;
460 }
461 }
462
463
464 @Override
465 public int flush(Buffer buffer) throws IOException
466 {
467 return flush(buffer,null,null);
468 }
469
470
471
472
473
474 @Override
475 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
476 {
477 int consumed=0;
478 int available=header.length();
479 if (buffer!=null)
480 available+=buffer.length();
481
482 needOutBuffer();
483 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
484 loop: while (true)
485 {
486 if (_outNIOBuffer.length()>0)
487 {
488 flush();
489 if (isBufferingOutput())
490 break loop;
491 }
492
493 switch(_engine.getHandshakeStatus())
494 {
495 case FINISHED:
496 case NOT_HANDSHAKING:
497 if (_closing || available==0)
498 {
499 if (consumed==0)
500 consumed= -1;
501 break loop;
502 }
503
504 int c;
505 if (header!=null && header.length()>0)
506 {
507 if (buffer!=null && buffer.length()>0)
508 c=wrap(header,buffer);
509 else
510 c=wrap(header);
511 }
512 else
513 c=wrap(buffer);
514
515
516 if (c>0)
517 {
518 _handshook=true;
519 consumed+=c;
520 available-=c;
521 }
522 else if (c<0)
523 {
524 if (consumed==0)
525 consumed=-1;
526 break loop;
527 }
528
529 break;
530
531 case NEED_UNWRAP:
532 checkRenegotiate();
533 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
534 try
535 {
536 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
537 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
538 {
539 break loop;
540 }
541 }
542 finally
543 {
544 _buffers.returnBuffer(buf);
545 }
546
547 break;
548
549 case NEED_TASK:
550 {
551 Runnable task;
552 while ((task=_engine.getDelegatedTask())!=null)
553 {
554 task.run();
555 }
556 break;
557 }
558
559 case NEED_WRAP:
560 {
561 checkRenegotiate();
562 synchronized(out_buffer)
563 {
564 try
565 {
566 _outNIOBuffer.compact();
567 int put=_outNIOBuffer.putIndex();
568 out_buffer.position();
569 _result=null;
570 _result=_engine.wrap(__NO_BUFFERS,out_buffer);
571 if (_debug) __log.debug(_session+" flush wrap "+_result);
572 switch(_result.getStatus())
573 {
574 case BUFFER_OVERFLOW:
575 case BUFFER_UNDERFLOW:
576 Log.warn("unwrap {}",_result);
577 case CLOSED:
578 _closing=true;
579 }
580 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
581 }
582 finally
583 {
584 out_buffer.position(0);
585 }
586 }
587
588 flush();
589 if (isBufferingOutput())
590 break loop;
591
592 break;
593 }
594 }
595 }
596
597 freeOutBuffer();
598 return consumed;
599 }
600
601
602 @Override
603 public void flush() throws IOException
604 {
605 if (_outNIOBuffer==null)
606 return;
607
608 int len=_outNIOBuffer.length();
609 if (isBufferingOutput())
610 {
611 int flushed=super.flush(_outNIOBuffer);
612 if (_debug) __log.debug(_session+" Flushed "+flushed+"/"+len);
613 if (isBufferingOutput())
614 {
615
616 Thread.yield();
617 flushed=super.flush(_outNIOBuffer);
618 if (_debug) __log.debug(_session+" flushed "+flushed+"/"+len);
619 }
620 }
621 }
622
623
624 private void checkRenegotiate() throws IOException
625 {
626 if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
627 {
628 Log.warn("SSL renegotiate denied: "+_channel);
629 close();
630 }
631 }
632
633
634 private ByteBuffer extractInputBuffer(Buffer buffer)
635 {
636 assert buffer instanceof NIOBuffer;
637 NIOBuffer nbuf=(NIOBuffer)buffer;
638 ByteBuffer bbuf=nbuf.getByteBuffer();
639 bbuf.position(buffer.putIndex());
640 return bbuf;
641 }
642
643
644
645
646
647 private boolean unwrap(ByteBuffer buffer) throws IOException
648 {
649 needInBuffer();
650 ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
651
652 if (_inNIOBuffer.hasContent())
653 _inNIOBuffer.compact();
654 else
655 _inNIOBuffer.clear();
656
657 int total_filled=0;
658
659
660 while (_inNIOBuffer.space()>0 && super.isOpen())
661 {
662 try
663 {
664 int filled=super.fill(_inNIOBuffer);
665 if (_debug) __log.debug(_session+" unwrap filled "+filled);
666
667
668 if (filled<=0)
669 break;
670 total_filled+=filled;
671 }
672 catch(IOException e)
673 {
674 if (_inNIOBuffer.length()==0)
675 {
676 if (_outNIOBuffer!=null)
677 {
678 _outNIOBuffer.clear();
679 freeOutBuffer();
680 }
681 throw e;
682 }
683 break;
684 }
685 }
686
687
688 if (total_filled==0 && _inNIOBuffer.length()==0)
689 {
690 if(!isOpen())
691 {
692 freeOutBuffer();
693 throw new EofException();
694 }
695 return false;
696 }
697
698
699 try
700 {
701
702
703 in_buffer.position(_inNIOBuffer.getIndex());
704 in_buffer.limit(_inNIOBuffer.putIndex());
705 _result=null;
706
707
708 _result=_engine.unwrap(in_buffer,buffer);
709 if (_debug) __log.debug(_session+" unwrap unwrap "+_result);
710
711
712 _inNIOBuffer.skip(_result.bytesConsumed());
713 }
714 finally
715 {
716
717 in_buffer.position(0);
718 in_buffer.limit(in_buffer.capacity());
719 freeInBuffer();
720 }
721
722
723 switch(_result.getStatus())
724 {
725 case BUFFER_OVERFLOW:
726 throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit());
727
728 case BUFFER_UNDERFLOW:
729
730
731
732
733 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
734 if(!isOpen())
735 {
736 _inNIOBuffer.clear();
737 if (_outNIOBuffer!=null)
738 _outNIOBuffer.clear();
739 throw new EofException();
740 }
741 return (total_filled > 0);
742
743 case CLOSED:
744 _closing=true;
745
746 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
747 case OK:
748
749 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
750 default:
751 Log.warn("unwrap "+_result);
752 throw new IOException(_result.toString());
753 }
754 }
755
756
757
758 private ByteBuffer extractOutputBuffer(Buffer buffer)
759 {
760 if (buffer.buffer() instanceof NIOBuffer)
761 return ((NIOBuffer)buffer.buffer()).getByteBuffer();
762
763 return ByteBuffer.wrap(buffer.array());
764 }
765
766
767 private int wrap(final Buffer header, final Buffer buffer) throws IOException
768 {
769 _gather[0]=extractOutputBuffer(header);
770
771 synchronized(_gather[0])
772 {
773 _gather[0].position(header.getIndex());
774 _gather[0].limit(header.putIndex());
775
776 _gather[1]=extractOutputBuffer(buffer);
777
778 synchronized(_gather[1])
779 {
780 _gather[1].position(buffer.getIndex());
781 _gather[1].limit(buffer.putIndex());
782
783 needOutBuffer();
784 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
785 synchronized(out_buffer)
786 {
787 int consumed=0;
788 try
789 {
790 _outNIOBuffer.clear();
791 out_buffer.position(0);
792 out_buffer.limit(out_buffer.capacity());
793
794 _result=null;
795 _result=_engine.wrap(_gather,out_buffer);
796 if (_debug) __log.debug(_session+" wrap wrap "+_result);
797 _outNIOBuffer.setGetIndex(0);
798 _outNIOBuffer.setPutIndex(_result.bytesProduced());
799 consumed=_result.bytesConsumed();
800 }
801 finally
802 {
803 out_buffer.position(0);
804
805 if (consumed>0)
806 {
807 int len=consumed<header.length()?consumed:header.length();
808 header.skip(len);
809 consumed-=len;
810 _gather[0].position(0);
811 _gather[0].limit(_gather[0].capacity());
812 }
813 if (consumed>0)
814 {
815 int len=consumed<buffer.length()?consumed:buffer.length();
816 buffer.skip(len);
817 consumed-=len;
818 _gather[1].position(0);
819 _gather[1].limit(_gather[1].capacity());
820 }
821 assert consumed==0;
822
823 freeOutBuffer();
824 }
825 }
826 }
827 }
828
829
830 switch(_result.getStatus())
831 {
832 case BUFFER_OVERFLOW:
833 case BUFFER_UNDERFLOW:
834 Log.warn("unwrap {}",_result);
835
836 case OK:
837 return _result.bytesConsumed();
838 case CLOSED:
839 _closing=true;
840 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
841
842 default:
843 Log.warn("wrap "+_result);
844 throw new IOException(_result.toString());
845 }
846 }
847
848
849 private int wrap(final Buffer buffer) throws IOException
850 {
851 _gather[0]=extractOutputBuffer(buffer);
852 synchronized(_gather[0])
853 {
854 ByteBuffer bb;
855
856 _gather[0].position(buffer.getIndex());
857 _gather[0].limit(buffer.putIndex());
858
859 int consumed=0;
860 needOutBuffer();
861 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
862 synchronized(out_buffer)
863 {
864 try
865 {
866 _outNIOBuffer.clear();
867 out_buffer.position(0);
868 out_buffer.limit(out_buffer.capacity());
869 _result=null;
870 _result=_engine.wrap(_gather[0],out_buffer);
871 if (_debug) __log.debug(_session+" wrap wrap "+_result);
872 _outNIOBuffer.setGetIndex(0);
873 _outNIOBuffer.setPutIndex(_result.bytesProduced());
874 consumed=_result.bytesConsumed();
875 }
876 finally
877 {
878 out_buffer.position(0);
879
880 if (consumed>0)
881 {
882 int len=consumed<buffer.length()?consumed:buffer.length();
883 buffer.skip(len);
884 consumed-=len;
885 _gather[0].position(0);
886 _gather[0].limit(_gather[0].capacity());
887 }
888 assert consumed==0;
889
890 freeOutBuffer();
891 }
892 }
893 }
894 switch(_result.getStatus())
895 {
896 case BUFFER_OVERFLOW:
897 case BUFFER_UNDERFLOW:
898 Log.warn("unwrap {}",_result);
899
900 case OK:
901 return _result.bytesConsumed();
902 case CLOSED:
903 _closing=true;
904 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
905
906 default:
907 Log.warn("wrap "+_result);
908 throw new IOException(_result.toString());
909 }
910 }
911
912
913 @Override
914 public boolean isBufferingInput()
915 {
916 final Buffer in = _inNIOBuffer;
917 return in==null?false:_inNIOBuffer.hasContent();
918 }
919
920
921 @Override
922 public boolean isBufferingOutput()
923 {
924 final NIOBuffer b=_outNIOBuffer;
925 return b==null?false:b.hasContent();
926 }
927
928
929 @Override
930 public boolean isBufferred()
931 {
932 return true;
933 }
934
935
936 public SSLEngine getSSLEngine()
937 {
938 return _engine;
939 }
940
941
942 @Override
943 public void setWritable(boolean writable)
944 {
945
946 if (writable || !HandshakeStatus.NEED_UNWRAP.equals(_engine.getHandshakeStatus()) || super.isBufferingOutput())
947 super.setWritable(writable);
948 }
949
950
951 @Override
952 public void scheduleWrite()
953 {
954
955 if (!HandshakeStatus.NEED_UNWRAP.equals(_engine.getHandshakeStatus()) || super.isBufferingOutput())
956 super.scheduleWrite();
957 }
958
959
960 @Override
961 public String toString()
962 {
963 final NIOBuffer i=_inNIOBuffer;
964 final NIOBuffer o=_outNIOBuffer;
965 return "SSL"+super.toString()+","+_engine.getHandshakeStatus()+", in/out="+
966 (i==null?0:_inNIOBuffer.length())+"/"+(o==null?0:o.length())+" "+_result;
967 }
968 }