View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
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   * SslSelectChannelEndPoint
35   * <p>
36   * A SelectChannelEndPoint that uses an {@link SSLEngine} to handle an
37   * SSL connection.
38   * <p>
39   * There is a named logger "org.eclipse.jetty.http.ssl"
40   * </p>
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 ByteBuffer[] __NO_BUFFERS={};
47  
48      private final Buffers _buffers;
49  
50      private final SSLEngine _engine;
51      private final SSLSession _session;
52      private volatile NIOBuffer _inNIOBuffer;
53      private volatile NIOBuffer _outNIOBuffer;
54  
55      private final ByteBuffer[] _gather=new ByteBuffer[2];
56  
57      private boolean _closing=false;
58      private SSLEngineResult _result;
59  
60      private boolean _handshook=false;
61      private boolean _allowRenegotiate=false;
62  
63      private final boolean _debug = __log.isDebugEnabled(); // snapshot debug status for optimizer
64  
65      /* ------------------------------------------------------------ */
66      public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime)
67              throws IOException
68      {
69          super(channel,selectSet,key, maxIdleTime);
70          _buffers=buffers;
71  
72          // ssl
73          _engine=engine;
74          _session=engine.getSession();
75  
76          if (_debug) __log.debug(_session+" channel="+channel);
77      }
78  
79      /* ------------------------------------------------------------ */
80      public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
81              throws IOException
82      {
83          super(channel,selectSet,key);
84          _buffers=buffers;
85  
86          // ssl
87          _engine=engine;
88          _session=engine.getSession();
89  
90          if (_debug) __log.debug(_session+" channel="+channel);
91      }
92  
93      int _outCount;
94  
95      /* ------------------------------------------------------------ */
96      private void needOutBuffer()
97      {
98          synchronized (this)
99          {
100             _outCount++;
101             if (_outNIOBuffer==null)
102                 _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
103         }
104     }
105 
106     /* ------------------------------------------------------------ */
107     private void freeOutBuffer()
108     {
109         synchronized (this)
110         {
111             if (--_outCount<=0 && _outNIOBuffer!=null && _outNIOBuffer.length()==0)
112             {
113                 _buffers.returnBuffer(_outNIOBuffer);
114                 _outNIOBuffer=null;
115                 _outCount=0;
116             }
117         }
118     }
119 
120     int _inCount;
121     /* ------------------------------------------------------------ */
122     private void needInBuffer()
123     {
124         synchronized (this)
125         {
126             _inCount++;
127             if(_inNIOBuffer==null)
128                 _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
129         }
130     }
131 
132     /* ------------------------------------------------------------ */
133     private void freeInBuffer()
134     {
135         synchronized (this)
136         {
137             if (--_inCount<=0 &&_inNIOBuffer!=null && _inNIOBuffer.length()==0)
138             {
139                 _buffers.returnBuffer(_inNIOBuffer);
140                 _inNIOBuffer=null;
141                 _inCount=0;
142             }
143         }
144     }
145 
146     /* ------------------------------------------------------------ */
147     /**
148      * @return True if SSL re-negotiation is allowed (default false)
149      */
150     public boolean isAllowRenegotiate()
151     {
152         return _allowRenegotiate;
153     }
154 
155     /* ------------------------------------------------------------ */
156     /**
157      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
158      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
159      * does not have CVE-2009-3555 fixed, then re-negotiation should
160      * not be allowed.
161      * @param allowRenegotiate true if re-negotiation is allowed (default false)
162      */
163     public void setAllowRenegotiate(boolean allowRenegotiate)
164     {
165         _allowRenegotiate = allowRenegotiate;
166     }
167 
168 
169     /* ------------------------------------------------------------ */
170     @Override
171     public boolean isOutputShutdown()
172     {
173         return _engine!=null && _engine.isOutboundDone();
174     }
175 
176     /* ------------------------------------------------------------ */
177     @Override
178     public boolean isInputShutdown()
179     {
180         return _engine!=null && _engine.isInboundDone();
181     }
182 
183     /* ------------------------------------------------------------ */
184     @Override
185     public void shutdownOutput() throws IOException
186     {
187         try
188         {
189             sslClose();
190         }
191         finally
192         {
193             super.shutdownOutput();
194         }
195     }
196 
197     /* ------------------------------------------------------------ */
198     protected void sslClose() throws IOException
199     {
200         if (_closing)
201             return;
202         _closing=true;
203 
204         // TODO - this really should not be done in a loop here - but with async callbacks.
205         long end=System.currentTimeMillis()+getMaxIdleTime();
206         try
207         {
208             while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end)
209             {
210                 flush();
211                 if (isBufferingOutput())
212                 {
213                     Thread.sleep(50); // TODO non blocking
214                     flush();
215                 }
216             }
217 
218             _engine.closeOutbound();
219 
220             loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
221             {
222                 while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
223                 {
224                     flush();
225                     if (isBufferingOutput())
226                         Thread.sleep(50);
227                 }
228 
229                 if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
230                 switch(_engine.getHandshakeStatus())
231                 {
232                     case FINISHED:
233                     case NOT_HANDSHAKING:
234                         _handshook=true;
235                         break loop;
236 
237                     case NEED_UNWRAP:
238                         Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
239                         try
240                         {
241                             ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
242                             if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
243                             {
244                                 break loop;
245                             }
246                         }
247                         catch(SSLException e)
248                         {
249                             super.close();
250                             Log.ignore(e);
251                         }
252                         finally
253                         {
254                             _buffers.returnBuffer(buffer);
255                         }
256                         break;
257 
258                     case NEED_TASK:
259                     {
260                         Runnable task;
261                         while ((task=_engine.getDelegatedTask())!=null)
262                         {
263                             task.run();
264                         }
265                         break;
266                     }
267 
268                     case NEED_WRAP:
269                     {
270                         needOutBuffer();
271                         ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
272                         try
273                         {
274                             if (_outNIOBuffer.length()>0)
275                                 flush();
276                             _outNIOBuffer.compact();
277                             int put=_outNIOBuffer.putIndex();
278                             out_buffer.position(put);
279                             _result=null;
280                             _result=_engine.wrap(__NO_BUFFERS,out_buffer);
281                             if (_debug) __log.debug(_session+" close wrap "+_result);
282                             _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
283                         }
284                         catch(SSLException e)
285                         {
286                             super.close();
287                             throw e;
288                         }
289                         finally
290                         {
291                             out_buffer.position(0);
292                             freeOutBuffer();
293                         }
294 
295                         break;
296                     }
297                 }
298             }
299         }
300         catch (Exception x)
301         {
302             Log.debug(x);
303             super.close();
304         }
305     }
306 
307     /* ------------------------------------------------------------ */
308     @Override
309     public void close() throws IOException
310     {
311         try
312         {
313             sslClose();
314         }
315         finally
316         {
317             super.close();
318         }
319     }
320 
321     /* ------------------------------------------------------------ */
322     /** Fill the buffer with unencrypted bytes.
323      * Called by a Http Parser when more data is
324      * needed to continue parsing a request or a response.
325      */
326     @Override
327     public int fill(Buffer buffer) throws IOException
328     {   
329         // This end point only works on NIO buffer type (director
330         // or indirect), so extract the NIO buffer that is wrapped
331         // by the passed jetty Buffer.
332         final ByteBuffer bbuf=extractInputBuffer(buffer);
333 
334         // remember the original size of the unencrypted buffer
335         int size=buffer.length();
336 
337         HandshakeStatus initialStatus = _engine.getHandshakeStatus();
338         //noinspection SynchronizationOnLocalVariableOrMethodParameter
339         synchronized (bbuf)
340         {
341             try
342             {
343                 // Call the SSLEngine unwrap method to process data in
344                 // the inBuffer.  If there is no data in the inBuffer, then
345                 // super.fill is called to read encrypted bytes.
346                 unwrap(bbuf);
347 
348                 // Loop through the SSL engine state machine
349 
350                 int wraps=0;
351                 loop: while (true)
352                 {
353                     // If we have encrypted data in output buffer
354                     if (isBufferingOutput())
355                     {
356                         // we must flush it, as the other end might be
357                         // waiting for that outgoing data before sending
358                         // more incoming data
359                         flush();
360 
361                         // If we were unable to flush all the data, then
362                         // we should break the loop and wait for the call
363                         // back to handle when the SelectSet detects that
364                         // the channel is writable again.
365                         if (isBufferingOutput())
366                             break loop;
367                     }
368 
369                     // handle the current hand share status
370                     switch(_engine.getHandshakeStatus())
371                     {
372                         case FINISHED:
373                         case NOT_HANDSHAKING:
374                             // If we are closing, then unwrap must have CLOSED result,
375                             // so return -1 to signal upwards
376                             if (_closing)
377                                 return -1;
378 
379                             // otherwise we break loop with the data we have unwrapped.
380                             break loop;
381 
382                         case NEED_UNWRAP:
383                             checkRenegotiate();
384                             // Need more data to be unwrapped so try another call to unwrap
385                             if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
386                             {
387                                 // If the unwrap call did not make any progress and we are still in
388                                 // NEED_UNWRAP, then we should break the loop and wait for more data to
389                                 // arrive.
390                                 break loop;
391                             }
392                             // progress was made so continue the loop.
393                             break;
394 
395                         case NEED_TASK:
396                         {
397                             // A task needs to be run, so run it!
398 
399                             Runnable task;
400                             while ((task=_engine.getDelegatedTask())!=null)
401                             {
402                                 task.run();
403                             }
404 
405                             // Detect SUN JVM Bug!!!
406                             if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
407                                _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
408                             {
409                                 // This should be NEED_WRAP
410                                 // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS.
411                                 // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it.
412                                 // See http://jira.codehaus.org/browse/JETTY-567 for more details
413                                 if (_debug) __log.warn(_session+" JETTY-567");
414                                 return -1;
415                             }
416                             break;
417                         }
418 
419                         case NEED_WRAP:
420                         {
421                             checkRenegotiate();
422                             // The SSL needs to send some handshake data to the other side,
423                             // so let fill become a flush for a little bit.
424                             wraps++;
425                             needOutBuffer();
426                             ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
427                             synchronized(out_buffer)
428                             {
429                                 try
430                                 {
431                                     // call wrap with empty application buffers, so it can
432                                     // generate required handshake messages into _outNIOBuffer
433                                     _outNIOBuffer.compact();
434                                     int put=_outNIOBuffer.putIndex();
435                                     out_buffer.position();
436                                     _result=null;
437                                     _result=_engine.wrap(__NO_BUFFERS,out_buffer);
438                                     if (_debug) __log.debug(_session+" fill wrap "+_result);
439                                     switch(_result.getStatus())
440                                     {
441                                         case BUFFER_OVERFLOW:
442                                         case BUFFER_UNDERFLOW:
443                                             Log.warn("wrap {}",_result);
444                                         case CLOSED:
445                                             _closing=true;
446                                     }
447 
448                                     _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
449                                 }
450                                 catch(SSLException e)
451                                 {
452                                     super.close();
453                                     throw e;
454                                 }
455                                 finally
456                                 {
457                                     out_buffer.position(0);
458                                 }
459                             }
460 
461                             // flush the encrypted outNIOBuffer
462                             flush();
463                             freeOutBuffer();
464 
465                             break;
466                         }
467                     }
468                 }
469             }
470             finally
471             {
472                 // reset the Buffers
473                 buffer.setPutIndex(bbuf.position());
474                 bbuf.position(0);
475             }
476 
477             // return the number of unencrypted bytes filled.
478             int filled=buffer.length()-size;
479             if (filled>0)
480                 _handshook=true;
481             return filled;
482         }
483     }
484 
485     /* ------------------------------------------------------------ */
486     @Override
487     public int flush(Buffer buffer) throws IOException
488     {
489         return flush(buffer,null,null);
490     }
491 
492 
493     /* ------------------------------------------------------------ */
494     /*
495      */
496     @Override
497     public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
498     {
499         int consumed=0;
500         int available=header.length();
501         if (buffer!=null)
502             available+=buffer.length();
503 
504         needOutBuffer();
505         ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
506         loop: while (true)
507         {
508             if (_outNIOBuffer.length()>0)
509             {
510                 flush();
511                 if (isBufferingOutput())
512                     break loop;
513             }
514 
515             switch(_engine.getHandshakeStatus())
516             {
517                 case FINISHED:
518                 case NOT_HANDSHAKING:
519                     if (_closing || available==0)
520                     {
521                         if (consumed==0)
522                             consumed= -1;
523                         break loop;
524                     }
525 
526                     int c;
527                     if (header!=null && header.length()>0)
528                     {
529                         if (buffer!=null && buffer.length()>0)
530                             c=wrap(header,buffer);
531                         else
532                             c=wrap(header);
533                     }
534                     else
535                         c=wrap(buffer);
536 
537 
538                     if (c>0)
539                     {
540                         _handshook=true;
541                         consumed+=c;
542                         available-=c;
543                     }
544                     else if (c<0)
545                     {
546                         if (consumed==0)
547                             consumed=-1;
548                         break loop;
549                     }
550 
551                     break;
552 
553                 case NEED_UNWRAP:
554                     checkRenegotiate();
555                     Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
556                     try
557                     {
558                         ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
559                         if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
560                         {
561                             break loop;
562                         }
563                     }
564                     finally
565                     {
566                         _buffers.returnBuffer(buf);
567                     }
568 
569                     break;
570 
571                 case NEED_TASK:
572                 {
573                     Runnable task;
574                     while ((task=_engine.getDelegatedTask())!=null)
575                     {
576                         task.run();
577                     }
578                     break;
579                 }
580 
581                 case NEED_WRAP:
582                 {
583                     checkRenegotiate();
584                     synchronized(out_buffer)
585                     {
586                         try
587                         {
588                             _outNIOBuffer.compact();
589                             int put=_outNIOBuffer.putIndex();
590                             out_buffer.position();
591                             _result=null;
592                             _result=_engine.wrap(__NO_BUFFERS,out_buffer);
593                             if (_debug) __log.debug(_session+" flush wrap "+_result);
594                             switch(_result.getStatus())
595                             {
596                                 case BUFFER_OVERFLOW:
597                                 case BUFFER_UNDERFLOW:
598                                     Log.warn("unwrap {}",_result);
599                                 case CLOSED:
600                                     _closing=true;
601                             }
602                             _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
603                         }
604                         catch(SSLException e)
605                         {
606                             super.close();
607                             throw e;
608                         }
609                         finally
610                         {
611                             out_buffer.position(0);
612                         }
613                     }
614 
615                     flush();
616                     if (isBufferingOutput())
617                         break loop;
618 
619                     break;
620                 }
621             }
622         }
623 
624         freeOutBuffer();
625         return consumed;
626     }
627 
628     /* ------------------------------------------------------------ */
629     @Override
630     public void flush() throws IOException
631     {
632         if (_outNIOBuffer==null)
633             return;
634 
635         int len=_outNIOBuffer.length();
636         if (isBufferingOutput())
637         {
638             int flushed=super.flush(_outNIOBuffer);
639             if (_debug) __log.debug(_session+" Flushed "+flushed+"/"+len);
640             if (isBufferingOutput())
641             {
642                 // Try again after yield.... cheaper than a reschedule.
643                 Thread.yield();
644                 flushed=super.flush(_outNIOBuffer);
645                 if (_debug) __log.debug(_session+" flushed "+flushed+"/"+len);
646             }
647             else if (_closing && !_engine.isOutboundDone())
648             {
649                 _engine.closeOutbound();
650             }
651         }
652     }
653 
654     /* ------------------------------------------------------------ */
655     private void checkRenegotiate() throws IOException
656     {
657         if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
658         {
659             Log.warn("SSL renegotiate denied: "+_channel);
660             super.close();
661         }
662     }
663 
664     /* ------------------------------------------------------------ */
665     private ByteBuffer extractInputBuffer(Buffer buffer)
666     {
667         assert buffer instanceof NIOBuffer;
668         NIOBuffer nbuf=(NIOBuffer)buffer;
669         ByteBuffer bbuf=nbuf.getByteBuffer();
670         bbuf.position(buffer.putIndex());
671         return bbuf;
672     }
673 
674     /* ------------------------------------------------------------ */
675     /**
676      * @return true if progress is made
677      */
678     private boolean unwrap(ByteBuffer buffer) throws IOException
679     {
680         needInBuffer();
681         ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
682 
683         if (_inNIOBuffer.hasContent())
684             _inNIOBuffer.compact();
685         else
686             _inNIOBuffer.clear();
687 
688         int total_filled=0;
689         boolean remoteClosed = false;
690         // loop filling as much encrypted data as we can into the buffer
691         while (_inNIOBuffer.space()>0 && super.isOpen())
692         {
693             try
694             {
695                 int filled=super.fill(_inNIOBuffer);
696                 if (_debug) __log.debug(_session+" unwrap filled "+filled);
697                 if (filled < 0)
698                     remoteClosed = true;
699                 // break the loop if no progress is made (we have read everything there is to read)
700                 if (filled<=0)
701                     break;
702                 total_filled+=filled;
703             }
704             catch(IOException e)
705             {
706                 if (_inNIOBuffer.length()==0)
707                 {
708                     freeInBuffer();
709                     if (_outNIOBuffer!=null)
710                     {
711                         _outNIOBuffer.clear();
712                         freeOutBuffer();
713                     }
714                     throw e;
715                 }
716                 break;
717             }
718         }
719 
720         // If we have no progress and no data
721         if (total_filled==0 && _inNIOBuffer.length()==0)
722         {
723             if (isOpen() && remoteClosed)
724             {
725                 try
726                 {
727                     _engine.closeInbound();
728                 }
729                 catch (SSLException x)
730                 {
731                     // It may happen, for example, in case of truncation
732                     // attacks, we close so that we do not spin forever
733                     super.close();
734                 }
735             }
736 
737             freeInBuffer();
738             freeOutBuffer();
739 
740             if (!isOpen())
741                 throw new EofException();
742 
743             return false;
744         }
745 
746         // We have some in data, so try to unwrap it.
747         try
748         {
749             // inBuffer is the NIO buffer inside the _inNIOBuffer,
750             // so update its position and limit from the inNIOBuffer.
751             in_buffer.position(_inNIOBuffer.getIndex());
752             in_buffer.limit(_inNIOBuffer.putIndex());
753 
754             // Do the unwrap
755             _result=_engine.unwrap(in_buffer,buffer);
756             if (_debug) __log.debug(_session+" unwrap unwrap "+_result);
757 
758             // skip the bytes consumed
759             _inNIOBuffer.skip(_result.bytesConsumed());
760         }
761         catch(SSLException e)
762         {
763             Log.warn(getRemoteAddr() + ":" + getRemotePort() + " " + e);
764             freeOutBuffer();
765             super.close();
766             throw e;
767         }
768         finally
769         {
770             // reset the buffer so it can be managed by the _inNIOBuffer again.
771             in_buffer.position(0);
772             in_buffer.limit(in_buffer.capacity());
773             freeInBuffer();
774         }
775 
776         // handle the unwrap results
777         switch(_result.getStatus())
778         {
779             case BUFFER_OVERFLOW:
780                 throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit());
781 
782             case BUFFER_UNDERFLOW:
783                 // Not enough data,
784                 // If we are closed, we will never get more, so EOF
785                 // else return and we will be tried again
786                 // later when more data arriving causes another dispatch.
787                 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
788                 if(!isOpen())
789                 {
790                     _inNIOBuffer.clear();
791                     if (_outNIOBuffer!=null)
792                         _outNIOBuffer.clear();
793                     throw new EofException();
794                 }
795                 return (total_filled > 0);
796 
797             case CLOSED:
798                 _closing=true;
799                 // return true is some bytes somewhere were moved about.
800                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
801             case OK:
802                 // return true is some bytes somewhere were moved about.
803                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
804             default:
805                 Log.warn("unwrap "+_result);
806                 throw new IOException(_result.toString());
807         }
808     }
809 
810 
811     /* ------------------------------------------------------------ */
812     private ByteBuffer extractOutputBuffer(Buffer buffer)
813     {
814         if (buffer.buffer() instanceof NIOBuffer)
815             return ((NIOBuffer)buffer.buffer()).getByteBuffer();
816 
817         return ByteBuffer.wrap(buffer.array());
818     }
819 
820     /* ------------------------------------------------------------ */
821     private int wrap(final Buffer header, final Buffer buffer) throws IOException
822     {
823         _gather[0]=extractOutputBuffer(header);
824 
825         synchronized(_gather[0])
826         {
827             _gather[0].position(header.getIndex());
828             _gather[0].limit(header.putIndex());
829 
830             _gather[1]=extractOutputBuffer(buffer);
831 
832             synchronized(_gather[1])
833             {
834                 _gather[1].position(buffer.getIndex());
835                 _gather[1].limit(buffer.putIndex());
836 
837                 needOutBuffer();
838                 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
839                 synchronized(out_buffer)
840                 {
841                     int consumed=0;
842                     try
843                     {
844                         _outNIOBuffer.clear();
845                         out_buffer.position(0);
846                         out_buffer.limit(out_buffer.capacity());
847 
848                         _result=null;
849                         _result=_engine.wrap(_gather,out_buffer);
850                         if (_debug) __log.debug(_session+" wrap wrap "+_result);
851                         _outNIOBuffer.setGetIndex(0);
852                         _outNIOBuffer.setPutIndex(_result.bytesProduced());
853                         consumed=_result.bytesConsumed();
854                     }
855                     catch(SSLException e)
856                     {
857                         Log.warn(getRemoteAddr()+":"+getRemotePort()+" "+e);
858                         super.close();
859                         throw e;
860                     }
861                     finally
862                     {
863                         out_buffer.position(0);
864 
865                         if (consumed>0)
866                         {
867                             int len=consumed<header.length()?consumed:header.length();
868                             header.skip(len);
869                             consumed-=len;
870                             _gather[0].position(0);
871                             _gather[0].limit(_gather[0].capacity());
872                         }
873                         if (consumed>0)
874                         {
875                             int len=consumed<buffer.length()?consumed:buffer.length();
876                             buffer.skip(len);
877                             consumed-=len;
878                             _gather[1].position(0);
879                             _gather[1].limit(_gather[1].capacity());
880                         }
881                         assert consumed==0;
882 
883                         freeOutBuffer();
884                     }
885                 }
886             }
887         }
888 
889 
890         switch(_result.getStatus())
891         {
892             case BUFFER_OVERFLOW:
893             case BUFFER_UNDERFLOW:
894                 Log.warn("unwrap {}",_result);
895 
896             case OK:
897                 return _result.bytesConsumed();
898             case CLOSED:
899                 _closing=true;
900                 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
901 
902             default:
903                 Log.warn("wrap "+_result);
904             throw new IOException(_result.toString());
905         }
906     }
907 
908     /* ------------------------------------------------------------ */
909     private int wrap(final Buffer buffer) throws IOException
910     {
911         _gather[0]=extractOutputBuffer(buffer);
912         synchronized(_gather[0])
913         {
914             ByteBuffer bb;
915 
916             _gather[0].position(buffer.getIndex());
917             _gather[0].limit(buffer.putIndex());
918 
919             int consumed=0;
920             needOutBuffer();
921             ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
922             synchronized(out_buffer)
923             {
924                 try
925                 {
926                     _outNIOBuffer.clear();
927                     out_buffer.position(0);
928                     out_buffer.limit(out_buffer.capacity());
929                     _result=null;
930                     _result=_engine.wrap(_gather[0],out_buffer);
931                     if (_debug) __log.debug(_session+" wrap wrap "+_result);
932                     _outNIOBuffer.setGetIndex(0);
933                     _outNIOBuffer.setPutIndex(_result.bytesProduced());
934                     consumed=_result.bytesConsumed();
935                 }
936                 catch(SSLException e)
937                 {
938                     Log.warn(getRemoteAddr()+":"+getRemotePort()+" "+e);
939                     super.close();
940                     throw e;
941                 }
942                 finally
943                 {
944                     out_buffer.position(0);
945 
946                     if (consumed>0)
947                     {
948                         int len=consumed<buffer.length()?consumed:buffer.length();
949                         buffer.skip(len);
950                         consumed-=len;
951                         _gather[0].position(0);
952                         _gather[0].limit(_gather[0].capacity());
953                     }
954                     assert consumed==0;
955 
956                     freeOutBuffer();
957                 }
958             }
959         }
960         switch(_result.getStatus())
961         {
962             case BUFFER_OVERFLOW:
963             case BUFFER_UNDERFLOW:
964                 Log.warn("unwrap {}",_result);
965 
966             case OK:
967                 return _result.bytesConsumed();
968             case CLOSED:
969                 _closing=true;
970                 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
971 
972             default:
973                 Log.warn("wrap "+_result);
974             throw new IOException(_result.toString());
975         }
976     }
977 
978     /* ------------------------------------------------------------ */
979     @Override
980     public boolean isBufferingInput()
981     {
982         final Buffer in = _inNIOBuffer;
983         return in==null?false:_inNIOBuffer.hasContent();
984     }
985 
986     /* ------------------------------------------------------------ */
987     @Override
988     public boolean isBufferingOutput()
989     {
990         final NIOBuffer b=_outNIOBuffer;
991         return b==null?false:b.hasContent();
992     }
993 
994     /* ------------------------------------------------------------ */
995     @Override
996     public boolean isBufferred()
997     {
998         return true;
999     }
1000 
1001     /* ------------------------------------------------------------ */
1002     public SSLEngine getSSLEngine()
1003     {
1004         return _engine;
1005     }
1006 
1007     /* ------------------------------------------------------------ */
1008     @Override
1009     public void scheduleWrite()
1010     {
1011         // only set !writable if we are not waiting for input
1012         if (!HandshakeStatus.NEED_UNWRAP.equals(_engine.getHandshakeStatus()) || super.isBufferingOutput())
1013         super.scheduleWrite();
1014     }
1015 
1016     /* ------------------------------------------------------------ */
1017     @Override
1018     public String toString()
1019     {
1020         final NIOBuffer i=_inNIOBuffer;
1021         final NIOBuffer o=_outNIOBuffer;
1022         return "SSL"+super.toString()+","+_engine.getHandshakeStatus()+", in/out="+
1023         (i==null?0:_inNIOBuffer.length())+"/"+(o==null?0:o.length())+
1024         " bi/o="+isBufferingInput()+"/"+isBufferingOutput()+
1025         " "+_result;
1026     }
1027 }