This commit is contained in:
Sasha Szpakowski
2024-08-10 17:54:23 -03:00
parent 36fa78f392
commit a48639d112
13 changed files with 288 additions and 86 deletions
@@ -5,7 +5,7 @@
#include <sstream>
#include <type_traits>
#include <dlfcn.h>
#include "../common/LibraryLoader.h"
// We want std::string that contains null byte, hence length of 1.
// NOLINTNEXTLINE
@@ -52,10 +52,11 @@ static std::string getStringUTF(JNIEnv *env, jstring str)
AndroidClient::AndroidClient()
: HTTPSClient()
{
LibraryLoader::handle *library = LibraryLoader::GetCurrentProcessHandle();
// Look for SDL_AndroidGetJNIEnv
SDL_AndroidGetJNIEnv = (decltype(SDL_AndroidGetJNIEnv)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetJNIEnv");
LibraryLoader::LoadSymbol(SDL_AndroidGetJNIEnv, library, "SDL_AndroidGetJNIEnv");
// Look for SDL_AndroidGetActivity
SDL_AndroidGetActivity = (decltype(SDL_AndroidGetActivity)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetActivity");
LibraryLoader::LoadSymbol(SDL_AndroidGetActivity, library, "SDL_AndroidGetActivity");
}
bool AndroidClient::valid() const
@@ -78,6 +78,11 @@ HTTPSClient::Reply NSURLClient::request(const HTTPSClient::Request &req)
}
}
if (reply.responseCode == 0 && body == nil && error != nil)
{
reply.body = toCppString(error.localizedDescription);
}
return reply;
}}
@@ -1,6 +1,7 @@
#include "HTTPS.h"
#include "config.h"
#include "ConnectionClient.h"
#include "LibraryLoader.h"
#include <stdexcept>
@@ -65,6 +66,9 @@ static HTTPSClient *clients[] = {
nullptr,
};
// Call into the library loader to make sure it is linked in
static LibraryLoader::handle* dummyProcessHandle = LibraryLoader::GetCurrentProcessHandle();
HTTPSClient::Reply request(const HTTPSClient::Request &req)
{
for (size_t i = 0; clients[i]; ++i)
@@ -0,0 +1,20 @@
#pragma once
namespace LibraryLoader
{
using handle = void;
using function = void();
handle *OpenLibrary(const char *name);
void CloseLibrary(handle *handle);
handle* GetCurrentProcessHandle();
function *GetFunction(handle *handle, const char *name);
template<class T>
inline bool LoadSymbol(T& var, handle *handle, const char *name)
{
var = reinterpret_cast<T>(GetFunction(handle, name));
return var != nullptr;
}
}
@@ -10,6 +10,7 @@
#elif defined(WIN32) || defined(_WIN32)
#define HTTPS_BACKEND_SCHANNEL
#define HTTPS_USE_WINSOCK
#define HTTPS_LIBRARY_LOADER_WINDOWS
#include <winapifamily.h>
#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
// WinINet is only supported on desktop.
@@ -23,9 +24,13 @@
#endif
#elif defined(__ANDROID__)
#define HTTPS_BACKEND_ANDROID
#define HTTPS_LIBRARY_LOADER_UNIX
#elif defined(__APPLE__)
#define HTTPS_BACKEND_NSURL
#define HTTPS_LIBRARY_LOADER_UNIX
#elif defined(linux) || defined(__linux) || defined(__linux__)
#define HTTPS_LIBRARY_LOADER_UNIX
#if defined __has_include
#if __has_include(<curl/curl.h>)
#define HTTPS_BACKEND_CURL
@@ -1,8 +1,3 @@
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#endif
#include "CurlClient.h"
#ifdef HTTPS_BACKEND_CURL
@@ -12,30 +7,12 @@
#include <sstream>
#include <vector>
// Dynamic library loader
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
typedef struct StringReader
{
const std::string *str;
size_t pos;
} StringReader;
template <class T>
static inline bool loadSymbol(T &var, void *handle, const char *name)
{
#ifdef _WIN32
var = (T) GetProcAddress((HMODULE) handle, name);
#else
var = (T) dlsym(handle, name);
#endif
return var != nullptr;
}
CurlClient::Curl::Curl()
: handle(nullptr)
, loaded(false)
@@ -48,33 +25,35 @@ CurlClient::Curl::Curl()
, slist_append(nullptr)
, slist_free_all(nullptr)
{
using namespace LibraryLoader;
#ifdef _WIN32
handle = (void *) LoadLibraryA("libcurl.dll");
handle = OpenLibrary("libcurl.dll");
#else
handle = dlopen("libcurl.so.4", RTLD_LAZY);
handle = OpenLibrary("libcurl.so.4");
#endif
if (!handle)
return;
// Load symbols
decltype(&curl_global_init) global_init = nullptr;
if (!loadSymbol(global_init, handle, "curl_global_init"))
if (!LoadSymbol(global_init, handle, "curl_global_init"))
return;
if (!loadSymbol(global_cleanup, handle, "curl_global_cleanup"))
if (!LoadSymbol(global_cleanup, handle, "curl_global_cleanup"))
return;
if (!loadSymbol(easy_init, handle, "curl_easy_init"))
if (!LoadSymbol(easy_init, handle, "curl_easy_init"))
return;
if (!loadSymbol(easy_cleanup, handle, "curl_easy_cleanup"))
if (!LoadSymbol(easy_cleanup, handle, "curl_easy_cleanup"))
return;
if (!loadSymbol(easy_setopt, handle, "curl_easy_setopt"))
if (!LoadSymbol(easy_setopt, handle, "curl_easy_setopt"))
return;
if (!loadSymbol(easy_perform, handle, "curl_easy_perform"))
if (!LoadSymbol(easy_perform, handle, "curl_easy_perform"))
return;
if (!loadSymbol(easy_getinfo, handle, "curl_easy_getinfo"))
if (!LoadSymbol(easy_getinfo, handle, "curl_easy_getinfo"))
return;
if (!loadSymbol(slist_append, handle, "curl_slist_append"))
if (!LoadSymbol(slist_append, handle, "curl_slist_append"))
return;
if (!loadSymbol(slist_free_all, handle, "curl_slist_free_all"))
if (!LoadSymbol(slist_free_all, handle, "curl_slist_free_all"))
return;
global_init(CURL_GLOBAL_DEFAULT);
@@ -87,11 +66,7 @@ CurlClient::Curl::~Curl()
global_cleanup();
if (handle)
#ifdef _WIN32
FreeLibrary((HMODULE) handle);
#else
dlclose(handle);
#endif
LibraryLoader::CloseLibrary(handle);
}
static char toUppercase(char c)
@@ -7,6 +7,7 @@
#include <curl/curl.h>
#include "../common/HTTPSClient.h"
#include "../common/LibraryLoader.h"
class CurlClient : public HTTPSClient
{
@@ -19,7 +20,7 @@ private:
{
Curl();
~Curl();
void *handle;
LibraryLoader::handle *handle;
bool loaded;
decltype(&curl_global_cleanup) global_cleanup;
@@ -0,0 +1,66 @@
#include "../common/config.h"
#include "../common/LibraryLoader.h"
#ifdef HTTPS_LIBRARY_LOADER_LINKTIME
#include <cstring>
#ifdef HTTPS_BACKEND_CURL
#include <curl/curl.h>
static char CurlHandle;
#endif
#if defined(HTTPS_BACKEND_OPENSSL) || defined(HTTPS_BACKEND_ANDROID)
# error "Selected backends that are not compatible with this loader"
#endif
namespace LibraryLoader
{
handle *OpenLibrary(const char *name)
{
#ifdef HTTPS_BACKEND_CURL
if (strstr(name, "libcurl") == name)
return reinterpret_cast<handle *>(&CurlHandle);
#endif
return nullptr;
}
void CloseLibrary(handle *)
{
}
handle* GetCurrentProcessHandle()
{
return nullptr;
}
function *GetFunction(handle *handle, const char *name)
{
#define RETURN_MATCHING_FUNCTION(func) \
if (strcmp(name, #func) == 0) \
return reinterpret_cast<function *>(&func);
#ifdef HTTPS_BACKEND_CURL
if (handle == &CurlHandle)
{
RETURN_MATCHING_FUNCTION(curl_global_init);
RETURN_MATCHING_FUNCTION(curl_global_cleanup);
RETURN_MATCHING_FUNCTION(curl_easy_init);
RETURN_MATCHING_FUNCTION(curl_easy_cleanup);
RETURN_MATCHING_FUNCTION(curl_easy_setopt);
RETURN_MATCHING_FUNCTION(curl_easy_perform);
RETURN_MATCHING_FUNCTION(curl_easy_getinfo);
RETURN_MATCHING_FUNCTION(curl_slist_append);
RETURN_MATCHING_FUNCTION(curl_slist_free_all);
}
#endif
#undef RETURN_MATCHING_FUNCTION
return nullptr;
}
}
#endif // HTTPS_LIBRARY_LOADER_LINKTIME
@@ -2,65 +2,72 @@
#ifdef HTTPS_BACKEND_OPENSSL
#include <dlfcn.h>
#include "../common/LibraryLoader.h"
// Not present in openssl 1.1 headers
#define SSL_CTRL_OPTIONS 32
template <class T>
static inline bool loadSymbol(T &var, void *handle, const char *name)
static bool TryOpenLibraries(const char *sslName, LibraryLoader::handle *& sslHandle, const char *cryptoName, LibraryLoader::handle *&cryptoHandle)
{
var = reinterpret_cast<T>(dlsym(handle, name));
return var != nullptr;
sslHandle = LibraryLoader::OpenLibrary(sslName);
cryptoHandle = LibraryLoader::OpenLibrary(cryptoName);
if (sslHandle && cryptoHandle)
return true;
if (sslHandle)
LibraryLoader::CloseLibrary(sslHandle);
if (cryptoHandle)
LibraryLoader::CloseLibrary(cryptoHandle);
return false;
}
OpenSSLConnection::SSLFuncs::SSLFuncs()
{
valid = false;
using namespace LibraryLoader;
// Try OpenSSL 1.1
void *sslhandle = dlopen("libssl.so.1.1", RTLD_LAZY);
void *cryptohandle = dlopen("libcrypto.so.1.1", RTLD_LAZY);
// Try OpenSSL 1.0
if (!sslhandle || !cryptohandle)
{
sslhandle = dlopen("libssl.so.1.0.0", RTLD_LAZY);
cryptohandle = dlopen("libcrypto.so.1.0.0", RTLD_LAZY);
}
// Try OpenSSL without version
if (!sslhandle || !cryptohandle)
{
sslhandle = dlopen("libssl.so", RTLD_LAZY);
cryptohandle = dlopen("libcrypto.so", RTLD_LAZY);
}
// Give up
if (!sslhandle || !cryptohandle)
handle *sslhandle = nullptr;
handle *cryptohandle = nullptr;
valid = TryOpenLibraries("libssl.so.3", sslhandle, "libcrypto.so.3", cryptohandle)
|| TryOpenLibraries("libssl.so.1.1", sslhandle, "libcrypto.so.1.1", cryptohandle)
|| TryOpenLibraries("libssl.so.1.0.0", sslhandle, "libcrypto.so.1.0.0", cryptohandle)
// Try the version-less name last, it may not be compatible or tested
|| TryOpenLibraries("libssl.so", sslhandle, "libcrypto.so", cryptohandle);
if (!valid)
return;
valid = true;
valid = valid && (loadSymbol(library_init, sslhandle, "SSL_library_init") ||
loadSymbol(init_ssl, sslhandle, "OPENSSL_init_ssl"));
valid = valid && (
LoadSymbol(init_ssl, sslhandle, "OPENSSL_init_ssl") ||
LoadSymbol(library_init, sslhandle, "SSL_library_init"));
valid = valid && loadSymbol(CTX_new, sslhandle, "SSL_CTX_new");
valid = valid && loadSymbol(CTX_ctrl, sslhandle, "SSL_CTX_ctrl");
valid = valid && loadSymbol(CTX_set_verify, sslhandle, "SSL_CTX_set_verify");
valid = valid && loadSymbol(CTX_set_default_verify_paths, sslhandle, "SSL_CTX_set_default_verify_paths");
valid = valid && loadSymbol(CTX_free, sslhandle, "SSL_CTX_free");
valid = valid && LoadSymbol(CTX_new, sslhandle, "SSL_CTX_new");
valid = valid && LoadSymbol(CTX_ctrl, sslhandle, "SSL_CTX_ctrl");
if (valid)
LoadSymbol(CTX_set_options, sslhandle, "SSL_CTX_set_options");
valid = valid && LoadSymbol(CTX_set_verify, sslhandle, "SSL_CTX_set_verify");
valid = valid && LoadSymbol(CTX_set_default_verify_paths, sslhandle, "SSL_CTX_set_default_verify_paths");
valid = valid && LoadSymbol(CTX_free, sslhandle, "SSL_CTX_free");
valid = valid && loadSymbol(SSL_new, sslhandle, "SSL_new");
valid = valid && loadSymbol(SSL_free, sslhandle, "SSL_free");
valid = valid && loadSymbol(set_fd, sslhandle, "SSL_set_fd");
valid = valid && loadSymbol(connect, sslhandle, "SSL_connect");
valid = valid && loadSymbol(read, sslhandle, "SSL_read");
valid = valid && loadSymbol(write, sslhandle, "SSL_write");
valid = valid && loadSymbol(shutdown, sslhandle, "SSL_shutdown");
valid = valid && loadSymbol(get_verify_result, sslhandle, "SSL_get_verify_result");
valid = valid && loadSymbol(get_peer_certificate, sslhandle, "SSL_get_peer_certificate");
valid = valid && LoadSymbol(SSL_new, sslhandle, "SSL_new");
valid = valid && LoadSymbol(SSL_free, sslhandle, "SSL_free");
valid = valid && LoadSymbol(set_fd, sslhandle, "SSL_set_fd");
valid = valid && LoadSymbol(connect, sslhandle, "SSL_connect");
valid = valid && LoadSymbol(read, sslhandle, "SSL_read");
valid = valid && LoadSymbol(write, sslhandle, "SSL_write");
valid = valid && LoadSymbol(shutdown, sslhandle, "SSL_shutdown");
valid = valid && LoadSymbol(get_verify_result, sslhandle, "SSL_get_verify_result");
valid = valid && (LoadSymbol(get_peer_certificate, sslhandle, "SSL_get1_peer_certificate") ||
LoadSymbol(get_peer_certificate, sslhandle, "SSL_get_peer_certificate"));
valid = valid && (loadSymbol(SSLv23_method, sslhandle, "SSLv23_method") ||
loadSymbol(SSLv23_method, sslhandle, "TLS_method"));
valid = valid && (
LoadSymbol(SSLv23_method, sslhandle, "TLS_client_method") ||
LoadSymbol(SSLv23_method, sslhandle, "TLS_method") ||
LoadSymbol(SSLv23_method, sslhandle, "SSLv23_method"));
valid = valid && loadSymbol(check_host, cryptohandle, "X509_check_host");
valid = valid && LoadSymbol(check_host, cryptohandle, "X509_check_host");
valid = valid && LoadSymbol(X509_free, cryptohandle, "X509_free");
if (library_init)
library_init();
@@ -81,7 +88,10 @@ OpenSSLConnection::OpenSSLConnection()
if (!context)
return;
ssl.CTX_ctrl(context, SSL_CTRL_OPTIONS, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3, nullptr);
if (ssl.CTX_set_options)
ssl.CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
else
ssl.CTX_ctrl(context, SSL_CTRL_OPTIONS, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3, nullptr);
ssl.CTX_set_verify(context, SSL_VERIFY_PEER, nullptr);
ssl.CTX_set_default_verify_paths(context);
}
@@ -123,6 +133,7 @@ bool OpenSSLConnection::connect(const std::string &hostname, uint16_t port)
close();
return false;
}
ssl.X509_free(cert);
return true;
}
@@ -36,6 +36,7 @@ private:
SSL_CTX *(*CTX_new)(const SSL_METHOD *method);
long (*CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
long (*CTX_set_options)(SSL_CTX *ctx, long options);
void (*CTX_set_verify)(SSL_CTX *ctx, int mode, void *verify_callback);
int (*CTX_set_default_verify_paths)(SSL_CTX *ctx);
void (*CTX_free)(SSL_CTX *ctx);
@@ -53,6 +54,7 @@ private:
const SSL_METHOD *(*SSLv23_method)();
int (*check_host)(X509 *cert, const char *name, size_t namelen, unsigned int flags, char **peername);
void (*X509_free)(X509* cert);
};
static SSLFuncs ssl;
};
@@ -0,0 +1,33 @@
#include "../common/config.h"
#include "../common/LibraryLoader.h"
#ifdef HTTPS_LIBRARY_LOADER_UNIX
#include <dlfcn.h>
namespace LibraryLoader
{
handle *OpenLibrary(const char *name)
{
return dlopen(name, RTLD_LAZY);
}
void CloseLibrary(handle *handle)
{
if (handle)
dlclose(handle);
}
handle* GetCurrentProcessHandle()
{
return RTLD_DEFAULT;
}
function *GetFunction(handle *handle, const char *name)
{
return reinterpret_cast<function *>(dlsym(handle, name));
}
}
#endif // HTTPS_LIBRARY_LOADER_UNIX
@@ -0,0 +1,35 @@
#include "../common/config.h"
#include "../common/LibraryLoader.h"
#ifdef HTTPS_LIBRARY_LOADER_WINDOWS
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace LibraryLoader
{
handle *OpenLibrary(const char *name)
{
return reinterpret_cast<handle *>(LoadLibraryA(name));
}
void CloseLibrary(handle *handle)
{
if (handle)
FreeLibrary(reinterpret_cast<HMODULE>(handle));
}
handle* GetCurrentProcessHandle()
{
return reinterpret_cast<handle *>(GetModuleHandle(nullptr));
}
function *GetFunction(handle *handle, const char *name)
{
HMODULE nativeHandle = reinterpret_cast<HMODULE>(handle);
return reinterpret_cast<function *>(GetProcAddress(nativeHandle, name));
}
}
#endif // HTTPS_LIBRARY_LOADER_WINDOWS