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 throw new IOException("Write while closing");
246 break;
247 }
248
249
250 if (outBuf!=null && outBuf.hasContent())
251 {
252 int c=wrap(outBuf);
253 progress=c>0||_result.bytesProduced()>0||_result.bytesConsumed()>0;
254
255 if (c>0)
256 sent+=c;
257 else if (c<0 && sent==0)
258 sent=-1;
259 }
260
261
262 if (inBBuf.remaining()>0 && _inNIOBuffer!=null && _inNIOBuffer.hasContent())
263 {
264 int space=inBBuf.remaining();
265 progress|=unwrap(inBBuf);
266 received+=space-inBBuf.remaining();
267 }
268 break;
269
270
271 case NEED_TASK:
272 {
273
274 Runnable task;
275 while ((task=_engine.getDelegatedTask())!=null)
276 {
277 progress=true;
278 task.run();
279 }
280
281
282 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
283 _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && sent==0)
284 {
285
286
287
288
289 if (_debug) LOG.warn("{} JETTY-567",_session);
290 return -1;
291 }
292 break;
293 }
294
295 case NEED_WRAP:
296 {
297 checkRenegotiate();
298
299
300 int c=0;
301 if (outBuf!=null && outBuf.hasContent())
302 c=wrap(outBuf);
303 else
304 c=wrap(__EMPTY_BUFFER);
305
306 progress=_result.bytesProduced()>0||_result.bytesConsumed()>0;
307 if (c>0)
308 sent+=c;
309 else if (c<0 && sent==0)
310 sent=-1;
311 break;
312 }
313
314 case NEED_UNWRAP:
315 {
316 checkRenegotiate();
317
318
319 progress|=unwrap(inBBuf);
320 if (_closing)
321 inBBuf.clear();
322 break;
323 }
324 }
325
326 if (_debug) LOG.debug("{} progress {}",_session,progress);
327 }
328
329 if (_debug) LOG.debug("{} received {} sent {}",_session,received,sent);
330
331 freeInBuffer();
332 return (received<0||sent<0)?-1:(received+sent);
333 }
334
335
336
337
338 @Override
339 public void close() throws IOException
340 {
341 if (_closing)
342 return;
343
344 _closing=true;
345 LOG.debug("{} close",_session);
346 try
347 {
348 _engine.closeOutbound();
349 process(null,null);
350 }
351 catch (IOException e)
352 {
353
354
355 LOG.ignore(e);
356 }
357 finally
358 {
359 super.close();
360 }
361 }
362
363
364
365
366
367
368 @Override
369 public int fill(Buffer buffer) throws IOException
370 {
371 _debug=LOG.isDebugEnabled();
372 LOG.debug("{} fill",_session);
373
374
375
376 ByteBuffer bbuf=((NIOBuffer)buffer).getByteBuffer();
377
378
379
380 int size=buffer.length();
381
382
383 synchronized (bbuf)
384 {
385 bbuf.position(buffer.putIndex());
386 try
387 {
388
389
390
391 unwrap(bbuf);
392 process(bbuf,null);
393 }
394 finally
395 {
396
397 buffer.setPutIndex(bbuf.position());
398 bbuf.position(0);
399 }
400 }
401
402 int filled=buffer.length()-size;
403 if (filled==0 && (isInputShutdown() || !isOpen()))
404 return -1;
405
406 return filled;
407 }
408
409
410 @Override
411 public int flush(Buffer buffer) throws IOException
412 {
413 _debug=LOG.isDebugEnabled();
414 LOG.debug("{} flush1",_session);
415 return process(null,buffer);
416 }
417
418
419
420
421
422 @Override
423 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
424 {
425 _debug=LOG.isDebugEnabled();
426 LOG.debug("{} flush3",_session);
427
428 int len=0;
429 int flushed=0;
430 if (header!=null && header.hasContent())
431 {
432 len=header.length();
433 flushed=flush(header);
434 }
435 if (flushed==len && buffer!=null && buffer.hasContent())
436 {
437 int f=flush(buffer);
438 if (f>=0)
439 flushed+=f;
440 else if (flushed==0)
441 flushed=-1;
442 }
443
444 return flushed;
445 }
446
447
448 @Override
449 public void flush() throws IOException
450 {
451 LOG.debug("{} flush",_session);
452 if (!isOpen())
453 throw new EofException();
454
455 if (isBufferingOutput())
456 {
457 int flushed=super.flush(_outNIOBuffer);
458 if (_debug)
459 LOG.debug("{} flushed={} left={}",_session,flushed,_outNIOBuffer.length());
460 }
461 else if (_engine.isOutboundDone() && super.isOpen())
462 {
463 if (_debug)
464 LOG.debug("{} flush shutdownOutput",_session);
465 try
466 {
467 super.shutdownOutput();
468 }
469 catch(IOException e)
470 {
471 LOG.ignore(e);
472 }
473 }
474
475 freeOutBuffer();
476 }
477
478
479 private void checkRenegotiate() throws IOException
480 {
481 if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
482 {
483 LOG.warn("SSL renegotiate denied: {}",_channel);
484 super.close();
485 }
486 }
487
488
489
490
491
492 private boolean unwrap(ByteBuffer buffer) throws IOException
493 {
494 needInBuffer();
495 ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
496
497 _inNIOBuffer.compact();
498
499 int total_filled=0;
500 boolean remoteClosed = false;
501
502 LOG.debug("{} unwrap space={} open={}",_session,_inNIOBuffer.space(),super.isOpen());
503
504
505 while (_inNIOBuffer.space()>0 && super.isOpen())
506 {
507 int filled=super.fill(_inNIOBuffer);
508 if (_debug) LOG.debug("{} filled {}",_session,filled);
509 if (filled < 0)
510 remoteClosed = true;
511
512 if (filled<=0)
513 break;
514 total_filled+=filled;
515 }
516
517
518 if (total_filled==0 && _inNIOBuffer.length()==0)
519 {
520
521 if (isOpen() && remoteClosed)
522 {
523 try
524 {
525 _engine.closeInbound();
526 }
527 catch (SSLException x)
528 {
529
530
531 super.close();
532 }
533 }
534
535 if (!isOpen())
536 throw new EofException();
537
538 return false;
539 }
540
541
542 try
543 {
544
545
546 in_buffer.position(_inNIOBuffer.getIndex());
547 in_buffer.limit(_inNIOBuffer.putIndex());
548
549
550 _result=_engine.unwrap(in_buffer,buffer);
551 if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED)
552 _handshook=true;
553 if (_debug) LOG.debug("{} unwrap {}",_session,_result);
554
555
556 _inNIOBuffer.skip(_result.bytesConsumed());
557 }
558 catch(SSLException e)
559 {
560 LOG.warn(getRemoteAddr() + ":" + getRemotePort() + " ",e);
561 super.close();
562 throw e;
563 }
564 finally
565 {
566
567 in_buffer.position(0);
568 in_buffer.limit(in_buffer.capacity());
569 }
570
571
572 switch(_result.getStatus())
573 {
574 case BUFFER_OVERFLOW:
575 LOG.debug("{} unwrap overflow",_session);
576 return false;
577
578 case BUFFER_UNDERFLOW:
579
580
581
582
583 if (LOG.isDebugEnabled()) LOG.debug("{} unwrap {}",_session,_result);
584 if(!isOpen())
585 {
586 _inNIOBuffer.clear();
587 if (_outNIOBuffer!=null)
588 _outNIOBuffer.clear();
589 throw new EofException();
590 }
591 return (total_filled > 0);
592
593 case CLOSED:
594 _closing=true;
595
596 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
597
598 case OK:
599
600 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
601
602 default:
603 LOG.warn("{} unwrap default: {}",_session,_result);
604 throw new IOException(_result.toString());
605 }
606 }
607
608
609 private ByteBuffer extractOutputBuffer(Buffer buffer)
610 {
611 if (buffer.buffer() instanceof NIOBuffer)
612 return ((NIOBuffer)buffer.buffer()).getByteBuffer();
613
614 return ByteBuffer.wrap(buffer.array());
615 }
616
617
618 private int wrap(final Buffer buffer) throws IOException
619 {
620 ByteBuffer bbuf=extractOutputBuffer(buffer);
621 synchronized(bbuf)
622 {
623 int consumed=0;
624 needOutBuffer();
625 _outNIOBuffer.compact();
626 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
627 synchronized(out_buffer)
628 {
629 try
630 {
631 bbuf.position(buffer.getIndex());
632 bbuf.limit(buffer.putIndex());
633 out_buffer.position(_outNIOBuffer.putIndex());
634 out_buffer.limit(out_buffer.capacity());
635 _result=_engine.wrap(bbuf,out_buffer);
636 if (_debug) LOG.debug("{} wrap {}",_session,_result);
637 if (!_handshook && _result.getHandshakeStatus()==SSLEngineResult.HandshakeStatus.FINISHED)
638 _handshook=true;
639 _outNIOBuffer.setPutIndex(out_buffer.position());
640 consumed=_result.bytesConsumed();
641 }
642 catch(SSLException e)
643 {
644 LOG.warn(getRemoteAddr()+":"+getRemotePort()+" ",e);
645 super.close();
646 throw e;
647 }
648 finally
649 {
650 out_buffer.position(0);
651 bbuf.position(0);
652 bbuf.limit(bbuf.capacity());
653
654 if (consumed>0)
655 {
656 int len=consumed<buffer.length()?consumed:buffer.length();
657 buffer.skip(len);
658 consumed-=len;
659 }
660 }
661 }
662 }
663 switch(_result.getStatus())
664 {
665 case BUFFER_UNDERFLOW:
666 throw new IllegalStateException();
667
668 case BUFFER_OVERFLOW:
669 LOG.debug("{} wrap {}",_session,_result);
670 flush();
671 return 0;
672
673 case OK:
674 return _result.bytesConsumed();
675 case CLOSED:
676 _closing=true;
677 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
678
679 default:
680 LOG.warn("{} wrap default {}",_session,_result);
681 throw new IOException(_result.toString());
682 }
683 }
684
685
686 @Override
687 public boolean isBufferingInput()
688 {
689 final Buffer in = _inNIOBuffer;
690 return in!=null && in.hasContent();
691 }
692
693
694 @Override
695 public boolean isBufferingOutput()
696 {
697 final NIOBuffer out = _outNIOBuffer;
698 return out!=null && out.hasContent();
699 }
700
701
702 @Override
703 public boolean isBufferred()
704 {
705 return true;
706 }
707
708
709 public SSLEngine getSSLEngine()
710 {
711 return _engine;
712 }
713
714
715 @Override
716 public String toString()
717 {
718 final NIOBuffer i=_inNIOBuffer;
719 final NIOBuffer o=_outNIOBuffer;
720 return "SSL"+super.toString()+","+(_engine==null?"-":_engine.getHandshakeStatus())+", in/out="+
721 (i==null?0:i.length())+"/"+(o==null?0:o.length())+
722 " bi/o="+isBufferingInput()+"/"+isBufferingOutput()+
723 " "+_result;
724 }
725 }