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