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