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