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 public SslHttpChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
66 throws SSLException, IOException
67 {
68 super(channel,selectSet,key);
69 _buffers=buffers;
70
71
72 _engine=engine;
73 _session=engine.getSession();
74
75
76 _outNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
77 _outBuffer=_outNIOBuffer.getByteBuffer();
78 _inNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
79 _inBuffer=_inNIOBuffer.getByteBuffer();
80
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 while (_inNIOBuffer.space()>0 && super.isOpen())
513 {
514 try
515 {
516 int filled=super.fill(_inNIOBuffer);
517 if (filled<=0)
518 break;
519 total_filled+=filled;
520 }
521 catch(IOException e)
522 {
523 if (_inNIOBuffer.length()==0)
524 throw e;
525 break;
526 }
527 }
528
529 if (_inNIOBuffer.length()==0)
530 {
531 if(!isOpen())
532 throw new org.mortbay.jetty.EofException();
533 return false;
534 }
535
536 try
537 {
538 _inBuffer.position(_inNIOBuffer.getIndex());
539 _inBuffer.limit(_inNIOBuffer.putIndex());
540 _result=null;
541 _result=_engine.unwrap(_inBuffer,buffer);
542 _inNIOBuffer.skip(_result.bytesConsumed());
543 }
544 finally
545 {
546 _inBuffer.position(0);
547 _inBuffer.limit(_inBuffer.capacity());
548 }
549
550 switch(_result.getStatus())
551 {
552 case BUFFER_OVERFLOW:
553 throw new IllegalStateException(_result.toString());
554
555 case BUFFER_UNDERFLOW:
556 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
557 return (total_filled > 0);
558
559 case CLOSED:
560 _closing=true;
561 case OK:
562 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
563 return progress;
564 default:
565 Log.warn("unwrap "+_result);
566 throw new IOException(_result.toString());
567 }
568 }
569
570
571
572 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
573 {
574 NIOBuffer nBuf=null;
575
576 if (buffer.buffer() instanceof NIOBuffer)
577 {
578 nBuf=(NIOBuffer)buffer.buffer();
579 return nBuf.getByteBuffer();
580 }
581 else
582 {
583 if (_reuseBuffer[n]==null)
584 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
585 NIOBuffer buf = _reuseBuffer[n];
586 buf.clear();
587 buf.put(buffer);
588 return buf.getByteBuffer();
589 }
590 }
591
592
593 private int wrap(Buffer header, Buffer buffer) throws IOException
594 {
595 _gather[0]=extractOutputBuffer(header,0);
596 synchronized(_gather[0])
597 {
598 _gather[0].position(header.getIndex());
599 _gather[0].limit(header.putIndex());
600
601 _gather[1]=extractOutputBuffer(buffer,1);
602
603 synchronized(_gather[1])
604 {
605 _gather[1].position(buffer.getIndex());
606 _gather[1].limit(buffer.putIndex());
607
608 synchronized(_outBuffer)
609 {
610 int consumed=0;
611 try
612 {
613 _outNIOBuffer.clear();
614 _outBuffer.position(0);
615 _outBuffer.limit(_outBuffer.capacity());
616
617 _result=null;
618 _result=_engine.wrap(_gather,_outBuffer);
619 _outNIOBuffer.setGetIndex(0);
620 _outNIOBuffer.setPutIndex(_result.bytesProduced());
621 consumed=_result.bytesConsumed();
622 }
623 finally
624 {
625 _outBuffer.position(0);
626
627 if (consumed>0 && header!=null)
628 {
629 int len=consumed<header.length()?consumed:header.length();
630 header.skip(len);
631 consumed-=len;
632 _gather[0].position(0);
633 _gather[0].limit(_gather[0].capacity());
634 }
635 if (consumed>0 && buffer!=null)
636 {
637 int len=consumed<buffer.length()?consumed:buffer.length();
638 buffer.skip(len);
639 consumed-=len;
640 _gather[1].position(0);
641 _gather[1].limit(_gather[1].capacity());
642 }
643 assert consumed==0;
644 }
645 }
646 }
647 }
648
649
650 switch(_result.getStatus())
651 {
652 case BUFFER_OVERFLOW:
653 case BUFFER_UNDERFLOW:
654 Log.warn("wrap {}",_result);
655
656 case OK:
657 return _result.bytesConsumed();
658 case CLOSED:
659 _closing=true;
660 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
661
662 default:
663 Log.warn("wrap "+_result);
664 throw new IOException(_result.toString());
665 }
666 }
667
668
669 private int wrap(Buffer buffer) throws IOException
670 {
671 _gather[0]=extractOutputBuffer(buffer,0);
672 synchronized(_gather[0])
673 {
674 _gather[0].position(buffer.getIndex());
675 _gather[0].limit(buffer.putIndex());
676
677 int consumed=0;
678 synchronized(_outBuffer)
679 {
680 try
681 {
682 _outNIOBuffer.clear();
683 _outBuffer.position(0);
684 _outBuffer.limit(_outBuffer.capacity());
685 _result=null;
686 _result=_engine.wrap(_gather[0],_outBuffer);
687 _outNIOBuffer.setGetIndex(0);
688 _outNIOBuffer.setPutIndex(_result.bytesProduced());
689 consumed=_result.bytesConsumed();
690 }
691 finally
692 {
693 _outBuffer.position(0);
694
695 if (consumed>0 && buffer!=null)
696 {
697 int len=consumed<buffer.length()?consumed:buffer.length();
698 buffer.skip(len);
699 consumed-=len;
700 _gather[0].position(0);
701 _gather[0].limit(_gather[0].capacity());
702 }
703 assert consumed==0;
704 }
705 }
706 }
707 switch(_result.getStatus())
708 {
709 case BUFFER_OVERFLOW:
710 case BUFFER_UNDERFLOW:
711 Log.warn("wrap {}",_result);
712
713 case OK:
714 return _result.bytesConsumed();
715 case CLOSED:
716 _closing=true;
717 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
718
719 default:
720 Log.warn("wrap "+_result);
721 throw new IOException(_result.toString());
722 }
723 }
724
725
726 public boolean isBufferingInput()
727 {
728 return _inNIOBuffer.hasContent();
729 }
730
731
732 public boolean isBufferingOutput()
733 {
734 return _outNIOBuffer.hasContent();
735 }
736
737
738 public boolean isBufferred()
739 {
740 return true;
741 }
742
743
744 public SSLEngine getSSLEngine()
745 {
746 return _engine;
747 }
748
749
750 public String toString()
751 {
752 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" "+_result;
753 }
754 }