[Libreoffice-commits] online.git: Branch 'private/Ashod/nonblocking' - net/socket.hpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Sat Feb 18 00:45:13 UTC 2017


 net/socket.hpp |  154 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 141 insertions(+), 13 deletions(-)

New commits:
commit a3b6ef4a76aeb5daf4cbca3f25ca0983020bdca6
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Fri Feb 17 19:05:53 2017 -0500

    nb: basic SSL socket implementation
    
    Change-Id: Iaa2da47df17f14dfded44a8cf805020e8ef6b1ff
    Reviewed-on: https://gerrit.libreoffice.org/34395
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/net/socket.hpp b/net/socket.hpp
index 69d2710..95d4991 100644
--- a/net/socket.hpp
+++ b/net/socket.hpp
@@ -339,11 +339,6 @@ protected:
 class StreamSocket : public BufferingSocket
 {
 public:
-    StreamSocket() :
-        BufferingSocket()
-    {
-    }
-
     bool readIncomingData() override
     {
         ssize_t len;
@@ -391,26 +386,74 @@ protected:
 class SslStreamSocket : public BufferingSocket
 {
 public:
-    SslStreamSocket() :
-        BufferingSocket()
+    bool readIncomingData() override
     {
+        ssize_t len;
+        char buf[4096];
+        do
+        {
+            len = SSL_read(_ssl, buf, sizeof(buf));
+        }
+        while (len < 0 && errno == EINTR);
+
+        len = handleSslState(len);
+
+        if (len > 0)
+        {
+            // We have more data, let the application consume it, if possible.
+            assert (len < ssize_t(sizeof(buf)));
+            _inBuffer.insert(_inBuffer.end(), &buf[0], &buf[len]);
+            handleIncomingMessage();
+        }
+        // else poll will handle errors.
+
+        return len != 0; // zero is eof / clean socket close.
     }
 
-    bool readIncomingData() override
+    void writeOutgoingData() override
     {
-        //TODO:
-        return true;
+        // Should never call SSL_write with 0 length data.
+        assert (_outBuffer.size() > 0);
+        ssize_t len;
+        do
+        {
+            len = SSL_write(_ssl, &_outBuffer[0], _outBuffer.size());
+        }
+        while (len < 0 && errno == EINTR);
+
+        len = handleSslState(len);
+
+        if (len > 0)
+        {
+            // We've sent some data, remove from the buffer.
+            _outBuffer.erase(_outBuffer.begin(),
+                             _outBuffer.begin() + len);
+        }
+        // else poll will handle errors
     }
 
-    void writeOutgoingData() override
+    int getPollEvents() override
     {
-        //TODO;
+        if (_sslWantsTo == SslWantsTo::Read)
+        {
+            // Must read next before attempting to write.
+            return POLLIN;
+        }
+        else if (_sslWantsTo == SslWantsTo::Write)
+        {
+            // Must write next before attempting to read.
+            return POLLOUT;
+        }
+
+        // Do whatever makes sense based on buffer state.
+        return (_outBuffer.empty() ? POLLIN : (POLLIN | POLLOUT));
     }
 
 protected:
     SslStreamSocket(const int fd) :
         BufferingSocket(fd),
-        _ssl(nullptr)
+        _ssl(nullptr),
+        _sslWantsTo(SslWantsTo::ReadOrWrite)
     {
         BIO* bio = BIO_new(BIO_s_socket());
         if (bio == nullptr)
@@ -437,7 +480,92 @@ protected:
     template<class T> friend class ServerSocket;
 
 private:
+
+    /// The possible next I/O operation that SSL want to do.
+    enum class SslWantsTo
+    {
+        ReadOrWrite,
+        Read,
+        Write
+    };
+
+    /// Handles the state of SSL after read or write.
+    int handleSslState(const int rc)
+    {
+        if (rc > 0)
+        {
+            // Success: Reset so we can do either.
+            _sslWantsTo = SslWantsTo::ReadOrWrite;
+            return rc;
+        }
+
+        // Last operation failed. Find out if SSL was trying
+        // to do something different that failed, or not.
+        const int sslError = SSL_get_error(_ssl, rc);
+        switch (sslError)
+        {
+        case SSL_ERROR_ZERO_RETURN:
+            // Shutdown complete, we're disconnected.
+            return 0;
+
+        case SSL_ERROR_WANT_READ:
+            _sslWantsTo = SslWantsTo::Read;
+            return rc;
+
+        case SSL_ERROR_WANT_WRITE:
+            _sslWantsTo = SslWantsTo::Write;
+            return rc;
+
+        case SSL_ERROR_WANT_CONNECT:
+        case SSL_ERROR_WANT_ACCEPT:
+        case SSL_ERROR_WANT_X509_LOOKUP:
+            // Unexpected.
+            return rc;
+
+        case SSL_ERROR_SYSCALL:
+            if (errno != 0)
+            {
+                // Posix API error, let the caller handle.
+                return rc;
+            }
+
+            // fallthrough
+        default:
+            {
+                // The error is comming from BIO. Find out what happened.
+                const long lastError = ERR_get_error();
+                if (lastError == 0)
+                {
+                    if (rc == 0)
+                    {
+                        // Socket closed.
+                        return 0;
+                    }
+                    else if (rc == -1)
+                    {
+                        throw std::runtime_error("SSL Socket closed unexpectedly.");
+                    }
+                    else
+                    {
+                        throw std::runtime_error("SSL BIO reported error [" + std::to_string(rc) + "].");
+                    }
+                }
+                else
+                {
+                    char buf[512];
+                    ERR_error_string_n(lastError, buf, sizeof(buf));
+                    throw std::runtime_error(buf);
+                }
+            }
+            break;
+        }
+
+        return rc;
+    }
+
+private:
     SSL* _ssl;
+    SslWantsTo _sslWantsTo;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list