1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.security;
16
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.nio.channels.SelectionKey;
20 import java.nio.channels.SocketChannel;
21
22 import javax.net.ssl.SSLEngine;
23 import javax.net.ssl.SSLEngineResult;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.SSLSession;
26 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
27
28 import org.mortbay.io.Buffer;
29 import org.mortbay.io.Buffers;
30 import org.mortbay.io.nio.NIOBuffer;
31 import org.mortbay.io.nio.SelectChannelEndPoint;
32 import org.mortbay.io.nio.SelectorManager;
33 import org.mortbay.jetty.nio.SelectChannelConnector;
34 import org.mortbay.log.Log;
35
36
37
38
39
40
41
42
43 public class SslHttpChannelEndPoint extends SelectChannelConnector.ConnectorEndPoint implements Runnable
44 {
45 private static final ByteBuffer[] __NO_BUFFERS={};
46
47 private Buffers _buffers;
48
49 private SSLEngine _engine;
50 private ByteBuffer _inBuffer;
51 private NIOBuffer _inNIOBuffer;
52 private ByteBuffer _outBuffer;
53 private NIOBuffer _outNIOBuffer;
54
55 private NIOBuffer[] _reuseBuffer=new NIOBuffer[2];
56 private ByteBuffer[] _gather=new ByteBuffer[2];
57
58 private boolean _closing=false;
59 private SSLEngineResult _result;
60 private String _last;
61
62
63 protected SSLSession _session;
64
65
66
67
68
69 public SslHttpChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
70 throws SSLException, IOException
71 {
72 super(channel,selectSet,key);
73 _buffers=buffers;
74
75
76 _engine=engine;
77 _session=engine.getSession();
78
79
80 _outNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
81 _outBuffer=_outNIOBuffer.getByteBuffer();
82 _inNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
83 _inBuffer=_inNIOBuffer.getByteBuffer();
84
85
86 }
87
88
89 public void dump()
90 {
91 System.err.println(_result);
92
93
94 }
95
96
97
98
99
100 protected void idleExpired()
101 {
102 try
103 {
104 _selectSet.getManager().dispatch(new Runnable()
105 {
106 public void run()
107 {
108 doIdleExpired();
109 }
110 });
111 }
112 catch(Exception e)
113 {
114 Log.ignore(e);
115 }
116 }
117
118
119 protected void doIdleExpired()
120 {
121
122 super.idleExpired();
123 }
124
125
126 public void close() throws IOException
127 {
128
129
130
131 _closing=true;
132 try
133 {
134 int tries=0;
135
136 while (_outNIOBuffer.length()>0)
137 {
138
139 if (tries++>100)
140 throw new IllegalStateException();
141 flush();
142 Thread.sleep(100);
143 }
144
145 _engine.closeOutbound();
146
147 loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()))
148 {
149
150 if (tries++>100)
151 throw new IllegalStateException();
152
153 if (_outNIOBuffer.length()>0)
154 {
155 flush();
156 Thread.sleep(100);
157 }
158
159 switch(_engine.getHandshakeStatus())
160 {
161 case FINISHED:
162 case NOT_HANDSHAKING:
163 break loop;
164
165 case NEED_UNWRAP:
166 Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
167 try
168 {
169 ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
170 if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
171 {
172
173 break loop;
174 }
175 }
176 catch(SSLException e)
177 {
178 Log.ignore(e);
179 }
180 finally
181 {
182 _buffers.returnBuffer(buffer);
183 }
184 break;
185
186 case NEED_TASK:
187 {
188 Runnable task;
189 while ((task=_engine.getDelegatedTask())!=null)
190 {
191 task.run();
192 }
193 break;
194 }
195
196 case NEED_WRAP:
197 {
198 if (_outNIOBuffer.length()>0)
199 flush();
200
201 try
202 {
203 _outNIOBuffer.compact();
204 int put=_outNIOBuffer.putIndex();
205 _outBuffer.position(put);
206 _result=null;
207 _last="close wrap";
208 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
209 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
210 }
211 finally
212 {
213 _outBuffer.position(0);
214 }
215
216 flush();
217
218 break;
219 }
220 }
221 }
222 }
223 catch(IOException e)
224 {
225 Log.ignore(e);
226 }
227 catch (InterruptedException e)
228 {
229 Log.ignore(e);
230 }
231 finally
232 {
233 super.close();
234
235 if (_inNIOBuffer!=null)
236 _buffers.returnBuffer(_inNIOBuffer);
237 if (_outNIOBuffer!=null)
238 _buffers.returnBuffer(_outNIOBuffer);
239 if (_reuseBuffer[0]!=null)
240 _buffers.returnBuffer(_reuseBuffer[0]);
241 if (_reuseBuffer[1]!=null)
242 _buffers.returnBuffer(_reuseBuffer[1]);
243 }
244 }
245
246
247
248
249 public int fill(Buffer buffer) throws IOException
250 {
251 ByteBuffer bbuf=extractInputBuffer(buffer);
252 int size=buffer.length();
253 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
254 synchronized (bbuf)
255 {
256 try
257 {
258 unwrap(bbuf);
259
260 int tries=0, wraps=0;
261 loop: while (true)
262 {
263
264 if (tries++>100)
265 throw new IllegalStateException();
266
267
268
269 if (_outNIOBuffer.length()>0)
270 flush();
271
272
273 switch(_engine.getHandshakeStatus())
274 {
275 case FINISHED:
276 case NOT_HANDSHAKING:
277 if (_closing)
278 return -1;
279 break loop;
280
281 case NEED_UNWRAP:
282 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
283 {
284
285 break loop;
286 }
287 break;
288
289 case NEED_TASK:
290 {
291 Runnable task;
292 while ((task=_engine.getDelegatedTask())!=null)
293 {
294
295 task.run();
296 }
297 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
298 HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && wraps==0)
299 {
300
301
302
303
304 return -1;
305 }
306 break;
307 }
308
309 case NEED_WRAP:
310 {
311 wraps++;
312 synchronized(_outBuffer)
313 {
314 try
315 {
316 _outNIOBuffer.compact();
317 int put=_outNIOBuffer.putIndex();
318 _outBuffer.position();
319 _result=null;
320 _last="fill wrap";
321 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
322 switch(_result.getStatus())
323 {
324 case BUFFER_OVERFLOW:
325 case BUFFER_UNDERFLOW:
326 Log.warn("wrap {}",_result);
327 case CLOSED:
328 _closing=true;
329 }
330
331
332 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
333 }
334 finally
335 {
336 _outBuffer.position(0);
337 }
338 }
339
340 flush();
341
342 break;
343 }
344 }
345 }
346 }
347 catch(SSLException e)
348 {
349 Log.warn(e.toString());
350 Log.debug(e);
351 throw e;
352 }
353 finally
354 {
355 buffer.setPutIndex(bbuf.position());
356 bbuf.position(0);
357 }
358 }
359 return buffer.length()-size;
360
361 }
362
363
364 public int flush(Buffer buffer) throws IOException
365 {
366 return flush(buffer,null,null);
367 }
368
369
370
371
372
373 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
374 {
375 int consumed=0;
376 int available=header.length();
377 if (buffer!=null)
378 available+=buffer.length();
379
380 int tries=0;
381 loop: while (true)
382 {
383
384 if (tries++>100)
385 throw new IllegalStateException();
386
387
388
389 if (_outNIOBuffer.length()>0)
390 flush();
391
392
393
394 switch(_engine.getHandshakeStatus())
395 {
396 case FINISHED:
397 case NOT_HANDSHAKING:
398
399 if (_closing || available==0)
400 {
401 if (consumed==0)
402 consumed= -1;
403 break loop;
404 }
405
406 int c=(header!=null && buffer!=null)?wrap(header,buffer):wrap(header);
407 if (c>0)
408 {
409 consumed+=c;
410 available-=c;
411 }
412 else if (c<0)
413 {
414 if (consumed==0)
415 consumed=-1;
416 break loop;
417 }
418
419 break;
420
421 case NEED_UNWRAP:
422 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
423 try
424 {
425 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
426 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
427 {
428
429 break loop;
430 }
431 }
432 finally
433 {
434 _buffers.returnBuffer(buf);
435 }
436
437 break;
438
439 case NEED_TASK:
440 {
441 Runnable task;
442 while ((task=_engine.getDelegatedTask())!=null)
443 {
444
445 task.run();
446 }
447 break;
448 }
449
450 case NEED_WRAP:
451 {
452 synchronized(_outBuffer)
453 {
454 try
455 {
456 _outNIOBuffer.compact();
457 int put=_outNIOBuffer.putIndex();
458 _outBuffer.position();
459 _result=null;
460 _last="flush wrap";
461 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
462 switch(_result.getStatus())
463 {
464 case BUFFER_OVERFLOW:
465 case BUFFER_UNDERFLOW:
466 Log.warn("wrap {}",_result);
467 case CLOSED:
468 _closing=true;
469 }
470
471 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
472 }
473 finally
474 {
475 _outBuffer.position(0);
476 }
477 }
478
479 flush();
480
481 break;
482 }
483 }
484 }
485
486 return consumed;
487 }
488
489
490
491 public void flush() throws IOException
492 {
493 while (_outNIOBuffer.length()>0)
494 {
495 int flushed=super.flush(_outNIOBuffer);
496
497
498 if (flushed==0)
499 {
500 Thread.yield();
501 flushed=super.flush(_outNIOBuffer);
502
503 }
504 }
505 }
506
507
508 private ByteBuffer extractInputBuffer(Buffer buffer)
509 {
510 assert buffer instanceof NIOBuffer;
511 NIOBuffer nbuf=(NIOBuffer)buffer;
512 ByteBuffer bbuf=nbuf.getByteBuffer();
513 bbuf.position(buffer.putIndex());
514 return bbuf;
515 }
516
517
518
519
520
521 private boolean unwrap(ByteBuffer buffer) throws IOException
522 {
523 if (_inNIOBuffer.hasContent())
524 _inNIOBuffer.compact();
525 else
526 _inNIOBuffer.clear();
527
528 int total_filled=0;
529 while (_inNIOBuffer.space()>0 && super.isOpen())
530 {
531 try
532 {
533 int filled=super.fill(_inNIOBuffer);
534
535 if (filled<=0)
536 break;
537 total_filled+=filled;
538 }
539 catch(IOException e)
540 {
541 if (_inNIOBuffer.length()==0)
542 throw e;
543 break;
544 }
545 }
546
547
548
549 if (_inNIOBuffer.length()==0)
550 {
551 if(!isOpen())
552 throw new org.mortbay.jetty.EofException();
553 return false;
554 }
555
556 try
557 {
558 _inBuffer.position(_inNIOBuffer.getIndex());
559 _inBuffer.limit(_inNIOBuffer.putIndex());
560 _result=null;
561 _last="unwrap";
562 _result=_engine.unwrap(_inBuffer,buffer);
563
564 _inNIOBuffer.skip(_result.bytesConsumed());
565 }
566 finally
567 {
568 _inBuffer.position(0);
569 _inBuffer.limit(_inBuffer.capacity());
570 }
571
572
573 switch(_result.getStatus())
574 {
575 case BUFFER_OVERFLOW:
576 case BUFFER_UNDERFLOW:
577 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
578 return (total_filled > 0);
579
580 case CLOSED:
581 _closing=true;
582 case OK:
583 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
584
585 return progress;
586 default:
587 Log.warn("unwrap "+_result);
588 throw new IOException(_result.toString());
589 }
590 }
591
592
593
594 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
595 {
596 NIOBuffer nBuf=null;
597
598 if (buffer.buffer() instanceof NIOBuffer)
599 {
600 nBuf=(NIOBuffer)buffer.buffer();
601 return nBuf.getByteBuffer();
602 }
603 else
604 {
605 if (_reuseBuffer[n]==null)
606 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
607 NIOBuffer buf = _reuseBuffer[n];
608 buf.clear();
609 buf.put(buffer);
610 return buf.getByteBuffer();
611 }
612 }
613
614
615 private int wrap(Buffer header, Buffer buffer) throws IOException
616 {
617 _gather[0]=extractOutputBuffer(header,0);
618 synchronized(_gather[0])
619 {
620 _gather[0].position(header.getIndex());
621 _gather[0].limit(header.putIndex());
622
623 _gather[1]=extractOutputBuffer(buffer,1);
624
625 synchronized(_gather[1])
626 {
627 _gather[1].position(buffer.getIndex());
628 _gather[1].limit(buffer.putIndex());
629
630 synchronized(_outBuffer)
631 {
632 int consumed=0;
633 try
634 {
635 _outNIOBuffer.clear();
636 _outBuffer.position(0);
637 _outBuffer.limit(_outBuffer.capacity());
638
639 _result=null;
640 _last="wrap wrap";
641 _result=_engine.wrap(_gather,_outBuffer);
642
643 _outNIOBuffer.setGetIndex(0);
644 _outNIOBuffer.setPutIndex(_result.bytesProduced());
645 consumed=_result.bytesConsumed();
646 }
647 finally
648 {
649 _outBuffer.position(0);
650
651 if (consumed>0 && header!=null)
652 {
653 int len=consumed<header.length()?consumed:header.length();
654 header.skip(len);
655 consumed-=len;
656 _gather[0].position(0);
657 _gather[0].limit(_gather[0].capacity());
658 }
659 if (consumed>0 && buffer!=null)
660 {
661 int len=consumed<buffer.length()?consumed:buffer.length();
662 buffer.skip(len);
663 consumed-=len;
664 _gather[1].position(0);
665 _gather[1].limit(_gather[1].capacity());
666 }
667 assert consumed==0;
668 }
669 }
670 }
671 }
672
673
674 switch(_result.getStatus())
675 {
676 case BUFFER_OVERFLOW:
677 case BUFFER_UNDERFLOW:
678 Log.warn("wrap {}",_result);
679
680 case OK:
681 return _result.bytesConsumed();
682 case CLOSED:
683 _closing=true;
684 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
685
686 default:
687 Log.warn("wrap "+_result);
688 throw new IOException(_result.toString());
689 }
690 }
691
692
693 private int wrap(Buffer header) throws IOException
694 {
695 _gather[0]=extractOutputBuffer(header,0);
696 synchronized(_gather[0])
697 {
698 _gather[0].position(header.getIndex());
699 _gather[0].limit(header.putIndex());
700
701 int consumed=0;
702 synchronized(_outBuffer)
703 {
704 try
705 {
706 _outNIOBuffer.clear();
707 _outBuffer.position(0);
708 _outBuffer.limit(_outBuffer.capacity());
709 _result=null;
710 _last="wrap wrap";
711 _result=_engine.wrap(_gather[0],_outBuffer);
712
713 _outNIOBuffer.setGetIndex(0);
714 _outNIOBuffer.setPutIndex(_result.bytesProduced());
715 consumed=_result.bytesConsumed();
716 }
717 finally
718 {
719 _outBuffer.position(0);
720
721 if (consumed>0 && header!=null)
722 {
723 int len=consumed<header.length()?consumed:header.length();
724 header.skip(len);
725 consumed-=len;
726 _gather[0].position(0);
727 _gather[0].limit(_gather[0].capacity());
728 }
729 assert consumed==0;
730 }
731 }
732 }
733 switch(_result.getStatus())
734 {
735 case BUFFER_OVERFLOW:
736 case BUFFER_UNDERFLOW:
737 Log.warn("wrap {}",_result);
738
739 case OK:
740 return _result.bytesConsumed();
741 case CLOSED:
742 _closing=true;
743 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
744
745 default:
746 Log.warn("wrap "+_result);
747 throw new IOException(_result.toString());
748 }
749 }
750
751
752 public boolean isBufferingInput()
753 {
754 return _inNIOBuffer.hasContent();
755 }
756
757
758 public boolean isBufferingOutput()
759 {
760 return _outNIOBuffer.hasContent();
761 }
762
763
764 public boolean isBufferred()
765 {
766 return true;
767 }
768
769
770 public SSLEngine getSSLEngine()
771 {
772 return _engine;
773 }
774
775
776 public String toString()
777 {
778 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" last "+_last+" "+_result;
779 }
780 }