changeset 46:d28e2624c1d5

Reset connection before the next request. This makes the downloader work for the first simple test.
author Andre Heinecke <aheinecke@intevation.de>
date Mon, 17 Mar 2014 10:51:47 +0000
parents c6125d73faf4
children 7e37c6033c81
files ui/downloader.cpp ui/sslconnection.cpp ui/sslconnection.h ui/tests/downloadertest.cpp
diffstat 4 files changed, 148 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/ui/downloader.cpp	Fri Mar 14 16:40:53 2014 +0000
+++ b/ui/downloader.cpp	Mon Mar 17 10:51:47 2014 +0000
@@ -22,9 +22,6 @@
 #define MAX_SW_SIZE 10485760
 #define MAX_LIST_SIZE 1048576
 
-#define LIST_RESOURCE "/incoming/aheinecke/test"
-#define SW_RESOURCE "/incoming/aheinecke/test"
-
 
 Downloader::Downloader(QObject* parent, const QString& url,
                        const QByteArray& certificate,
@@ -73,9 +70,7 @@
     QTextStream responseStream(&response);
     QLocale cLocale = QLocale::c();
     QString headRequest =
-        QString::fromLatin1("HEAD %1 HTTP/1.1\r\n"
-                "Connection: Keep-Alive\r\n"
-                "\r\n\r\n").arg(resource);
+        QString::fromLatin1("HEAD %1 HTTP/1.0\r\n\r\n").arg(resource);
 
     ret = mSSLConnection.write(headRequest.toUtf8());
     if (ret != 0) {
@@ -104,7 +99,7 @@
             }
         }
     }
-
+    qDebug() << "Response: " << response;
     emit error (tr("Invalid response from the server"), SSLConnection::InvalidResponse);
     return QDateTime();
 }
@@ -116,7 +111,7 @@
     int ret = -1;
     size_t bytesRead = 0;
     QString getRequest =
-        QString::fromLatin1("GET %1 HTTP/1.1\r\n\r\n").arg(resource);
+        QString::fromLatin1("GET %1 HTTP/1.0\r\n\r\n").arg(resource);
 
     QSaveFile outputFile(fileName);
 
