]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/io/ssl/SslConnection.java
Merge branch 'libs/jetty/upstream' into libs/jetty/local
[gigi.git] / lib / jetty / org / eclipse / jetty / io / ssl / SslConnection.java
index ee9e449b15110bd5e613ca531cda3e2d2600bc8e..21c8bf3834aefd1de94e887b8d7f7a1b47acd42f 100644 (file)
@@ -1,6 +1,6 @@
 //
 //  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
 //  ------------------------------------------------------------------------
 //  All rights reserved. This program and the accompanying materials
 //  are made available under the terms of the Eclipse Public License v1.0
@@ -83,7 +83,8 @@ public class SslConnection extends AbstractConnection
     private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
     private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
     private final ByteBufferPool _bufferPool;
-    private final SSLEngine _sslEngine;
+    private SSLEngine _sslEngine;
+    private final SslReconfigurator _sslFactory;
     private final DecryptedEndPoint _decryptedEndPoint;
     private ByteBuffer _decryptedInput;
     private ByteBuffer _encryptedInput;
@@ -101,12 +102,17 @@ public class SslConnection extends AbstractConnection
     private boolean _renegotiationAllowed;
 
     public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
+    {
+       this(byteBufferPool, executor, endPoint, sslEngine, null);
+    }
+    public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, SslReconfigurator fact)
     {
         // This connection does not execute calls to onfillable, so they will be called by the selector thread.
         // onfillable does not block and will only wakeup another thread to do the actual reading and handling.
         super(endPoint, executor, !EXECUTE_ONFILLABLE);
         this._bufferPool = byteBufferPool;
         this._sslEngine = sslEngine;
+        this._sslFactory = fact;
         this._decryptedEndPoint = newDecryptedEndPoint();
     }
 
@@ -246,6 +252,7 @@ public class SslConnection extends AbstractConnection
         private boolean _cannotAcceptMoreAppDataToFlush;
         private boolean _handshaken;
         private boolean _underFlown;
+        private boolean _peeking = _sslFactory != null;
 
         private final Callback _writeCallback = new Callback()
         {
@@ -305,7 +312,7 @@ public class SslConnection extends AbstractConnection
                 {
                     @Override
                     public void succeeded()
-                    {                        
+                    {
                     }
 
                     @Override
@@ -315,7 +322,7 @@ public class SslConnection extends AbstractConnection
                             getFillInterest().onFail(x);
                         getWriteFlusher().onFail(x);
                     }
-                    
+
                 },x);
             }
         };
@@ -352,7 +359,7 @@ public class SslConnection extends AbstractConnection
             // all data could be wrapped. So either we need to write some encrypted data,
             // OR if we are handshaking we need to read some encrypted data OR
             // if neither then we should just try the flush again.
-            boolean flush = false;
+            boolean try_again = false;
             synchronized (DecryptedEndPoint.this)
             {
                 if (DEBUG)
@@ -373,10 +380,17 @@ public class SslConnection extends AbstractConnection
                 }
                 else
                 {
-                    flush = true;
+                    // We can get here because the WriteFlusher might not see progress
+                    // when it has just flushed the encrypted data, but not consumed anymore
+                    // of the application buffers.  This is mostly avoided by another iteration
+                    // within DecryptedEndPoint flush(), but I cannot convince myself that
+                    // this is never ever the case.
+                    try_again = true;
                 }
             }
-            if (flush)
+
+
+            if (try_again)
             {
                 // If the output is closed,
                 if (isOutputShutdown())
@@ -388,7 +402,9 @@ public class SslConnection extends AbstractConnection
                 else
                 {
                     // try to flush what is pending
-                    getWriteFlusher().completeWrite();
+                    // because this is a special case (see above) we could probably
+                    // avoid the dispatch, but best to be sure
+                    getExecutor().execute(_runCompletWrite);
                 }
             }
         }
@@ -480,7 +496,7 @@ public class SslConnection extends AbstractConnection
                 // We will need a network buffer
                 if (_encryptedInput == null)
                     _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
-                else
+                else if(!_peeking)
                     BufferUtil.compact(_encryptedInput);
 
                 // We also need an app buffer, but can use the passed buffer if it is big enough
@@ -505,8 +521,15 @@ public class SslConnection extends AbstractConnection
                         // Let's unwrap even if we have no net data because in that
                         // case we want to fall through to the handshake handling
                         int pos = BufferUtil.flipToFill(app_in);
