View Javadoc

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