@@ -134,19 +129,20 @@
     }
 
     do {
+        /* Read the response in 8KiB chunks */
         QByteArray response = mSSLConnection.read(8192);
         if (response.isNull()) {
             qDebug() << "Error reading response";
             emit error(tr("Connection lost"), SSLConnection::ConnectionLost);
             return false;
         }
-        if (response.isEmpty()) {
-            /* We have read everything there is to read */
+        outputFile.write(response);
+        qDebug() << "Wrote: "<< response.size();
+        bytesRead += response.size();
+        if (response.size() < 8192) {
+            /* Nothing more to read for us */
             break;
         }
-
-        outputFile.write(response);
-        bytesRead += response.size();
     } while (bytesRead < maxSize);
 
     return outputFile.commit();
@@ -194,8 +190,7 @@
 
         qDebug() << "fileName: " << fileName;
 
-        if (!downloadFile(QString::fromLatin1(SW_RESOURCE),
-                   fileName, MAX_SW_SIZE)) {
+        if (!downloadFile(mResourceSW, fileName, MAX_SW_SIZE)) {
             return;
         }
 
@@ -214,8 +209,7 @@
 
         qDebug() << "fileName: " << fileName;
 
-        if (!downloadFile(QString::fromLatin1(LIST_RESOURCE),
-                   fileName, MAX_LIST_SIZE)) {
+        if (!downloadFile(mResourceList, fileName, MAX_LIST_SIZE)) {
             return;
         }
 
--- a/ui/sslconnection.cpp	Fri Mar 14 16:40:53 2014 +0000
+++ b/ui/sslconnection.cpp	Mon Mar 17 10:51:47 2014 +0000
@@ -7,6 +7,7 @@
 #include <QApplication>
 
 #define MAX_IO_TRIES 10
+#define MAX_RESETS 10
 
 #ifdef CONNECTION_DEBUG
 static void my_debug(void *ctx, int level, const char *str)
@@ -36,6 +37,7 @@
     int ret = -1;
 
     memset(&mSSL, 0, sizeof(ssl_context));
+    memset(&mSavedSession, 0, sizeof( ssl_session ) );
 
     if (certificate.isEmpty()) {
         QFile certResource(":certs/kolab.org");
@@ -111,13 +113,7 @@
 }
 
 SSLConnection::~SSLConnection() {
-    if (mConnected) {
-        ssl_close_notify (&mSSL);
-        if (mServerFD != -1) {
-            net_close(mServerFD);
-            mServerFD = -1;
-        }
-    }
+    disconnect();
     x509_crt_free(&mX509PinnedCert);
     entropy_free(&mEntropy);
     if (mInitialized) {
@@ -125,9 +121,20 @@
     }
 }
 
+void SSLConnection::disconnect() {
+    if (mConnected) {
+        ssl_close_notify(&mSSL);
+        if (mServerFD != -1) {
+            net_close(mServerFD);
+            mServerFD = -1;
+        }
+        ssl_session_free(&mSavedSession);
+        mConnected = false;
+    }
+}
+
 int SSLConnection::connect() {
     int ret = -1;
-    const x509_crt *peerCert;
 
     if (!mInitialized) {
         mErrorState = ErrUnknown;
@@ -138,6 +145,7 @@
                   mUrl.port(443));
 
     if (ret != 0) {
+        qDebug() << "Connect failed: " << getErrorMsg(ret);
         mErrorState = NoConnection;
         return ret;
     }
@@ -148,13 +156,34 @@
     while ((ret = ssl_handshake(&mSSL)) != 0) {
         if (ret != POLARSSL_ERR_NET_WANT_READ &&
                 ret != POLARSSL_ERR_NET_WANT_WRITE) {
-            qDebug() << "SSL Handshake failed: "
-                 << getErrorMsg(ret);
+            qDebug() << "SSL Handshake failed: " << getErrorMsg(ret);
             mErrorState = SSLHandshakeFailed;
             return ret;
         }
     }
 
+    ret = ssl_get_session(&mSSL, &mSavedSession);
+    if (ret != 0) {
+        qDebug() << "SSL get session failed: " << getErrorMsg(ret);
+
+        mErrorState = NoConnection;
+        return ret;
+    }
+    printf( " ok\n    [ Ciphersuite is %s ]\n",
+            ssl_get_ciphersuite( &mSSL) );
+    ret = validateCertificate();
+
+    if (ret == 0) {
+        mConnected = true;
+    }
+    return ret;
+}
+
+int SSLConnection::validateCertificate()
+{
+    int ret = -1;
+    const x509_crt *peerCert = NULL;
+
     /* we might want to set the verify function
      * with ssl_set_verify before to archive the
      * certificate pinning. */
@@ -218,7 +247,15 @@
     const unsigned char *buf = (const unsigned char *) request.constData();
     size_t len = (size_t) request.size();
 
-    qDebug() << "Seinding request: " << request;
+    if (mNeedsReset) {
+        ret = reset();
+        if (ret != 0) {
+            qDebug() << "Reset failed: " << getErrorMsg(ret);
+            return ret;
+        }
+    }
+
+    qDebug() << "Sending request: " << request;
     /* According to doc for ssl_write:
      *
      * When this function returns POLARSSL_ERR_NET_WANT_WRITE,
@@ -235,8 +272,8 @@
                 return -1;
             }
         }
-
-        if (ret != POLARSSL_ERR_NET_WANT_WRITE) {
+        if (ret != POLARSSL_ERR_NET_WANT_WRITE &&
+                ret != POLARSSL_ERR_NET_WANT_READ) {
             return ret;
         }
         tries++;
@@ -247,20 +284,72 @@
     return ret;
 }
 
+
+int SSLConnection::reset()
+{
+    int ret = -1;
+    ssl_close_notify(&mSSL);
+
+    ret = ssl_session_reset(&mSSL);
+    if (ret != 0)
+    {
+        qDebug() << "SSL Connection reset failed: "
+                 << getErrorMsg(ret);
+        return ret;
+    }
+
+    ssl_set_session(&mSSL, &mSavedSession);
+
+    ret = net_connect(&mServerFD, mUrl.host().toLatin1().constData(),
+                  mUrl.port(443));
+
+    if (ret != 0) {
+        mErrorState = NoConnection;
+        qDebug() << "Connection failed." << getErrorMsg(ret);
+        return ret;
+    }
+
+    while ((ret = ssl_handshake(&mSSL)) != 0) {
+        if (ret != POLARSSL_ERR_NET_WANT_READ &&
+                ret != POLARSSL_ERR_NET_WANT_WRITE) {
+            qDebug() << "SSL Handshake failed: "
+                 << getErrorMsg(ret);
+            mErrorState = SSLHandshakeFailed;
+            return ret;
+        }
+    }
+
+    qDebug() << "Reset connection. ";
+    /* Validation should not be necessary as we reused a saved
+     * session. But just to be sure. */
+    return validateCertificate();
+}
+
 QByteArray SSLConnection::read(size_t len)
 {
     unsigned char buf[len];
     QByteArray retval("");
     int ret = -1;
+    unsigned int tries = 0;
 
+    mNeedsReset = true;
     do {
         memset (buf, 0, sizeof(buf));
         ret = ssl_read(&mSSL, buf, len);
+        qDebug() << "Read retval : " << ret;
+        qDebug() << "Len is: " << len;
         if (ret == 0 ||
-            ret == POLARSSL_ERR_SSL_CONN_EOF) {
+            ret == POLARSSL_ERR_SSL_CONN_EOF ||
+            ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) {
             /* EOF */
             return retval;
         }
+        if (ret == POLARSSL_ERR_NET_WANT_WRITE ||
+                ret == POLARSSL_ERR_NET_WANT_READ) {
+            net_usleep(100000); /* sleep 100ms to give the socket a chance
+                                   to recover */
+            tries++;
+        }
         if (ret <= 0) {
             qDebug() << "Read failed: " << getErrorMsg(ret);
             return QByteArray();
@@ -271,8 +360,8 @@
             return QByteArray();
         }
         len -= (unsigned int) ret;
-        retval.append((const char *)buf, len);
-    } while (len > 0);
+        retval.append((const char *)buf, ret);
+    } while (len > 0 && tries < MAX_IO_TRIES);
 
     return retval;
 }
--- a/ui/sslconnection.h	Fri Mar 14 16:40:53 2014 +0000
+++ b/ui/sslconnection.h	Mon Mar 17 10:51:47 2014 +0000
@@ -48,8 +48,12 @@
     int write(const QByteArray& request);
 
     /**
-     * @brief read at most len bytes
-     * and return them as a byte array returns a NULL byte array on error*/
+     * @brief read at most len bytes and reset the connection
+     *
+     * @param [in] len Amount of bytes to read.
+     *
+     * @returns a byte array containing the data or
+     * a NULL byte array on error*/
     QByteArray read(size_t len);
 
     bool initialized() { return mInitialized; }
@@ -70,8 +74,11 @@
     entropy_context mEntropy;
     ctr_drbg_context mCtr_drbg;
     ssl_context mSSL;
+    ssl_session mSavedSession;
     bool mInitialized;
-    bool mConnected;
+    bool mConnected; /* A connection was established */
+    bool mNeedsReset; /* The connection needs to be reset before the next
+                         write */
     int mServerFD;
     SSLConnection::ErrorCode mErrorState;
     /* @brief: Initialize polarssl structures
@@ -83,6 +90,24 @@
      * @returns: 0 on success a polarssl error otherwise.
      */
     int init();
+
+    /* @brief Reset the connection.
+     *
+     * Resets the https connection and does another handshake.
+     *
+     * @returns: 0 on success a polarssl error or -1 otherwise. */
+    int reset();
+
+    /* @brief validates that the certificate matches the pinned one.
+     *
+     * Checks the peer certificate of mSSL and validates that the
+     * certificate matches mPinnedCertificate.
+     *
+     * @returns: 0 on success a polarssl error or -1 otherwise. */
+    int validateCertificate();
+
+    /* @brief disconnects the connection */
+    void disconnect();
 };
 
 #endif
--- a/ui/tests/downloadertest.cpp	Fri Mar 14 16:40:53 2014 +0000
+++ b/ui/tests/downloadertest.cpp	Mon Mar 17 10:51:47 2014 +0000
@@ -26,7 +26,7 @@
         "Interface = 127.0.0.1" << endl <<
         "}" << endl <<
         "Hostname = 127.0.0.1" << endl <<
-        "WebsiteRoot = " << SOURCE_DIR << endl;
+        "WebsiteRoot = " << SOURCE_DIR << "/data" << endl;
     configStream.flush();
 
     serverConfig.close();
@@ -60,8 +60,8 @@
             validCert.readAll(),
             QDateTime::currentDateTime(), // Last installed SW
             QDateTime::fromString("2010", "YYYY"),
-            "/list-valid.txt",
-            "/random_2MB");
+            "/random_2MB",
+            "/list-valid.txt");
     validCert.close();
 
     QSignalSpy newListAvailable(downloader,
@@ -78,7 +78,7 @@
     int waited = 0;
     while (newListAvailable.count() == 0 &&
             errors.count() == 0  && waited < 20) {
-        QTest::qWait(200);
+        QTest::qWait(2000);
         waited++;
     }
     QVERIFY(waited != 20);

http://wald.intevation.org/projects/trustbridge/