-                        SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
-                        BufferUtil.flipToFlush(app_in, pos);
+                        SSLEngineResult unwrapResult;
+                        try
+                        {
+                            unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
+                        }
+                        finally
+                        {
+                            BufferUtil.flipToFlush(app_in, pos);
+                        }
                         if (DEBUG)
                             LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
 
@@ -514,7 +537,9 @@ public class SslConnection extends AbstractConnection
                         HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
                         Status unwrapResultStatus = unwrapResult.getStatus();
 
-                        _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
+                        // Extra check on unwrapResultStatus == OK with zero length buffer is due
+                        // to SSL client on android (see bug #454773)
+                        _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed()==0 && unwrapResult.bytesProduced()==0;
 
                         if (_underFlown)
                         {
@@ -547,6 +572,12 @@ public class SslConnection extends AbstractConnection
                                         // or shutting down the output.
                                         return -1;
                                     }
+                                    case NEED_UNWRAP:
+                                    {
+                                        // We expected to read more, but we got closed.
+                                        // Return -1 to indicate to the application to drive the close.
+                                        return -1;
+                                    }
                                     default:
                                     {
                                         throw new IllegalStateException();
@@ -594,6 +625,13 @@ public class SslConnection extends AbstractConnection
                                     case NEED_TASK:
                                     {
                                         _sslEngine.getDelegatedTask().run();
+                                        if(_peeking)
+                                        {
+                                               _sslEngine = _sslFactory.restartSSL(_sslEngine.getHandshakeSession());
+                                               _encryptedInput.position(0);
+                                               _peeking = false;
+                                               continue decryption;
+                                        }
                                         continue;
                                     }
                                     case NEED_WRAP:
@@ -640,7 +678,7 @@ public class SslConnection extends AbstractConnection
             }
             catch (Exception e)
             {
-                getEndPoint().close();
+                close();
                 throw e;
             }
             finally
@@ -710,24 +748,26 @@ public class SslConnection extends AbstractConnection
                     // We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
                     BufferUtil.compact(_encryptedOutput);
                     int pos = BufferUtil.flipToFill(_encryptedOutput);
-                    SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
+                    SSLEngineResult wrapResult;
+                    try
+                    {
+                        wrapResult=_sslEngine.wrap(appOuts, _encryptedOutput);
+                    }
+                    finally
+                    {
+                        BufferUtil.flipToFlush(_encryptedOutput, pos);
+                    }
+
                     if (DEBUG)
                         LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
-                    BufferUtil.flipToFlush(_encryptedOutput, pos);
                     if (wrapResult.bytesConsumed()>0)
                         consumed+=wrapResult.bytesConsumed();
+                    Status wrapResultStatus = wrapResult.getStatus();
 
                     boolean allConsumed=true;
-                    // clear empty buffers to prevent position creeping up the buffer
                     for (ByteBuffer b : appOuts)
-                    {
-                        if (BufferUtil.isEmpty(b))
-                            BufferUtil.clear(b);
-                        else
+                        if (BufferUtil.hasContent(b))
                             allConsumed=false;
-                    }
-
-                    Status wrapResultStatus = wrapResult.getStatus();
 
                     // and deal with the results returned from the sslEngineWrap
                     switch (wrapResultStatus)
@@ -774,19 +814,26 @@ public class SslConnection extends AbstractConnection
                             {
                                 if (DEBUG)
                                     LOG.debug("{} renegotiation denied", SslConnection.this);
-                                shutdownOutput();
+                                getEndPoint().shutdownOutput();
                                 return allConsumed;
                             }
 
                             // if we have net bytes, let's try to flush them
                             if (BufferUtil.hasContent(_encryptedOutput))
-                                getEndPoint().flush(_encryptedOutput);
+                                if (!getEndPoint().flush(_encryptedOutput))
+                                    getEndPoint().flush(_encryptedOutput); // one retry
 
                             // But we also might have more to do for the handshaking state.
                             switch (handshakeStatus)
                             {
                                 case NOT_HANDSHAKING:
-                                    // Return with the number of bytes consumed (which may be 0)
+                                    // If we have not consumed all and had just finished handshaking, then we may
+                                    // have just flushed the last handshake in the encrypted buffers, so we should
+                                    // try again.
+                                    if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
+                                        continue;
+
+                                    // Return true if we consumed all the bytes and encrypted are all flushed
                                     return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
 
                                 case NEED_TASK:
@@ -818,11 +865,6 @@ public class SslConnection extends AbstractConnection
                     }
                 }
             }
-            catch (Exception e)
-            {
-                getEndPoint().close();
-                throw e;
-            }
             finally
             {
                 if (DEBUG)