# HG changeset patch # User Andre Heinecke # Date 1395053507 0 # Node ID d28e2624c1d556e2697538a1ea70df859b6dd19e # Parent c6125d73faf433665b733708285322c0807cd4e5 Reset connection before the next request. This makes the downloader work for the first simple test. diff -r c6125d73faf4 -r d28e2624c1d5 ui/downloader.cpp --- 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; } diff -r c6125d73faf4 -r d28e2624c1d5 ui/sslconnection.cpp --- 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 #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; } diff -r c6125d73faf4 -r d28e2624c1d5 ui/sslconnection.h --- 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 diff -r c6125d73faf4 -r d28e2624c1d5 ui/tests/downloadertest.cpp --- 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);