summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2019-07-28 22:11:18 +0000
committerB Stack <bgstack15@gmail.com>2019-07-28 22:11:18 +0000
commitd55d83514b81127e53ebcaaaf261c40c6bd265b1 (patch)
tree3e1ab2d39bea9777796470da1ee22d8a8591feef /zen
parentMerge branch '10.13' into 'master' (diff)
parentAdd open_ssl.cpp to CPP_FILES in Makefile (diff)
downloadFreeFileSync-d55d83514b81127e53ebcaaaf261c40c6bd265b1.tar.gz
FreeFileSync-d55d83514b81127e53ebcaaaf261c40c6bd265b1.tar.bz2
FreeFileSync-d55d83514b81127e53ebcaaaf261c40c6bd265b1.zip
Merge branch '10.14' into 'master'10.14
10.14 See merge request opensource-tracking/FreeFileSync!11
Diffstat (limited to 'zen')
-rw-r--r--zen/http.cpp75
-rw-r--r--zen/http.h15
-rw-r--r--zen/open_ssl.cpp581
-rw-r--r--zen/open_ssl.h49
-rw-r--r--zen/socket.h5
-rw-r--r--zen/warn_static.h8
6 files changed, 695 insertions, 38 deletions
diff --git a/zen/http.cpp b/zen/http.cpp
index f8538c93..43c9dcbf 100644
--- a/zen/http.cpp
+++ b/zen/http.cpp
@@ -7,6 +7,7 @@
#include "http.h"
#include "socket.h"
+ #include "open_ssl.h"
using namespace zen;
@@ -14,11 +15,15 @@ using namespace zen;
class HttpInputStream::Impl
{
public:
- Impl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError
- const std::vector<std::pair<std::string, std::string>>* postParams) : //issue POST if bound, GET otherwise
+ Impl(const Zstring& url,
+ const std::vector<std::pair<std::string, std::string>>* postParams, //issue POST if bound, GET otherwise
+ bool disableGetCache /*not relevant for POST (= never cached)*/,
+ const Zstring& userAgent,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/,
+ const IOCallback& notifyUnbufferedIO) : //throw SysError
notifyUnbufferedIO_(notifyUnbufferedIO)
{
- ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ );
+ ZEN_ON_SCOPE_FAIL(cleanup(); /*destructor call would lead to member double clean-up!!!*/);
const Zstring urlFmt = afterFirst(url, Zstr("://"), IF_MISSING_RETURN_NONE);
const Zstring server = beforeFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_ALL);
@@ -33,12 +38,15 @@ public:
throw SysError(L"URL uses unexpected protocol.");
}();
- assert(!useTls); //not supported by our plain socket!
- (void)useTls;
-
- socket_ = std::make_unique<Socket>(server, Zstr("http")); //throw SysError
- //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services
+ if (useTls) //HTTP default port: 443, see %WINDIR%\system32\drivers\etc\services
+ {
+ socket_ = std::make_unique<Socket>(server, Zstr("https")); //throw SysError
+ tlsCtx_ = std::make_unique<TlsContext>(socket_->get(), server, caCertFilePath); //throw SysError
+ }
+ else //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services
+ socket_ = std::make_unique<Socket>(server, Zstr("http")); //throw SysError
+ //we don't support "chunked transfer encoding" => HTTP 1.0
std::map<std::string, std::string, LessAsciiNoCase> headers;
headers["Host" ] = utfTo<std::string>(server); //only required for HTTP/1.1
headers["User-Agent"] = utfTo<std::string>(userAgent);
@@ -46,12 +54,11 @@ public:
const std::string postBuf = postParams ? xWwwFormUrlEncode(*postParams) : "";
- if (!postParams) //HTTP GET
+ if (!postParams /*HTTP GET*/ && disableGetCache)
headers["Pragma"] = "no-cache"; //HTTP 1.0 only! superseeded by "Cache-Control"
- //consider internetIsAlive() test; not relevant for POST (= never cached)
else //HTTP POST
{
- headers["Content-type"] = "application/x-www-form-urlencoded";
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
headers["Content-Length"] = numberTo<std::string>(postBuf.size());
}
@@ -64,9 +71,13 @@ public:
//send request
for (size_t bytesToSend = msg.size(); bytesToSend > 0;)
- bytesToSend -= tryWriteSocket(socket_->get(), &*(msg.end() - bytesToSend), bytesToSend); //throw SysError
+ bytesToSend -= tlsCtx_ ?
+ tlsCtx_->tryWrite( &*(msg.end() - bytesToSend), bytesToSend) : //throw SysError
+ tryWriteSocket(socket_->get(), &*(msg.end() - bytesToSend), bytesToSend); //throw SysError
- shutdownSocketSend(socket_->get()); //throw SysError
+ //shutdownSocketSend(socket_->get()); //throw SysError
+ //NO! Sending TCP FIN before receiving response (aka "TCP Half Closed") is not always supported! e.g. Cloudflare server will immediately end connection: recv() returns 0.
+ //"clients SHOULD NOT half-close their TCP connections": https://github.com/httpwg/http-core/issues/22
//receive response:
std::string headBuf;
@@ -164,7 +175,9 @@ private:
return 0;
bytesToRead = static_cast<size_t>(std::min(static_cast<int64_t>(bytesToRead), contentRemaining_)); //[!] contentRemaining_ > 4 GB possible!
}
- const size_t bytesReceived = tryReadSocket(socket_->get(), buffer, bytesToRead); //throw SysError; may return short, only 0 means EOF!
+ const size_t bytesReceived = tlsCtx_ ?
+ tlsCtx_->tryRead( buffer, bytesToRead) : //throw SysError; may return short, only 0 means EOF!
+ tryReadSocket (socket_->get(), buffer, bytesToRead); //
if (contentRemaining_ >= 0)
contentRemaining_ -= bytesReceived;
@@ -174,14 +187,15 @@ private:
return bytesReceived; //"zero indicates end of file"
}
- Impl (const Impl&) = delete;
- Impl& operator=(const Impl&) = delete;
-
void cleanup()
{
}
- std::unique_ptr<Socket> socket_; //*bound* after constructor has run
+ Impl (const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ std::unique_ptr<Socket> socket_; //*bound* after constructor has run
+ std::unique_ptr<TlsContext> tlsCtx_; //optional: support HTTPS
int statusCode_ = 0;
std::map<std::string, std::string, LessAsciiNoCase> responseHeaders_;
@@ -208,14 +222,17 @@ std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl
namespace
{
-std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError
- const std::vector<std::pair<std::string, std::string>>* postParams) //issue POST if bound, GET otherwise
+std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url,
+ const std::vector<std::pair<std::string, std::string>>* postParams /*issue POST if bound, GET otherwise*/,
+ const Zstring& userAgent,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/,
+ const IOCallback& notifyUnbufferedIO) //throw SysError
{
Zstring urlRed = url;
//"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
for (int redirects = 0; redirects < 6; ++redirects)
{
- auto response = std::make_unique<HttpInputStream::Impl>(urlRed, userAgent, notifyUnbufferedIO, postParams); //throw SysError
+ auto response = std::make_unique<HttpInputStream::Impl>(urlRed, postParams, false /*disableGetCache*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError
//http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
const int statusCode = response->getStatusCode();
@@ -310,16 +327,16 @@ std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const st
}
-HttpInputStream zen::sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO,
- const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
+HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector<std::pair<std::string, std::string>>& postParams,
+ const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError
{
- return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, &postParams); //throw SysError
+ return sendHttpRequestImpl(url, &postParams, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError
}
-HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO) //throw SysError
+HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError
{
- return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, nullptr); //throw SysError
+ return sendHttpRequestImpl(url, nullptr /*postParams*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError
}
@@ -328,9 +345,11 @@ bool zen::internetIsAlive() //noexcept
try
{
auto response = std::make_unique<HttpInputStream::Impl>(Zstr("http://www.google.com/"),
+ nullptr /*postParams*/,
+ true /*disableGetCache*/,
Zstr("FreeFileSync"),
- nullptr /*notifyUnbufferedIO*/,
- nullptr /*postParams*/); //throw SysError
+ nullptr /*caCertFilePath*/,
+ nullptr /*notifyUnbufferedIO*/); //throw SysError
const int statusCode = response->getStatusCode();
//attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
diff --git a/zen/http.h b/zen/http.h
index 5d84be2c..2fafa574 100644
--- a/zen/http.h
+++ b/zen/http.h
@@ -15,7 +15,7 @@ namespace zen
{
/*
- thread-safe! (Window/Linux/macOS)
- - HTTPS supported only for Windows
+ - Linux/macOS: init OpenSSL before use!
*/
class HttpInputStream
{
@@ -36,9 +36,16 @@ private:
};
-HttpInputStream sendHttpGet (const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError
-HttpInputStream sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/, //
- const std::vector<std::pair<std::string, std::string>>& postParams);
+HttpInputStream sendHttpGet(const Zstring& url,
+ const Zstring& userAgent,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/,
+ const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError
+
+HttpInputStream sendHttpPost(const Zstring& url,
+ const std::vector<std::pair<std::string, std::string>>& postParams,
+ const Zstring& userAgent,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/,
+ const IOCallback& notifyUnbufferedIO /*throw X*/);
bool internetIsAlive(); //noexcept
std::string xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs);
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
new file mode 100644
index 00000000..0f07e5e3
--- /dev/null
+++ b/zen/open_ssl.cpp
@@ -0,0 +1,581 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "open_ssl.h"
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+using namespace zen;
+
+
+namespace
+{
+/* Sign a file using SHA-256:
+ openssl dgst -sha256 -sign private.pem -out file.sig file.txt
+
+ verify the signature: (caveat: public key expected to be in pkix format!)
+ openssl dgst -sha256 -verify public.pem -signature file.sig file.txt */
+
+
+std::wstring formatOpenSSLError(const std::wstring& functionName, unsigned long ec)
+{
+ char errorBuf[256] = {}; //== buffer size used by ERR_error_string(); err.c: it seems the message uses at most ~200 bytes
+ ::ERR_error_string_n(ec, errorBuf, sizeof(errorBuf)); //includes null-termination
+
+ return formatSystemError(functionName, replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(ec)), utfTo<std::wstring>(errorBuf));
+}
+
+
+std::wstring formatLastOpenSSLError(const std::wstring& functionName)
+{
+ const unsigned long ec = ::ERR_peek_last_error();
+ ::ERR_clear_error(); //clean up for next OpenSSL operation on this thread
+ return formatOpenSSLError(functionName, ec);
+}
+
+//================================================================================
+
+std::shared_ptr<EVP_PKEY> generateRsaKeyPair(int bits) //throw SysError
+{
+ EVP_PKEY_CTX* keyCtx = ::EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, //int id,
+ nullptr); //ENGINE* e
+ if (!keyCtx)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_CTX_new_id"));
+ ZEN_ON_SCOPE_EXIT(::EVP_PKEY_CTX_free(keyCtx));
+
+ if (::EVP_PKEY_keygen_init(keyCtx) != 1)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_keygen_init"));
+
+ //"RSA keys set the key length during key generation rather than parameter generation"
+ if (::EVP_PKEY_CTX_set_rsa_keygen_bits(keyCtx, bits) <= 0) //"[...] return a positive value for success" => effectively returns "1"
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_CTX_set_rsa_keygen_bits"));
+
+ EVP_PKEY* keyPair = nullptr;
+ if (::EVP_PKEY_keygen(keyCtx, &keyPair) != 1)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_keygen"));
+
+ return std::shared_ptr<EVP_PKEY>(keyPair, ::EVP_PKEY_free);
+}
+
+//================================================================================
+
+using BioToEvpFunc = EVP_PKEY* (*)(BIO* bp, EVP_PKEY** x, pem_password_cb* cb, void* u);
+
+std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToEvpFunc bioToEvp, const wchar_t* functionName) //throw SysError
+{
+ BIO* bio = ::BIO_new_mem_buf(keyStream.c_str(), static_cast<int>(keyStream.size()));
+ if (!bio)
+ throw SysError(formatLastOpenSSLError(L"BIO_new_mem_buf"));
+ ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
+
+ if (EVP_PKEY* evpKey = bioToEvp(bio, //BIO* bp,
+ nullptr, //EVP_PKEY** x,
+ nullptr, //pem_password_cb* cb,
+ nullptr)) //void* u
+ return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+ throw SysError(formatLastOpenSSLError(functionName));
+}
+
+
+using BioToRsaFunc = RSA* (*)(BIO* bp, RSA** x, pem_password_cb* cb, void* u);
+
+std::shared_ptr<EVP_PKEY> streamToEvpKey(const std::string& keyStream, BioToRsaFunc bioToRsa, const wchar_t* functionName) //throw SysError
+{
+ BIO* bio = ::BIO_new_mem_buf(keyStream.c_str(), static_cast<int>(keyStream.size()));
+ if (!bio)
+ throw SysError(formatLastOpenSSLError(L"BIO_new_mem_buf"));
+ ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
+
+ RSA* rsa = bioToRsa(bio, //BIO* bp,
+ nullptr, //RSA** x,
+ nullptr, //pem_password_cb* cb,
+ nullptr); //void* u
+ if (!rsa)
+ throw SysError(formatLastOpenSSLError(functionName));
+ ZEN_ON_SCOPE_EXIT(::RSA_free(rsa));
+
+ EVP_PKEY* evpKey = ::EVP_PKEY_new();
+ if (!evpKey)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_new"));
+ std::shared_ptr<EVP_PKEY> sharedKey(evpKey, ::EVP_PKEY_free);
+
+ if (::EVP_PKEY_set1_RSA(evpKey, rsa) != 1) //calls RSA_up_ref() + transfers ownership to evpKey
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_set1_RSA"));
+
+ return sharedKey;
+}
+
+//--------------------------------------------------------------------------------
+
+std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamType streamType, bool publicKey) //throw SysError
+{
+ switch (streamType)
+ {
+ case RsaStreamType::pkix:
+ return publicKey ?
+ streamToEvpKey(keyStream, ::PEM_read_bio_PUBKEY, L"PEM_read_bio_PUBKEY") : //throw SysError
+ streamToEvpKey(keyStream, ::PEM_read_bio_PrivateKey, L"PEM_read_bio_PrivateKey"); //
+
+ case RsaStreamType::pkcs1:
+ return publicKey ?
+ streamToEvpKey(keyStream, ::PEM_read_bio_RSAPublicKey, L"PEM_read_bio_RSAPublicKey") : //throw SysError
+ streamToEvpKey(keyStream, ::PEM_read_bio_RSAPrivateKey, L"PEM_read_bio_RSAPrivateKey"); //
+
+ case RsaStreamType::pkcs1_raw:
+ break;
+ }
+
+ auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str());
+ EVP_PKEY* evpKey = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type,
+ nullptr, //EVP_PKEY** a,
+ &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp,
+ static_cast<long>(keyStream.size())); //long length
+ if (!evpKey)
+ throw SysError(formatLastOpenSSLError(publicKey ? L"d2i_PublicKey" : L"d2i_PrivateKey"));
+ return std::shared_ptr<EVP_PKEY>(evpKey, ::EVP_PKEY_free);
+}
+
+//================================================================================
+
+using EvpToBioFunc = int (*)(BIO* bio, EVP_PKEY* evpKey);
+
+std::string evpKeyToStream(EVP_PKEY* evpKey, EvpToBioFunc evpToBio, const wchar_t* functionName) //throw SysError
+{
+ BIO* bio = ::BIO_new(BIO_s_mem());
+ if (!bio)
+ throw SysError(formatLastOpenSSLError(L"BIO_new"));
+ ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
+
+ if (evpToBio(bio, evpKey) != 1)
+ throw SysError(formatLastOpenSSLError(functionName));
+ //---------------------------------------------
+ const int keyLen = BIO_pending(bio);
+ if (keyLen < 0)
+ throw SysError(formatLastOpenSSLError(L"BIO_pending"));
+ if (keyLen == 0)
+ throw SysError(L"BIO_pending failed."); //no more error details
+
+ std::string keyStream(keyLen, '\0');
+
+ if (::BIO_read(bio, &keyStream[0], keyLen) != keyLen)
+ throw SysError(formatLastOpenSSLError(L"BIO_read"));
+ return keyStream;
+}
+
+
+using RsaToBioFunc = int (*)(BIO* bp, RSA* x);
+
+std::string evpKeyToStream(EVP_PKEY* evpKey, RsaToBioFunc rsaToBio, const wchar_t* functionName) //throw SysError
+{
+ BIO* bio = ::BIO_new(BIO_s_mem());
+ if (!bio)
+ throw SysError(formatLastOpenSSLError(L"BIO_new"));
+ ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio));
+
+ RSA* rsa = ::EVP_PKEY_get0_RSA(evpKey); //unowned reference!
+ if (!rsa)
+ throw SysError(formatLastOpenSSLError(L"EVP_PKEY_get0_RSA"));
+
+ if (rsaToBio(bio, rsa) != 1)
+ throw SysError(formatLastOpenSSLError(functionName));
+ //---------------------------------------------
+ const int keyLen = BIO_pending(bio);
+ if (keyLen < 0)
+ throw SysError(formatLastOpenSSLError(L"BIO_pending"));
+ if (keyLen == 0)
+ throw SysError(L"BIO_pending failed."); //no more error details
+
+ std::string keyStream(keyLen, '\0');
+
+ if (::BIO_read(bio, &keyStream[0], keyLen) != keyLen)
+ throw SysError(formatLastOpenSSLError(L"BIO_read"));
+ return keyStream;
+}
+
+
+//fix OpenSSL API inconsistencies:
+int PEM_write_bio_PrivateKey2(BIO* bio, EVP_PKEY* key)
+{
+ return ::PEM_write_bio_PrivateKey(bio, //BIO* bp,
+ key, //EVP_PKEY* x,
+ nullptr, //const EVP_CIPHER* enc,
+ nullptr, //unsigned char* kstr,
+ 0, //int klen,
+ nullptr, //pem_password_cb* cb,
+ nullptr); //void* u
+}
+
+int PEM_write_bio_RSAPrivateKey2(BIO* bio, RSA* rsa)
+{
+ return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp,
+ rsa, //RSA* x,
+ nullptr, //const EVP_CIPHER* enc,
+ nullptr, //unsigned char* kstr,
+ 0, //int klen,
+ nullptr, //pem_password_cb* cb,
+ nullptr); //void* u
+}
+
+int PEM_write_bio_RSAPublicKey2(BIO* bio, RSA* rsa) { return ::PEM_write_bio_RSAPublicKey(bio, rsa); }
+
+//--------------------------------------------------------------------------------
+
+std::string keyToStream(EVP_PKEY* evpKey, RsaStreamType streamType, bool publicKey) //throw SysError
+{
+ switch (streamType)
+ {
+ case RsaStreamType::pkix:
+ return publicKey ?
+ evpKeyToStream(evpKey, ::PEM_write_bio_PUBKEY, L"PEM_write_bio_PUBKEY") : //throw SysError
+ evpKeyToStream(evpKey, ::PEM_write_bio_PrivateKey2, L"PEM_write_bio_PrivateKey"); //
+
+ case RsaStreamType::pkcs1:
+ return publicKey ?
+ evpKeyToStream(evpKey, ::PEM_write_bio_RSAPublicKey2, L"PEM_write_bio_RSAPublicKey") : //throw SysError
+ evpKeyToStream(evpKey, ::PEM_write_bio_RSAPrivateKey2, L"PEM_write_bio_RSAPrivateKey"); //
+
+ case RsaStreamType::pkcs1_raw:
+ break;
+ }
+
+ unsigned char* buf = nullptr;
+ const int bufSize = (publicKey ? ::i2d_PublicKey : ::i2d_PrivateKey)(evpKey, &buf);
+ if (bufSize <= 0)
+ throw SysError(formatLastOpenSSLError(publicKey ? L"i2d_PublicKey" : L"i2d_PrivateKey"));
+ ZEN_ON_SCOPE_EXIT(::OPENSSL_free(buf)); //memory is only allocated for bufSize > 0
+
+ return { reinterpret_cast<const char*>(buf), static_cast<size_t>(bufSize) };
+}
+
+//================================================================================
+
+std::string createSignature(const std::string& message, EVP_PKEY* privateKey) //throw SysError
+{
+ //https://www.openssl.org/docs/manmaster/man3/EVP_DigestSign.html
+ EVP_MD_CTX* mdctx = ::EVP_MD_CTX_create();
+ if (!mdctx)
+ throw SysError(L"EVP_MD_CTX_create failed."); //no more error details
+ ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx));
+
+ if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx,
+ nullptr, //EVP_PKEY_CTX** pctx,
+ EVP_sha256(), //const EVP_MD* type,
+ nullptr, //ENGINE* e,
+ privateKey) != 1) //EVP_PKEY* pkey
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestSignInit"));
+
+ if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx,
+ message.c_str(), //const void* d,
+ message.size()) != 1) //size_t cnt
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestSignUpdate"));
+
+ size_t sigLenMax = 0; //"first call to EVP_DigestSignFinal returns the maximum buffer size required"
+ if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx,
+ nullptr, //unsigned char* sigret,
+ &sigLenMax) != 1) //size_t* siglen
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestSignFinal"));
+
+ std::string signature(sigLenMax, '\0');
+ size_t sigLen = sigLenMax;
+
+ if (::EVP_DigestSignFinal(mdctx, //EVP_MD_CTX* ctx,
+ reinterpret_cast<unsigned char*>(&signature[0]), //unsigned char* sigret,
+ &sigLen) != 1) //size_t* siglen
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestSignFinal"));
+ signature.resize(sigLen);
+
+ return signature;
+}
+
+
+void verifySignature(const std::string& message, const std::string& signature, EVP_PKEY* publicKey) //throw SysError
+{
+ //https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerify.html
+ EVP_MD_CTX* mdctx = ::EVP_MD_CTX_create();
+ if (!mdctx)
+ throw SysError(L"EVP_MD_CTX_create failed."); //no more error details
+ ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx));
+
+ if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx,
+ nullptr, //EVP_PKEY_CTX** pctx,
+ EVP_sha256(), //const EVP_MD* type,
+ nullptr, //ENGINE* e,
+ publicKey) != 1) //EVP_PKEY* pkey
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestVerifyInit"));
+
+ if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx,
+ message.c_str(), //const void* d,
+ message.size()) != 1) //size_t cnt
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestVerifyUpdate"));
+
+ if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx,
+ reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig,
+ signature.size()) != 1) //size_t siglen
+ throw SysError(formatLastOpenSSLError(L"EVP_DigestVerifyFinal"));
+}
+}
+
+
+std::string zen::convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey) //throw SysError
+{
+ assert(typeFrom != typeTo);
+ std::shared_ptr<EVP_PKEY> evpKey = streamToKey(keyStream, typeFrom, publicKey); //throw SysError
+ return keyToStream(evpKey.get(), typeTo, publicKey); //throw SysError
+}
+
+
+void zen::verifySignature(const std::string& message, const std::string& signature, const std::string& publicKeyStream, RsaStreamType streamType) //throw SysError
+{
+ std::shared_ptr<EVP_PKEY> publicKey = streamToKey(publicKeyStream, streamType, true /*publicKey*/); //throw SysError
+ ::verifySignature(message, signature, publicKey.get()); //throw SysError
+}
+
+
+namespace
+{
+std::wstring formatSslErrorRaw(int ec)
+{
+ switch (ec)
+ {
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_NONE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_SSL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_READ);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_WRITE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_X509_LOOKUP);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_SYSCALL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_ZERO_RETURN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_CONNECT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_ACCEPT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_ASYNC);
+ ZEN_CHECK_CASE_FOR_CONSTANT(SSL_ERROR_WANT_ASYNC_JOB);
+ }
+ return L"Unknown SSL error: " + numberTo<std::wstring>(ec);
+}
+
+
+std::wstring formatX509ErrorRaw(long ec)
+{
+ switch (ec)
+ {
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_OK);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNSPECIFIED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_GET_CRL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_SIGNATURE_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_NOT_YET_VALID);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_HAS_EXPIRED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CRL_NOT_YET_VALID);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CRL_HAS_EXPIRED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_OUT_OF_MEM);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_CHAIN_TOO_LONG);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_REVOKED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_CA);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PATH_LENGTH_EXCEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_PURPOSE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_UNTRUSTED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CERT_REJECTED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUBJECT_ISSUER_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_AKID_SKID_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_KEYUSAGE_NO_CERTSIGN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_NON_CA);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_EXTENSION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_POLICY_EXTENSION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_NO_EXPLICIT_POLICY);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_DIFFERENT_CRL_SCOPE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNNESTED_RESOURCE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PERMITTED_VIOLATION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_EXCLUDED_VIOLATION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUBTREE_MINMAX);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_APPLICATION_VERIFICATION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PATH_LOOP);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_INVALID_VERSION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_INVALID_ALGORITHM);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_INVALID_CURVE);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_HOSTNAME_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_EMAIL_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_IP_ADDRESS_MISMATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_DANE_NO_MATCH);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_EE_KEY_TOO_SMALL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CA_KEY_TOO_SMALL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_CA_MD_TOO_WEAK);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_INVALID_CALL);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_STORE_LOOKUP);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_NO_VALID_SCTS);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_OCSP_VERIFY_NEEDED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_OCSP_VERIFY_FAILED);
+ ZEN_CHECK_CASE_FOR_CONSTANT(X509_V_ERR_OCSP_CERT_UNKNOWN);
+ }
+ return L"Unknown X509 error: " + numberTo<std::wstring>(ec);
+}
+}
+
+class TlsContext::Impl
+{
+public:
+ Impl(int socket, //throw SysError
+ const std::string& server,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/)
+ {
+ ZEN_ON_SCOPE_FAIL(cleanup(); /*destructor call would lead to member double clean-up!!!*/);
+
+ ctx_ = ::SSL_CTX_new(TLS_client_method());
+ if (!ctx_)
+ throw SysError(formatLastOpenSSLError(L"SSL_CTX_new"));
+
+ ssl_ = ::SSL_new(ctx_);
+ if (!ssl_)
+ throw SysError(formatLastOpenSSLError(L"SSL_new"));
+
+ BIO* bio = ::BIO_new_socket(socket, BIO_NOCLOSE);
+ if (!bio)
+ throw SysError(formatLastOpenSSLError(L"BIO_new_socket"));
+ ::SSL_set0_rbio(ssl_, bio); //pass ownership
+
+ if (::BIO_up_ref(bio) != 1)
+ throw SysError(formatLastOpenSSLError(L"BIO_up_ref"));
+ ::SSL_set0_wbio(ssl_, bio); //pass ownership
+
+ assert(::SSL_get_mode(ssl_) == SSL_MODE_AUTO_RETRY); //verify OpenSSL default
+ ::SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+ if (::SSL_set_tlsext_host_name(ssl_, server.c_str()) != 1) //enable SNI (Server Name Indication)
+ throw SysError(formatLastOpenSSLError(L"SSL_set_tlsext_host_name"));
+
+ if (caCertFilePath)
+ {
+ if (!::SSL_CTX_load_verify_locations(ctx_, utfTo<std::string>(*caCertFilePath).c_str(), nullptr))
+ throw SysError(formatLastOpenSSLError(L"SSL_CTX_load_verify_locations"));
+ //alternative: SSL_CTX_set_default_verify_paths(): use OpenSSL default paths considering SSL_CERT_FILE environment variable
+
+ //1. enable check for valid certificate: see SSL_get_verify_result()
+ ::SSL_set_verify(ssl_, SSL_VERIFY_PEER, nullptr);
+
+ //2. enable check that the certificate matches our host: see SSL_get_verify_result()
+ if (::SSL_set1_host(ssl_, server.c_str()) != 1)
+ throw SysError(L"SSL_set1_host failed."); //no more error details
+ }
+
+ const int rv = ::SSL_connect(ssl_); //implicitly calls SSL_set_connect_state()
+ if (rv != 1)
+ throw SysError(formatLastOpenSSLError(L"SSL_connect") + L" " + formatSslErrorRaw(::SSL_get_error(ssl_, rv)));
+
+ if (caCertFilePath)
+ {
+ const long verifyResult = ::SSL_get_verify_result(ssl_);
+ if (verifyResult != X509_V_OK)
+ throw SysError(formatSystemError(L"SSL_get_verify_result", formatX509ErrorRaw(verifyResult), L""));
+ }
+ }
+
+ ~Impl()
+ {
+ //"SSL_shutdown() must not be called if a previous fatal error has occurred on a connection"
+ const bool scopeFail = std::uncaught_exceptions() > exeptionCount_;
+ if (!scopeFail)
+ {
+ //"It is acceptable for an application to only send its shutdown alert and then close
+ //the underlying connection without waiting for the peer's response."
+ [[maybe_unused]] const int rv = ::SSL_shutdown(ssl_);
+ assert(rv == 0); //"the close_notify was sent but the peer did not send it back yet."
+ }
+
+ cleanup();
+ }
+
+ size_t tryRead(void* buffer, size_t bytesToRead) //throw SysError; may return short, only 0 means EOF!
+ {
+ if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ size_t bytesReceived = 0;
+ const int rv = ::SSL_read_ex(ssl_, buffer, bytesToRead, &bytesReceived);
+ if (rv != 1)
+ {
+ const int sslError = ::SSL_get_error(ssl_, rv);
+ if (sslError == SSL_ERROR_ZERO_RETURN || //EOF + close_notify alert
+ (sslError == SSL_ERROR_SYSCALL && ::ERR_peek_last_error() == 0)) //EOF: only expected for HTTP/1.0
+ return 0;
+ throw SysError(formatLastOpenSSLError(L"SSL_read_ex") + L" " + formatSslErrorRaw(sslError));
+ }
+ assert(bytesReceived > 0); //SSL_read_ex() considers EOF an error!
+ if (bytesReceived > bytesToRead) //better safe than sorry
+ throw SysError(L"SSL_read_ex: buffer overflow.");
+
+ return bytesReceived; //"zero indicates end of file"
+ }
+
+ size_t tryWrite(const void* buffer, size_t bytesToWrite) //throw SysError; may return short! CONTRACT: bytesToWrite > 0
+ {
+ if (bytesToWrite == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ size_t bytesWritten = 0;
+ const int rv = ::SSL_write_ex(ssl_, buffer, bytesToWrite, &bytesWritten);
+ if (rv != 1)
+ throw SysError(formatLastOpenSSLError(L"SSL_write_ex") + L" " + formatSslErrorRaw(::SSL_get_error(ssl_, rv)));
+
+ if (bytesWritten > bytesToWrite)
+ throw SysError(L"SSL_write_ex: buffer overflow.");
+ if (bytesWritten == 0)
+ throw SysError(L"SSL_write_ex: zero bytes processed");
+
+ return bytesWritten;
+ }
+
+private:
+ void cleanup()
+ {
+ if (ssl_)
+ ::SSL_free(ssl_);
+
+ if (ctx_)
+ ::SSL_CTX_free(ctx_);
+ }
+
+ Impl (const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ SSL_CTX* ctx_ = nullptr;
+ SSL* ssl_ = nullptr;
+ const int exeptionCount_ = std::uncaught_exceptions();
+};
+
+
+zen::TlsContext::TlsContext(int socket, const Zstring& server, const Zstring* caCertFilePath) :
+ pimpl_(std::make_unique<Impl>(socket, utfTo<std::string>(server), caCertFilePath)) {} //throw SysError
+zen::TlsContext::~TlsContext() {}
+size_t zen::TlsContext::tryRead ( void* buffer, size_t bytesToRead ) { return pimpl_->tryRead(buffer, bytesToRead); } //throw SysError
+size_t zen::TlsContext::tryWrite(const void* buffer, size_t bytesToWrite) { return pimpl_->tryWrite(buffer, bytesToWrite); } //throw SysError
diff --git a/zen/open_ssl.h b/zen/open_ssl.h
new file mode 100644
index 00000000..5bf4e9ce
--- /dev/null
+++ b/zen/open_ssl.h
@@ -0,0 +1,49 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef OPEN_SSL_H_801974580936508934568792347506
+#define OPEN_SSL_H_801974580936508934568792347506
+
+#include <zen/zstring.h>
+#include <zen/sys_error.h>
+
+
+namespace zen //init OpenSSL before use!
+{
+enum class RsaStreamType
+{
+ pkix, //base-64-encoded SubjectPublicKeyInfo structure ("BEGIN PUBLIC KEY")
+ pkcs1, //base-64-encoded RSA number and exponent ("BEGIN RSA PUBLIC KEY")
+ pkcs1_raw
+};
+
+//verify signatures produced with: "openssl dgst -sha256 -sign private.pem -out file.sig file.txt"
+void verifySignature(const std::string& message,
+ const std::string& signature,
+ const std::string& publicKeyStream,
+ RsaStreamType streamType); //throw SysError
+
+std::string convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey); //throw SysError
+
+
+class TlsContext
+{
+public:
+ TlsContext(int socket, //throw SysError
+ const Zstring& server,
+ const Zstring* caCertFilePath /*optional: enable certificate validation*/);
+ ~TlsContext();
+
+ size_t tryRead( void* buffer, size_t bytesToRead ); //throw SysError; may return short, only 0 means EOF!
+ size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw SysError; may return short! CONTRACT: bytesToWrite > 0
+
+private:
+ class Impl;
+ const std::unique_ptr<Impl> pimpl_;
+};
+}
+
+#endif //OPEN_SSL_H_801974580936508934568792347506
diff --git a/zen/socket.h b/zen/socket.h
index 0b4c823d..7ca0c93f 100644
--- a/zen/socket.h
+++ b/zen/socket.h
@@ -105,7 +105,7 @@ size_t tryReadSocket(SocketType socket, void* buffer, size_t bytesToRead) //thro
THROW_LAST_SYS_ERROR_WSA(L"recv");
if (static_cast<size_t>(bytesReceived) > bytesToRead) //better safe than sorry
- throw SysError(L"HttpInputStream::tryRead: buffer overflow.");
+ throw SysError(L"recv: buffer overflow.");
return bytesReceived; //"zero indicates end of file"
}
@@ -138,10 +138,11 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite
}
+//initiate termination of connection by sending TCP FIN package
inline
void shutdownSocketSend(SocketType socket) //throw SysError
{
- if (::shutdown(socket, SHUT_WR) != 0)
+ if (::shutdown(socket, SHUT_WR) != 0)
THROW_LAST_SYS_ERROR_WSA(L"shutdown");
}
diff --git a/zen/warn_static.h b/zen/warn_static.h
index fb8fbb95..17e7cf25 100644
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -8,10 +8,10 @@
#define WARN_STATIC_H_08724567834560832745
/*
-Portable Compile-Time Warning
------------------------------
-Usage:
- warn_static("my message")
+ Portable Compile-Time Warning
+ -----------------------------
+ Usage:
+ warn_static("my message")
*/
#define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM
bgstack15