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 return -1;
303 }
304 break;
305 }
306
307 case NEED_WRAP:
308 {
309 wraps++;
310 synchronized(_outBuffer)
311 {
312 try
313 {
314 _outNIOBuffer.compact();
315 int put=_outNIOBuffer.putIndex();
316 _outBuffer.position();
317 _result=null;
318 _last="fill wrap";
319 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
320 switch(_result.getStatus())
321 {
322 case BUFFER_OVERFLOW:
323 case BUFFER_UNDERFLOW:
324 Log.warn("wrap {}",_result);
325 case CLOSED:
326 _closing=true;
327 }
328
329
330 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
331 }
332 finally
333 {
334 _outBuffer.position(0);
335 }
336 }
337
338 flush();
339
340 break;
341 }
342 }
343 }
344 }
345 catch(SSLException e)
346 {
347 Log.warn(e.toString());
348 Log.debug(e);
349 throw e;
350 }
351 finally
352 {
353 buffer.setPutIndex(bbuf.position());
354 bbuf.position(0);
355 }
356 }
357 return buffer.length()-size;
358
359 }
360
361
362 public int flush(Buffer buffer) throws IOException
363 {
364 return flush(buffer,null,null);
365 }
366
367
368
369
370
371 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
372 {
373 int consumed=0;
374 int available=header.length();
375 if (buffer!=null)
376 available+=buffer.length();
377
378 int tries=0;
379 loop: while (true)
380 {
381
382 if (tries++>100)
383 throw new IllegalStateException();
384
385
386
387 if (_outNIOBuffer.length()>0)
388 flush();
389
390
391
392 switch(_engine.getHandshakeStatus())
393 {
394 case FINISHED:
395 case NOT_HANDSHAKING:
396
397 if (_closing || available==0)
398 {
399 if (consumed==0)
400 consumed= -1;
401 break loop;
402 }
403
404 int c=(header!=null && buffer!=null)?wrap(header,buffer):wrap(header);
405 if (c>0)
406 {
407 consumed+=c;
408 available-=c;
409 }
410 else if (c<0)
411 {
412 if (consumed==0)
413 consumed=-1;
414 break loop;
415 }
416
417 break;
418
419 case NEED_UNWRAP:
420 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
421 try
422 {
423 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
424 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
425 {
426
427 break loop;
428 }
429 }
430 finally
431 {
432 _buffers.returnBuffer(buf);
433 }
434
435 break;
436
437 case NEED_TASK:
438 {
439 Runnable task;
440 while ((task=_engine.getDelegatedTask())!=null)
441 {
442
443 task.run();
444 }
445 break;
446 }
447
448 case NEED_WRAP:
449 {
450 synchronized(_outBuffer)
451 {
452 try
453 {
454 _outNIOBuffer.compact();
455 int put=_outNIOBuffer.putIndex();
456 _outBuffer.position();
457 _result=null;
458 _last="flush wrap";
459 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
460 switch(_result.getStatus())
461 {
462 case BUFFER_OVERFLOW:
463 case BUFFER_UNDERFLOW:
464 Log.warn("wrap {}",_result);
465 case CLOSED:
466 _closing=true;
467 }
468
469 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
470 }
471 finally
472 {
473 _outBuffer.position(0);
474 }
475 }
476
477 flush();
478
479 break;
480 }
481 }
482 }
483
484 return consumed;
485 }
486
487
488
489 public void flush() throws IOException
490 {
491 while (_outNIOBuffer.length()>0)
492 {
493 int flushed=super.flush(_outNIOBuffer);
494
495
496 if (flushed==0)
497 {
498 Thread.yield();
499 flushed=super.flush(_outNIOBuffer);
500
501 }
502 }
503 }
504
505
506 private ByteBuffer extractInputBuffer(Buffer buffer)
507 {
508 assert buffer instanceof NIOBuffer;
509 NIOBuffer nbuf=(NIOBuffer)buffer;
510 ByteBuffer bbuf=nbuf.getByteBuffer();
511 bbuf.position(buffer.putIndex());
512 return bbuf;
513 }
514
515
516
517
518
519 private boolean unwrap(ByteBuffer buffer) throws IOException
520 {
521 if (_inNIOBuffer.hasContent())
522 _inNIOBuffer.compact();
523 else
524 _inNIOBuffer.clear();
525
526 int total_filled=0;
527 while (_inNIOBuffer.space()>0 && super.isOpen())
528 {
529 try
530 {
531 int filled=super.fill(_inNIOBuffer);
532
533 if (filled<=0)
534 break;
535 total_filled+=filled;
536 }
537 catch(IOException e)
538 {
539 if (_inNIOBuffer.length()==0)
540 throw e;
541 break;
542 }
543 }
544
545
546
547 if (_inNIOBuffer.length()==0)
548 {
549 if(!isOpen())
550 throw new org.mortbay.jetty.EofException();
551 return false;
552 }
553
554 try
555 {
556 _inBuffer.position(_inNIOBuffer.getIndex());
557 _inBuffer.limit(_inNIOBuffer.putIndex());
558 _result=null;
559 _last="unwrap";
560 _result=_engine.unwrap(_inBuffer,buffer);
561
562 _inNIOBuffer.skip(_result.bytesConsumed());
563 }
564 finally
565 {
566 _inBuffer.position(0);
567 _inBuffer.limit(_inBuffer.capacity());
568 }
569
570
571 switch(_result.getStatus())
572 {
573 case BUFFER_OVERFLOW:
574 case BUFFER_UNDERFLOW:
575 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
576 return (total_filled > 0);
577
578 case CLOSED:
579 _closing=true;
580 case OK:
581 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
582
583 return progress;
584 default:
585 Log.warn("unwrap "+_result);
586 throw new IOException(_result.toString());
587 }
588 }
589
590
591
592 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
593 {
594 NIOBuffer nBuf=null;
595
596 if (buffer.buffer() instanceof NIOBuffer)
597 {
598 nBuf=(NIOBuffer)buffer.buffer();
599 return nBuf.getByteBuffer();
600 }
601 else
602 {
603 if (_reuseBuffer[n]==null)
604 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
605 NIOBuffer buf = _reuseBuffer[n];
606 buf.clear();
607 buf.put(buffer);
608 return buf.getByteBuffer();
609 }
610 }
611
612
613 private int wrap(Buffer header, Buffer buffer) throws IOException
614 {
615 _gather[0]=extractOutputBuffer(header,0);
616 synchronized(_gather[0])
617 {
618 _gather[0].position(header.getIndex());
619 _gather[0].limit(header.putIndex());
620
621 _gather[1]=extractOutputBuffer(buffer,1);
622
623 synchronized(_gather[1])
624 {
625 _gather[1].position(buffer.getIndex());
626 _gather[1].limit(buffer.putIndex());
627
628 synchronized(_outBuffer)
629 {
630 int consumed=0;
631 try
632 {
633 _outNIOBuffer.clear();
634 _outBuffer.position(0);
635 _outBuffer.limit(_outBuffer.capacity());
636
637 _result=null;
638 _last="wrap wrap";
639 _result=_engine.wrap(_gather,_outBuffer);
640
641 _outNIOBuffer.setGetIndex(0);
642 _outNIOBuffer.setPutIndex(_result.bytesProduced());
643 consumed=_result.bytesConsumed();
644 }
645 finally
646 {
647 _outBuffer.position(0);
648
649 if (consumed>0 && header!=null)
650 {
651 int len=consumed<header.length()?consumed:header.length();
652 header.skip(len);
653 consumed-=len;
654 _gather[0].position(0);
655 _gather[0].limit(_gather[0].capacity());
656 }
657 if (consumed>0 && buffer!=null)
658 {
659 int len=consumed<buffer.length()?consumed:buffer.length();
660 buffer.skip(len);
661 consumed-=len;
662 _gather[1].position(0);
663 _gather[1].limit(_gather[1].capacity());
664 }
665 assert consumed==0;
666 }
667 }
668 }
669 }
670
671
672 switch(_result.getStatus())
673 {
674 case BUFFER_OVERFLOW:
675 case BUFFER_UNDERFLOW:
676 Log.warn("wrap {}",_result);
677
678 case OK:
679 return _result.bytesConsumed();
680 case CLOSED:
681 _closing=true;
682 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
683
684 default:
685 Log.warn("wrap "+_result);
686 throw new IOException(_result.toString());
687 }
688 }
689
690
691 private int wrap(Buffer header) throws IOException
692 {
693 _gather[0]=extractOutputBuffer(header,0);
694 synchronized(_gather[0])
695 {
696 _gather[0].position(header.getIndex());
697 _gather[0].limit(header.putIndex());
698
699 int consumed=0;
700 synchronized(_outBuffer)
701 {
702 try
703 {
704 _outNIOBuffer.clear();
705 _outBuffer.position(0);
706 _outBuffer.limit(_outBuffer.capacity());
707 _result=null;
708 _last="wrap wrap";
709 _result=_engine.wrap(_gather[0],_outBuffer);
710
711 _outNIOBuffer.setGetIndex(0);
712 _outNIOBuffer.setPutIndex(_result.bytesProduced());
713 consumed=_result.bytesConsumed();
714 }
715 finally
716 {
717 _outBuffer.position(0);
718
719 if (consumed>0 && header!=null)
720 {
721 int len=consumed<header.length()?consumed:header.length();
722 header.skip(len);
723 consumed-=len;
724 _gather[0].position(0);
725 _gather[0].limit(_gather[0].capacity());
726 }
727 assert consumed==0;
728 }
729 }
730 }
731 switch(_result.getStatus())
732 {
733 case BUFFER_OVERFLOW:
734 case BUFFER_UNDERFLOW:
735 Log.warn("wrap {}",_result);
736
737 case OK:
738 return _result.bytesConsumed();
739 case CLOSED:
740 _closing=true;
741 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
742
743 default:
744 Log.warn("wrap "+_result);
745 throw new IOException(_result.toString());
746 }
747 }
748
749
750 public boolean isBufferingInput()
751 {
752 return _inNIOBuffer.hasContent();
753 }
754
755
756 public boolean isBufferingOutput()
757 {
758 return _outNIOBuffer.hasContent();
759 }
760
761
762 public boolean isBufferred()
763 {
764 return true;
765 }
766
767
768 public SSLEngine getSSLEngine()
769 {
770 return _engine;
771 }
772
773
774 public String toString()
775 {
776 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" last "+_last+" "+_result;
777 }
778 }