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