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