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 }