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