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