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