Files
integration/Source/Integration/LuprexSockets.cpp

1072 lines
29 KiB
C++
Raw Normal View History

2023-06-23 16:27:23 -04:00
#include "LuprexSockets.h"
#include "LuprexGameModeBase.h"
#include "lpx-enginewrapper.hpp"
#include "lpx-drvutil.hpp"
2023-06-26 17:28:07 -04:00
#include "Sockets.h"
#include "SocketTypes.h"
#include "SocketSubsystem.h"
#include "AddressInfoTypes.h"
2023-06-23 16:27:23 -04:00
2023-09-04 03:21:23 -04:00
2023-06-23 16:27:23 -04:00
#define UI UI_ST
THIRD_PARTY_INCLUDES_START
#include <openssl/ssl.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/conf.h>
THIRD_PARTY_INCLUDES_END
#undef UI
2023-07-03 15:28:14 -04:00
2023-10-16 15:08:11 -04:00
#ifdef __linux__
#else
2023-06-26 17:28:07 -04:00
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
2023-10-16 15:08:11 -04:00
#endif
2023-06-26 17:28:07 -04:00
#include <cstdio>
#include <string>
#include <string_view>
2023-06-23 16:27:23 -04:00
2023-06-26 17:28:07 -04:00
#define MAX_BIO_BUFFER (128 * 1024)
2023-06-23 16:27:23 -04:00
2023-07-03 15:28:14 -04:00
static const char* dummy_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIDezCCAmOgAwIBAgIUajKmxrLMr9zBMlphrTJU5qKG8FgwDQYJKoZIhvcNAQEL\n"
"BQAwTDELMAkGA1UEBhMCVVMxFTATBgNVBAgMDFBlbm5zeWx2YW5pYTESMBAGA1UE\n"
"CgwJbG9jYWxob3N0MRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjIwMzIyMTczMzA4\n"
"WhgPMjEyMjAyMjYxNzMzMDhaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQIDAxQZW5u\n"
"c3lsdmFuaWExEjAQBgNVBAoMCWxvY2FsaG9zdDESMBAGA1UEAwwJbG9jYWxob3N0\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5OWIaKqYae4nPxvu5EP3\n"
"VilcjApYcMT4+2ypfQoB6PEep5lwguA929rNsTKnhGsEiQAZ0eZPEZN7VhUwf/hz\n"
"26jIyTT43ELkt6k97wwSZSXuT65RpSiemwEs6g2mMwzpgP6nv+yam4HjE9AKiHGN\n"
"YeTV72Nw1EN70t6IjIf4jsJRXqDJkUx5sSSD6j0WBTOhzozIDgZHTDwiLhatE66m\n"
"SNoD8oWC0PscbUgOJkFpbaCAS8RJmpsdgkTFae2rzL9cOFLGw6OgV/BV1J1s0ks8\n"
"+veoMMtIO6fese+OZ+DyQbuGaoaltZUXzY6QjD5l34m2mGplelT7BrpcqJTBHwmh\n"
"CwIDAQABo1MwUTAdBgNVHQ4EFgQUXQM5TVfJ9gpUXg8fZ8yfuUVcBP8wHwYDVR0j\n"
"BBgwFoAUXQM5TVfJ9gpUXg8fZ8yfuUVcBP8wDwYDVR0TAQH/BAUwAwEB/zANBgkq\n"
"hkiG9w0BAQsFAAOCAQEAqYX/ZGv0Qh/xdXppjnqojm8mH0giDW4tvwMqHcW3YRa3\n"
"9J2yYot+rHjU5g4n6HEmWDBE0eqLz9n3Y3fkFzT8RWZwBaST965CgsfGofyuA2hC\n"
"Ddn4Am3B5tTPmi8WWRZg8amhpGVD/mwkoVFIK0M337b1aZUJYPE+Kc9WetSL2KqB\n"
"EhqSQpkAWhVadzP85dq2T9EDjAvhlFTFlDEBx1GDUcc8M0KQ9NEvLT7LgoUcbMiT\n"
"PerlSZQTB0crchXTRSERgiwu80r7D6STn/RcPL9Fg5PkA94/d87jGbmV4sxSRsvM\n"
"z+DnJGjHrV1J/jHPrnVvVLpigBlGno3C5O/sRw3gcQ==\n"
"-----END CERTIFICATE-----\n";
static const char* dummy_key =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDk5Yhoqphp7ic/\n"
"G+7kQ/dWKVyMClhwxPj7bKl9CgHo8R6nmXCC4D3b2s2xMqeEawSJABnR5k8Rk3tW\n"
"FTB/+HPbqMjJNPjcQuS3qT3vDBJlJe5PrlGlKJ6bASzqDaYzDOmA/qe/7JqbgeMT\n"
"0AqIcY1h5NXvY3DUQ3vS3oiMh/iOwlFeoMmRTHmxJIPqPRYFM6HOjMgOBkdMPCIu\n"
"Fq0TrqZI2gPyhYLQ+xxtSA4mQWltoIBLxEmamx2CRMVp7avMv1w4UsbDo6BX8FXU\n"
"nWzSSzz696gwy0g7p96x745n4PJBu4ZqhqW1lRfNjpCMPmXfibaYamV6VPsGulyo\n"
"lMEfCaELAgMBAAECggEBAJa1AiFX4U4tva1xqNKmZV1XklWqIhzts7lnDBkF08gZ\n"
"qcNT5Z5mIpR09eVropwvEidZ56Yp63l5D0XYYbyAS1gfQ0QnGot7h7fdOKgB3MK4\n"
"PLY94gfKPNN17KqWHg2SvNNv1+cn04v78xUCb0zy5tHDp5Acexdm70ohtupARElJ\n"
"LSHdS7ebsqZUFXbbM3BpPEsQLi3PrzNs1DrKkZ3rR6eMGrsDqExXx8/foi9aZKsd\n"
"BGM2/kcTJ5aY6NhSv5iqO1oK46sbMrjVW/bYNsOyl0eFjwTRahn+Zhp/JMewZYeu\n"
"715g6kzbZNwEzBLgrhNPF6E2ycEr/C6z5bE78g5QCkECgYEA8s07UUY25bjYiWWy\n"
"W38pT7d/OXBSyKnq16N6MjVahl29r7nezFiDeLhLC0QiwXu/+qyxVZkB95MMGZXS\n"
"AsaKFNis3AJ6eR4SYyhpSScYKNvlKIiW37TtR4FDcy7y5LL6tFpiDDIGH3LuyWNo\n"
"d76142MBpv5aStnLGYU3pcZj43sCgYEA8VbNM4nqgSCQcbnHYjvsgphEMNSaoVie\n"
"xob2uigXdV6Te0ayoUFBnVNKVsRhk+sswuTV4k1pK/On+USVl2tQ16tcaVMjTfSD\n"
"HLYTJLmt6s4DcywWj5dfkbDoe5PulGXNZE960qXmOC62Lf0VMRwJ5x4FBRvGTjKC\n"
"zvekI2/kO7ECgYEAhBGeclb/BXXGUvY+TgadMf9d9KBkZ0IFu8Xwcd8TnoLe6vbv\n"
"ebery75zE228egIWKwREcYsIxuH1cvVLhrb35N73J7UxaTAyUD1rB598RL1XqPSj\n"
"HIwNhReK2NxwwnWYaQHA02FiczjRKjooWPojdcwk2fEArDZLg1YzLrj7HIECgYEA\n"
"htdx1Y8ESFtyeShMv5UtoxYCW6oeL3H9XH0CE6bc3IYYLvOkULbOO2HTEkGtJ2Fp\n"
"5AbJfiS0U4tS2dI5Jp4eUDH9cxexjRfFvd/5ODbKdnver5X9kQMJsbQ/YPSZg66R\n"
"oK9Lt7Bbvh5TScSy93psCgba1SzckspkDdGNkwMsaTECgYEAnFWaxormLUpXQRLs\n"
"tKzMMHgVnHlsHiqXH432zmT2fpGZHYoWbsGuQjjrHGnSiu3QbDhnzM6y/T2GRs6z\n"
"zHteIo/tzIyxg4MvJGJ9qANA7HoiKBdQ7G/I/NLJIyWAjj+e7/hgzKFcf+dpjpDq\n"
"HcKc9a4WXhC7yu79e5BnKWltHXY=\n"
"-----END PRIVATE KEY-----\n";
2023-06-23 16:27:23 -04:00
2023-09-15 13:28:18 -04:00
class FlxSocketsI;
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
/////////////////////////////////////////////////////////////////
//
2023-06-26 17:28:07 -04:00
// A port-listening socket.
2023-07-03 15:28:14 -04:00
//
/////////////////////////////////////////////////////////////////
2023-06-26 17:28:07 -04:00
class FLpxListener
{
public:
2023-09-15 13:28:18 -04:00
FlxSocketsI *LSI;
2023-06-26 17:28:07 -04:00
int BoundPort;
FSocket* Socket;
2023-09-15 13:28:18 -04:00
FLpxListener(FlxSocketsI *lsi, int bp, FSocket* sock);
2023-06-26 17:28:07 -04:00
~FLpxListener();
2023-07-03 15:28:14 -04:00
void AcceptConnection();
2023-06-26 17:28:07 -04:00
};
2023-07-03 15:28:14 -04:00
/////////////////////////////////////////////////////////////////
//
2023-06-23 16:27:23 -04:00
// A communication socket.
2023-07-03 15:28:14 -04:00
//
/////////////////////////////////////////////////////////////////
enum EChanState {
CHAN_INACTIVE,
CHAN_SSL_CONNECTING,
CHAN_SSL_ACCEPTING,
CHAN_SSL_READWRITE,
};
2023-06-23 16:27:23 -04:00
class FLpxChannel
{
2023-06-26 17:28:07 -04:00
public:
2023-09-15 13:28:18 -04:00
FlxSocketsI* LSI;
2023-07-03 15:28:14 -04:00
EChanState State;
2023-06-23 16:27:23 -04:00
int ChannelID;
FSocket* Socket;
SSL* SSLState;
BIO* RecvBIO;
BIO* SendBIO;
2023-07-03 15:28:14 -04:00
// True if the socket receive operation has ever failed.
bool RecvFail;
// True if the socket send operation has ever failed.
bool SendFail;
// Most recent probe of the Luprex output buffer for this channel.
uint32_t NBytes;
const char* Bytes;
2023-06-23 16:27:23 -04:00
// OpenSSL has a rule: if you try to SSL_write and it returns
// SSL_ERROR_WANT_READ, then you have to retry the write with the same
// number of bytes. In this event, we record how many bytes we
// attempted to write, which will enable us to retry.
int RetryWriteNBytes;
2023-06-26 17:28:07 -04:00
void Close(std::string_view error);
// Copy data from the socket into the recv bio.
2023-07-03 15:28:14 -04:00
// If it detects an error or EOF, sets the RecvFail flag.
// Once a RecvFail has occurred, further calls will be a No-OP.
2023-06-26 17:28:07 -04:00
void TransferSocketToRecvBIO();
// Copy data from the send bio to the socket.
2023-07-03 15:28:14 -04:00
// If it detects an error or EOF, sets the SendFail flag.
// Once a SendFail has occurred, further calls will be a No-Op.
2023-06-26 17:28:07 -04:00
void TransferSendBIOToSocket();
2023-07-03 15:28:14 -04:00
// Advance the channel
//
void AdvanceConnecting();
void AdvanceAccepting();
void AdvanceReadWrite();
void Advance();
2023-06-26 17:28:07 -04:00
// Check if an SSL error is serious. If so, close the channel.
// The 'retval' is the return value of the SSL function that returned an
2023-07-03 15:28:14 -04:00
// error. All errors are considered serious except for SSL_ERROR_WANT_READ,
// which is not serious because it is transient. However, if you get an
2023-06-26 17:28:07 -04:00
// SSL_ERROR_WANT_READ when there's tons of data available in the read
// buffer, that's inexplicable and therefore serious.
void CloseChannelIfSSLErrorIsSerious(int retval);
2023-09-15 13:28:18 -04:00
FLpxChannel(FlxSocketsI *lsi, FSocket* sock, int chid, SSL_CTX* ctx, EChanState state);
2023-07-03 15:28:14 -04:00
FLpxChannel() : FLpxChannel(nullptr, nullptr, 0, nullptr, CHAN_INACTIVE) {}
2023-09-03 02:01:32 -04:00
~FLpxChannel() { }
2023-06-23 16:27:23 -04:00
};
2023-07-03 15:28:14 -04:00
/////////////////////////////////////////////////////////////////
//
// The Entire Socket System Implementation
//
/////////////////////////////////////////////////////////////////
2023-09-15 13:28:18 -04:00
class FlxSocketsI : public FlxSockets
2023-06-23 16:27:23 -04:00
{
public:
2023-06-26 17:28:07 -04:00
// Fatal error status.
std::string FatalError;
2023-09-03 02:01:32 -04:00
// This pointer is NULL except when inside
// one of the methods that accepts a LockedWrapper.
2023-06-23 16:27:23 -04:00
EngineWrapper* Luprex;
2023-06-26 17:28:07 -04:00
// A general-purpose character buffer.
char ChBuf[DRV_SHORTSTRING_SIZE];
2023-06-23 16:27:23 -04:00
2023-06-26 17:28:07 -04:00
TArray<FLpxChannel> Channels;
TArray<FLpxListener> Listeners;
// Pointer to the socket subsystem.
ISocketSubsystem* Subsys;
2023-06-23 16:27:23 -04:00
2023-07-03 15:28:14 -04:00
BIO* TraceBIO;
2023-06-23 16:27:23 -04:00
SSL_CTX* ServerCTX;
SSL_CTX* ClientSecureCTX;
SSL_CTX* ClientInsecureCTX;
2023-09-15 13:28:18 -04:00
FlxSocketsI(FlxLockedWrapper &w);
virtual ~FlxSocketsI() override;
2023-06-23 16:27:23 -04:00
// Copy the trace to UE_LOG.
void LogTrace();
2023-06-26 17:28:07 -04:00
// Error handling.
void SetError(const std::string& s);
virtual std::string GetError() override { return FatalError; }
bool AnyError() { return !FatalError.empty(); }
// Return true if we're listening on port P.
bool ListeningOnPort(int p);
// Handle various phases of the operation.
2023-07-03 15:28:14 -04:00
void RemoveInactiveChannels();
2023-06-26 17:28:07 -04:00
void HandleListenPorts();
void HandleNewOutgoingSockets();
void HandleSocketInputOutput();
2023-09-03 02:01:32 -04:00
// Force Close Everything.
2023-09-15 13:28:18 -04:00
virtual void ForceCloseEverything(FlxLockedWrapper& w);
2023-09-03 02:01:32 -04:00
2023-06-26 17:28:07 -04:00
// Main update routine.
2023-09-15 13:28:18 -04:00
virtual void Update(FlxLockedWrapper &w) override;
2023-06-23 16:27:23 -04:00
};
2023-07-03 15:28:14 -04:00
/////////////////////////////////////////////////////////////////
//
// General Support Functions
//
/////////////////////////////////////////////////////////////////
2023-06-26 17:28:07 -04:00
2023-10-16 15:08:11 -04:00
#ifdef __linux__
inline static void strerror_helper(int status, int errnum, char errbuf[256]) {
if (status != 0) {
snprintf(errbuf, 256, "unknown errno %d", errnum);
}
}
inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) {
if (result != errbuf) {
snprintf(errbuf, 256, "%s", result);
}
}
static std::string strerror_str(int errnum) {
char buf[256];
auto rval = strerror_r(errnum, buf, 256);
strerror_helper(rval, errnum, buf);
return buf;
}
#else
2023-06-26 17:28:07 -04:00
static std::string strerror_str(int errnum) {
char buf[256];
int status = strerror_s(buf, 256, errnum);
if (status != 0)
{
snprintf(buf, 256, "unknown errno %d", errnum);
}
return buf;
}
2023-10-16 15:08:11 -04:00
#endif
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
static FSocket* OpenConnection(ISocketSubsystem *subsys, const std::string& host, const std::string& port, std::string& err)
{
std::string hostport = host + ":" + port;
FString fshost(host.size(), (const UTF8CHAR*)host.c_str());
FString fsport(port.size(), (const UTF8CHAR*)port.c_str());
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
FAddressInfoResult air = subsys->GetAddressInfo(*fshost, *fsport, EAddressInfoFlags::Default, NAME_None, ESocketType::SOCKTYPE_Streaming);
if (air.Results.Num() == 0) {
err = std::string("DNS Lookup failed for: ") + hostport;
return nullptr;
}
const FAddressInfoResultData& dns = air.Results[0];
const FInternetAddr& inetaddr = *dns.Address;
std::string sdescription = host + ":" + port;
FString description(sdescription.c_str());
FSocket* Socket = subsys->CreateSocket(NAME_Stream, description, inetaddr.GetProtocolType());
if (Socket == nullptr)
{
err = std::string("Could not create socket for ") + hostport;
return nullptr;
}
bool connected = Socket->Connect(inetaddr);
if (!connected)
{
subsys->DestroySocket(Socket);
err = std::string("Could not connect to ") + hostport;
return nullptr;
}
Socket->SetNonBlocking(true);
err = "";
return Socket;
}
FSocket* ListenOnPort(ISocketSubsystem* subsys, int port, std::string& err)
{
err = "";
TSharedPtr<FInternetAddr> addr = subsys->CreateInternetAddr();
bool ok;
addr->SetIp(TEXT("127.0.0.1"), ok);
if (!ok)
{
err = "Could not parse internet address";
return nullptr;
}
addr->SetPort(port);
FSocket* sock = subsys->CreateSocket(NAME_Stream, TEXT("Listener"), addr->GetProtocolType());
if (sock == nullptr)
{
err = "Could not create socket for listening";
return nullptr;
}
if (!sock->SetReuseAddr(true))
{
subsys->DestroySocket(sock);
err = "Could not configure socket to reuse address";
return nullptr;
}
if (!sock->Bind(*addr) || !sock->Listen(20))
{
subsys->DestroySocket(sock);
err = "Could not bind socket to local port";
return nullptr;
}
if (!sock->SetLinger(false))
{
subsys->DestroySocket(sock);
err = "Could not configure socket to not linger";
return nullptr;
}
if (!sock->SetNonBlocking(true))
{
subsys->DestroySocket(sock);
err = "Could not configure socket for nonblocking";
return nullptr;
}
return sock;
}
/////////////////////////////////////////////////////////////////
//
// OpenSSL-related Support Functions
//
/////////////////////////////////////////////////////////////////
static void SSLClearErrors() {
ERR_clear_error();
errno = 0;
}
static std::string SSLFullErrorString() {
BIO* b = BIO_new(BIO_s_mem());
ERR_print_errors(b);
char* data;
int ndata = BIO_get_mem_data(b, &data);
std::string result(' ', ndata);
memcpy(&result[0], data, ndata);
BIO_free(b);
return result;
}
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
static std::string SSLErrorString() {
2023-06-26 17:28:07 -04:00
// Get the last code.
int code = 0;
while (true) {
int icode = ERR_get_error();
if (icode == 0) break;
code = icode;
}
// Fetch and clear errno.
int terrno = errno;
errno = 0;
if (code != 0) {
const char* rc = ERR_reason_error_string(code);
if (rc != nullptr) {
return rc;
}
else {
return strerror_str(ERR_GET_REASON(code));
}
}
else if (terrno != 0) {
return strerror_str(terrno);
}
else {
return "";
}
}
2023-07-03 15:28:14 -04:00
static SSL_CTX* SSLNewContext(int verify, const SSL_METHOD *method, BIO *tracebio) {
check(method != nullptr);
SSL_CTX* ctx = SSL_CTX_new(method);
2023-06-26 17:28:07 -04:00
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_verify(ctx, verify, nullptr);
2023-07-03 15:28:14 -04:00
SSL_CTX_set_ecdh_auto(ctx, 1);
//if (tracebio != nullptr)
//{
// SSL_CTX_set_msg_callback(ctx, SSL_trace);
// SSL_CTX_set_msg_callback_arg(ctx, tracebio);
//}
2023-06-26 17:28:07 -04:00
return ctx;
}
2023-10-16 15:08:11 -04:00
#ifdef __linux__
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
check(SSL_CTX_set_default_verify_paths(ctx) == 1);
return "";
}
#else
2023-06-26 17:28:07 -04:00
static std::string SSLLoadCertificateAuthorities(SSL_CTX* ctx) {
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
if (!hStore) {
return "Could not open system cert store.";
}
PCCERT_CONTEXT pContext = NULL;
X509* x509;
X509_STORE* store = SSL_CTX_get_cert_store(ctx);
while (true)
{
pContext = CertEnumCertificatesInStore(hStore, pContext);
if (pContext == nullptr) break;
const unsigned char* encoded_cert = pContext->pbCertEncoded;
x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
if (x509) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertCloseStore(hStore, 0);
return "";
}
2023-10-16 15:08:11 -04:00
#endif
2023-06-26 17:28:07 -04:00
static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) {
2023-07-03 15:28:14 -04:00
SSLClearErrors();
2023-06-26 17:28:07 -04:00
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, str);
X509* certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
2023-07-03 15:28:14 -04:00
if (certificate == nullptr)
{
return "Could not parse PEM certificate string";
}
2023-06-26 17:28:07 -04:00
std::string result;
if (SSL_CTX_use_certificate(ctx, certificate) <= 0)
{
result = SSLErrorString();
}
X509_free(certificate);
BIO_free(bio);
return result;
}
2023-07-03 15:28:14 -04:00
static std::string SSLUsePrivateKeyString(SSL_CTX* ctx, const char* str) {
SSLClearErrors();
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, str);
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
if (pkey == nullptr)
{
return "Could not parse PEM private key string";
}
std::string result;
if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0)
{
result = SSLErrorString();
}
EVP_PKEY_free(pkey);
BIO_free(bio);
return result;
}
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
static std::string SSLLoadDummyCert(SSL_CTX* ctx)
2023-06-26 17:28:07 -04:00
{
std::string err1 = SSLUseCertificateString(ctx, dummy_cert);
2023-07-03 15:28:14 -04:00
std::string err2 = SSLUsePrivateKeyString(ctx, dummy_key);
2023-06-26 17:28:07 -04:00
if (!err1.empty()) return err1;
2023-07-03 15:28:14 -04:00
if (!err2.empty()) return err2;
2023-06-26 17:28:07 -04:00
return "";
}
// Return the amount of 'space left' in a BIO. This is a fiction,
// because MEM BIOs technically have unlimited capacity. We're
// artificially limiting them to a certain size because there's no
// reason to buffer huge amounts of data.
2023-07-03 15:28:14 -04:00
static int BIOSpace(BIO* bio) {
2023-06-26 17:28:07 -04:00
int space = (MAX_BIO_BUFFER)-BIO_pending(bio);
if (space < 0) space = 0;
return space;
}
// Discard the first nbytes in buffer.
// This is a terribly inefficient way to discard data that has
// already been processed. There has to be something better.
2023-07-03 15:28:14 -04:00
static void BIODiscard(BIO* b, int nbytes, char* chbuf) {
2023-06-26 17:28:07 -04:00
while (nbytes > 0) {
int nread = nbytes;
if (nread > DRV_SHORTSTRING_SIZE) nread = DRV_SHORTSTRING_SIZE;
int ndropped = BIO_read(b, chbuf, nread);
check(ndropped == nread);
nbytes -= ndropped;
}
}
2023-07-03 15:28:14 -04:00
/////////////////////////////////////////////////////////////////
//
// Channel Methods
//
/////////////////////////////////////////////////////////////////
#pragma optimize("", off)
2023-09-15 13:28:18 -04:00
FLpxChannel::FLpxChannel(FlxSocketsI* lsi, FSocket* sock, int chid, SSL_CTX* ctx, EChanState st)
2023-06-26 17:28:07 -04:00
{
LSI = lsi;
ChannelID = chid;
Socket = sock;
RecvBIO = BIO_new(BIO_s_mem());
SendBIO = BIO_new(BIO_s_mem());
2023-07-03 15:28:14 -04:00
RecvFail = false;
SendFail = false;
NBytes = 0;
Bytes = nullptr;
2023-06-26 17:28:07 -04:00
RetryWriteNBytes = 0;
2023-07-03 15:28:14 -04:00
SSLState = SSL_new(ctx);
SSL_set_bio(SSLState, RecvBIO, SendBIO);
2023-06-26 17:28:07 -04:00
State = st;
}
void FLpxChannel::Close(std::string_view err) {
// Close and release the SSL channel.
// This frees the BIO objects as well.
if (SSLState != nullptr) {
SSL_free(SSLState);
SSLState = nullptr;
}
// Close and release the socket.
if (Socket != nullptr)
{
Socket->Close();
LSI->Subsys->DestroySocket(Socket);
Socket = nullptr;
}
// Notify luprex that the channel has been closed.
if (ChannelID > 0)
{
2023-07-03 15:28:14 -04:00
if (LSI->Luprex->engine)
{
LSI->Luprex->play_notify_close(LSI->Luprex, ChannelID, err.size(), err.data());
}
ChannelID = -1;
2023-06-26 17:28:07 -04:00
}
2023-07-03 15:28:14 -04:00
RecvBIO = nullptr;
SendBIO = nullptr;
RecvFail = false;
SendFail = false;
2023-06-26 17:28:07 -04:00
NBytes = 0;
Bytes = nullptr;
2023-07-03 15:28:14 -04:00
RetryWriteNBytes = 0;
ChannelID = -1;
State = CHAN_INACTIVE;
2023-06-26 17:28:07 -04:00
}
2023-07-03 15:28:14 -04:00
#pragma optimize("", off)
2023-06-26 17:28:07 -04:00
void FLpxChannel::TransferSocketToRecvBIO() {
2023-07-03 15:28:14 -04:00
if ((State == CHAN_INACTIVE) || RecvFail) {
2023-06-26 17:28:07 -04:00
return;
}
2023-07-03 15:28:14 -04:00
2023-06-26 17:28:07 -04:00
int32 bytesread;
bool ok = Socket->Recv((uint8*)LSI->ChBuf, DRV_SHORTSTRING_SIZE, bytesread);
if (!ok)
{
2023-07-03 15:28:14 -04:00
RecvFail = true;
2023-06-26 17:28:07 -04:00
return;
}
if (bytesread > 0)
{
int nstored = BIO_write(RecvBIO, LSI->ChBuf, bytesread);
check(nstored == bytesread);
}
}
void FLpxChannel::TransferSendBIOToSocket() {
2023-07-03 15:28:14 -04:00
if ((State == CHAN_INACTIVE) || SendFail) {
2023-06-26 17:28:07 -04:00
return;
}
char* data;
int ndata = BIO_get_mem_data(SendBIO, &data);
if (ndata > DRV_SHORTSTRING_SIZE) ndata = DRV_SHORTSTRING_SIZE;
2023-07-03 15:28:14 -04:00
if (ndata == 0) return;
2023-06-26 17:28:07 -04:00
int32 bytessent;
bool ok = Socket->Send((const uint8*)data, ndata, bytessent);
if (!ok)
{
2023-07-03 15:28:14 -04:00
SendFail = true;
2023-06-26 17:28:07 -04:00
return;
}
if (bytessent > 0)
{
BIODiscard(SendBIO, bytessent, LSI->ChBuf);
}
}
void FLpxChannel::CloseChannelIfSSLErrorIsSerious(int retval) {
int error = SSL_get_error(SSLState, retval);
// Should never have write errors, because we're
// using a memory BIO with unlimited capacity.
check(error != SSL_ERROR_WANT_WRITE);
// If we get a read error, make sure it's plausible:
// if the recv bio is full, that makes no sense.
if (error == SSL_ERROR_WANT_READ) {
if (BIOSpace(RecvBIO) == 0) {
Close("ssl waiting for data, but there's tons of data");
}
return;
}
2023-07-03 15:28:14 -04:00
// Any other error is an actual error. Close the channel.
std::string errstr = SSLFullErrorString();
2023-06-26 17:28:07 -04:00
if (errstr == "") errstr = "unknown error";
Close(errstr);
}
2023-07-03 15:28:14 -04:00
void FLpxChannel::AdvanceConnecting()
{
int retval = SSL_connect(SSLState);
if (retval == 1)
{
State = CHAN_SSL_READWRITE;
}
else
{
CloseChannelIfSSLErrorIsSerious(retval);
}
}
#pragma optimize("", off)
void FLpxChannel::AdvanceAccepting()
{
int retval = SSL_accept(SSLState);
if (retval == 1)
{
State = CHAN_SSL_READWRITE;
}
else
{
CloseChannelIfSSLErrorIsSerious( retval);
}
LSI->LogTrace();
2023-07-03 15:28:14 -04:00
}
void FLpxChannel::AdvanceReadWrite()
{
// Read as much as we can, which of course will be limited
// by the fact that the recv_bio contains finite data.
while (true)
{
int read_result = SSL_read(SSLState, LSI->ChBuf, DRV_SHORTSTRING_SIZE);
if (read_result > 0)
{
LSI->Luprex->play_recv_incoming(LSI->Luprex, ChannelID, read_result, LSI->ChBuf);
}
else
{
CloseChannelIfSSLErrorIsSerious(read_result);
break;
}
}
// The read process could have generated an error which could
// have closed the channel. If so, don't try writing.
if (State == CHAN_INACTIVE)
{
return;
}
// Try to write data.
while (NBytes)
{
uint32_t wbytes;
if (RetryWriteNBytes > 0)
{
wbytes = RetryWriteNBytes;
check(wbytes < NBytes);
}
else
{
wbytes = NBytes;
if (wbytes > DRV_SHORTSTRING_SIZE) wbytes = DRV_SHORTSTRING_SIZE;
}
if (wbytes == 0) break;
int write_result = SSL_write(SSLState, Bytes, wbytes);
if (write_result > 0)
{
LSI->Luprex->play_sent_outgoing(LSI->Luprex, ChannelID, write_result);
RetryWriteNBytes = 0;
NBytes -= write_result;
Bytes += write_result;
}
else
{
CloseChannelIfSSLErrorIsSerious(write_result);
RetryWriteNBytes = wbytes;
break;
}
}
}
#pragma optimize("", off)
void FLpxChannel::Advance()
{
check(State != CHAN_INACTIVE);
// Pump from the socket into the RECV BIO.
TransferSocketToRecvBIO();
// Get a pointer to the Luprex outgoing bytes.
LSI->Luprex->get_outgoing(LSI->Luprex, ChannelID, &NBytes, &Bytes);
// If all outgoing buffers are empty, and Luprex has released
// the channel, close the channel.
if (NBytes == 0) {
if (LSI->Luprex->get_channel_released(LSI->Luprex, ChannelID)) {
if (BIO_pending(SendBIO) == 0) {
Close("");
return;
}
}
}
SSLClearErrors();
switch (State) {
case CHAN_SSL_CONNECTING:
AdvanceConnecting();
break;
case CHAN_SSL_ACCEPTING:
AdvanceAccepting();
break;
case CHAN_SSL_READWRITE:
AdvanceReadWrite();
break;
default:
2023-10-16 15:08:11 -04:00
checkf(false, TEXT("EChanState is invalid"));
2023-07-03 15:28:14 -04:00
break;
}
// Pump from the Send BIO to the socket.
TransferSendBIOToSocket();
if (RecvFail || SendFail)
{
Close("Connection aborted");
}
}
/////////////////////////////////////////////////////////////////
//
// Listener Methods
//
/////////////////////////////////////////////////////////////////
2023-09-15 13:28:18 -04:00
FLpxListener::FLpxListener(FlxSocketsI *lsi, int bp, FSocket *sock)
2023-06-26 17:28:07 -04:00
{
LSI = lsi;
BoundPort = bp;
Socket = sock;
}
FLpxListener::~FLpxListener()
{
if (Socket != nullptr)
{
Socket->Close();
LSI->Subsys->DestroySocket(Socket);
Socket = nullptr;
}
}
2023-07-03 15:28:14 -04:00
#pragma optimize("", off)
void FLpxListener::AcceptConnection()
{
FSocket* csocket = Socket->Accept(TEXT("Incoming Connection"));
if (csocket == nullptr)
{
return;
}
csocket->SetNonBlocking(true);
int ChannelID = LSI->Luprex->play_notify_accept(LSI->Luprex, BoundPort);
LSI->Channels.Emplace(LSI, csocket, ChannelID, LSI->ServerCTX, CHAN_SSL_ACCEPTING);
}
/////////////////////////////////////////////////////////////////
//
// The Socket System Implementation
//
/////////////////////////////////////////////////////////////////
2023-09-15 13:28:18 -04:00
void FlxSocketsI::SetError(const std::string& s)
2023-06-26 17:28:07 -04:00
{
if (FatalError.empty()) {
FatalError = s;
}
}
2023-07-03 15:28:14 -04:00
2023-09-15 13:28:18 -04:00
FlxSocketsI::FlxSocketsI(FlxLockedWrapper &w)
2023-06-23 16:27:23 -04:00
{
2023-09-03 02:01:32 -04:00
// We retain this pointer only so long as we have the wrapper lock.
Luprex = w.Get();
2023-07-03 15:28:14 -04:00
// This function is nonreentrant. It's not clear whether
// this is needed - it may be initialized elsewhere in unreal.
// It is also not clear that it's safe to do this in the
// blueprint thread (this constructor runs in the blueprint
// thread).
SSL_library_init();
2023-06-23 16:27:23 -04:00
ServerCTX = nullptr;
ClientSecureCTX = nullptr;
ClientInsecureCTX = nullptr;
2023-06-26 17:28:07 -04:00
Subsys = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
if (Subsys == nullptr)
{
SetError("Cannot obtain the socket subsystem");
}
2023-07-03 15:28:14 -04:00
TraceBIO = BIO_new(BIO_s_mem());
ServerCTX = SSLNewContext(SSL_VERIFY_NONE, TLS_server_method(), TraceBIO);
ClientSecureCTX = SSLNewContext(SSL_VERIFY_PEER, TLS_client_method(), TraceBIO);
ClientInsecureCTX = SSLNewContext(SSL_VERIFY_NONE, TLS_client_method(), TraceBIO);
2023-06-26 17:28:07 -04:00
SetError(SSLLoadCertificateAuthorities(ClientSecureCTX));
SetError(SSLLoadDummyCert(ServerCTX));
2023-07-03 15:28:14 -04:00
STACK_OF(SSL_CIPHER)* ciphers = SSL_CTX_get_ciphers(ServerCTX);
std::string names = "";
for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); i++)
{
const SSL_CIPHER* sslc = sk_SSL_CIPHER_value(ciphers, i);
const char* name = SSL_CIPHER_get_name(sslc);
names = names + " " + name;
}
2023-06-26 17:28:07 -04:00
HandleListenPorts();
2023-09-03 02:01:32 -04:00
// We're losing the wrapper lock, so set the pointer to nullptr.
Luprex = nullptr;
}
2023-09-15 13:28:18 -04:00
void FlxSocketsI::ForceCloseEverything(FlxLockedWrapper& w)
2023-09-03 02:01:32 -04:00
{
// We retain this pointer only so long as we have the wrapper lock.
Luprex = w.Get();
// Close all channels
for (FLpxChannel& chan : Channels) {
chan.Close("Force Close Everything");
}
// Delete any channels released by the above.
RemoveInactiveChannels();
// All channels should be gone now.
check(Channels.IsEmpty());
// We're losing the wrapper lock, so set the pointer to nullptr.
Luprex = nullptr;
2023-06-23 16:27:23 -04:00
}
2023-09-15 13:28:18 -04:00
FlxSocketsI::~FlxSocketsI()
2023-06-23 16:27:23 -04:00
{
2023-09-03 02:01:32 -04:00
checkf(Channels.IsEmpty(), TEXT("Must call ForceCloseEverything before destructor"));
2023-06-26 17:28:07 -04:00
if (ServerCTX != nullptr)
{
SSL_CTX_free(ServerCTX);
2023-09-03 02:01:32 -04:00
ServerCTX = nullptr;
2023-06-26 17:28:07 -04:00
}
if (ClientSecureCTX != nullptr)
{
SSL_CTX_free(ClientSecureCTX);
2023-09-03 02:01:32 -04:00
ClientSecureCTX = nullptr;
2023-06-26 17:28:07 -04:00
}
if (ClientInsecureCTX != nullptr)
{
SSL_CTX_free(ClientInsecureCTX);
2023-09-03 02:01:32 -04:00
ClientInsecureCTX = nullptr;
2023-06-26 17:28:07 -04:00
}
2023-09-03 02:01:32 -04:00
// TODO: Be more thorough.
2023-06-26 17:28:07 -04:00
}
void FlxSocketsI::LogTrace()
2023-07-03 15:28:14 -04:00
{
char* data;
int ndata = BIO_get_mem_data(TraceBIO, &data);
if (ndata == 0) return;
FString text(ndata, (const UTF8CHAR *)data);
UE_LOG(LogLuprexIntegration, Verbose, TEXT("SSL Trace: %s"), *text);
2023-07-03 15:28:14 -04:00
BIO_reset(TraceBIO);
}
2023-09-15 13:28:18 -04:00
bool FlxSocketsI::ListeningOnPort(int p)
2023-06-26 17:28:07 -04:00
{
for (const FLpxListener& l : Listeners)
{
if (l.BoundPort == p) return true;
}
return false;
}
2023-09-15 13:28:18 -04:00
void FlxSocketsI::HandleListenPorts()
2023-06-26 17:28:07 -04:00
{
uint32_t nports; const uint32_t* ports;
Luprex->get_listen_ports(Luprex, &nports, &ports);
for (uint32_t i = 0; i < nports; i++) {
int port = ports[i];
if (!ListeningOnPort(port))
{
2023-07-03 15:28:14 -04:00
std::string err;
FSocket* sock = ListenOnPort(Subsys, port, err);
if (sock == nullptr)
{
SetError(err);
}
else
{
Listeners.Emplace(this, port, sock);
}
2023-06-26 17:28:07 -04:00
}
}
}
2023-09-15 13:28:18 -04:00
void FlxSocketsI::HandleNewOutgoingSockets()
2023-06-26 17:28:07 -04:00
{
uint32_t nchids; const uint32_t* chids;
Luprex->get_new_outgoing(Luprex, &nchids, &chids);
for (uint32_t i = 0; i < nchids; i++) {
uint32_t chid = chids[i];
std::string err, cert, host, port;
const char* target = Luprex->get_target(Luprex, chid);
drvutil::split_target(target, cert, host, port);
if (cert.empty() || host.empty() || port.empty()) {
std::string message = "invalid target: ";
message += target;
Luprex->play_notify_close(Luprex, chid, message.size(), message.c_str());
continue;
}
SSL_CTX* ctx = nullptr;
if (cert == "cert") {
ctx = ClientSecureCTX;
}
else if (cert == "nocert") {
ctx = ClientInsecureCTX;
}
else {
std::string message = "invalid cert rule: ";
message += target;
Luprex->play_notify_close(Luprex, chid, message.size(), message.c_str());
continue;
}
2023-07-03 15:28:14 -04:00
FSocket *sock = OpenConnection(Subsys, host, port, err);
2023-06-26 17:28:07 -04:00
if (sock == nullptr) {
Luprex->play_notify_close(Luprex, chid, err.size(), err.c_str());
continue;
}
Channels.Emplace(this, sock, chid, ctx, CHAN_SSL_CONNECTING);
}
Luprex->play_clear_new_outgoing(Luprex);
}
2023-09-15 13:28:18 -04:00
void FlxSocketsI::RemoveInactiveChannels()
2023-07-03 15:28:14 -04:00
{
int i = 0;
int n = Channels.Num();
while (true)
{
while ((n > 0) && (Channels[n - 1].State == CHAN_INACTIVE)) {
n -= 1;
}
while ((i < n) && (Channels[i].State != CHAN_INACTIVE)) {
i += 1;
}
if (i >= n) break;
check(i < (n - 1));
std::swap(Channels[i], Channels[n - 1]);
i += 1;
n -= 1;
}
Channels.SetNum(n);
}
2023-06-26 17:28:07 -04:00
2023-09-15 13:28:18 -04:00
void FlxSocketsI::HandleSocketInputOutput()
2023-06-26 17:28:07 -04:00
{
2023-07-03 15:28:14 -04:00
for (FLpxListener& listener : Listeners)
{
listener.AcceptConnection();
}
// Peek output buffers and determine channel release flags.
for (FLpxChannel& chan : Channels)
{
chan.Advance();
}
2023-06-26 17:28:07 -04:00
2023-07-03 15:28:14 -04:00
// Delete any channels released by the above.
RemoveInactiveChannels();
2023-06-23 16:27:23 -04:00
}
2023-09-15 13:28:18 -04:00
void FlxSocketsI::Update(FlxLockedWrapper &w)
2023-06-23 16:27:23 -04:00
{
2023-09-03 02:01:32 -04:00
// We retain this pointer only so long as we have the wrapper lock.
Luprex = w.Get();
2023-06-26 17:28:07 -04:00
HandleNewOutgoingSockets();
HandleSocketInputOutput();
2023-09-03 02:01:32 -04:00
// We're losing the wrapper lock, so set the pointer to nullptr.
Luprex = nullptr;
2023-06-23 16:27:23 -04:00
}
2023-09-15 13:28:18 -04:00
FlxSockets* FlxSockets::Create(FlxLockedWrapper &w)
2023-06-23 16:27:23 -04:00
{
2023-09-15 13:28:18 -04:00
return new FlxSocketsI(w);
2023-06-23 16:27:23 -04:00
}