Implemented a lot of LuprexSockets
This commit is contained in:
@@ -6,16 +6,6 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
//// Main loop.
|
||||
//while (!engw.get_stop_driver(&engw)) {
|
||||
// handle_lua_source();
|
||||
// handle_console_output();
|
||||
// handle_new_outgoing_sockets();
|
||||
// handle_socket_input_output();
|
||||
// handle_console_input();
|
||||
// handle_console_output();
|
||||
// engw.play_invoke_event_update(&engw, drvutil::get_monotonic_clock());
|
||||
//}
|
||||
|
||||
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
||||
{
|
||||
@@ -180,7 +170,7 @@ void AIntegrationGameModeBase::BeginPlay()
|
||||
}
|
||||
std::string_view srcpakv = srcpak.view();
|
||||
char* argv[1];
|
||||
argv[0] = const_cast<char*>("lpxserver");
|
||||
argv[0] = const_cast<char*>("lpxclient");
|
||||
Luprex.play_initialize(&Luprex, 1, argv, srcpakv.size(), srcpakv.data(), "");
|
||||
if (Luprex.error[0])
|
||||
{
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
|
||||
#include "LuprexSockets.hpp"
|
||||
#include "enginewrapper.hpp"
|
||||
#include "drvutil.hpp"
|
||||
#include "Sockets.h"
|
||||
#include "SocketTypes.h"
|
||||
#include "SocketSubsystem.h"
|
||||
#include "AddressInfoTypes.h"
|
||||
|
||||
#define UI UI_ST
|
||||
THIRD_PARTY_INCLUDES_START
|
||||
@@ -13,7 +19,14 @@ THIRD_PARTY_INCLUDES_START
|
||||
#include <openssl/conf.h>
|
||||
THIRD_PARTY_INCLUDES_END
|
||||
#undef UI
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#define MAX_BIO_BUFFER (128 * 1024)
|
||||
|
||||
enum EChanState {
|
||||
CHAN_INACTIVE,
|
||||
@@ -23,9 +36,25 @@ enum EChanState {
|
||||
CHAN_SSL_READWRITE,
|
||||
};
|
||||
|
||||
class FLpxSocketsI;
|
||||
|
||||
// A port-listening socket.
|
||||
class FLpxListener
|
||||
{
|
||||
public:
|
||||
FLpxSocketsI *LSI;
|
||||
int BoundPort;
|
||||
FSocket* Socket;
|
||||
|
||||
FLpxListener(FLpxSocketsI *lsi, int bp, FSocket* sock);
|
||||
~FLpxListener();
|
||||
};
|
||||
|
||||
// A communication socket.
|
||||
class FLpxChannel
|
||||
{
|
||||
public:
|
||||
FLpxSocketsI* LSI;
|
||||
int ChannelID;
|
||||
FSocket* Socket;
|
||||
SSL* SSLState;
|
||||
@@ -49,25 +78,55 @@ class FLpxChannel
|
||||
EChanState State;
|
||||
uint32_t NBytes;
|
||||
const char* Bytes;
|
||||
};
|
||||
|
||||
// A port-listening socket.
|
||||
class FLpxListener
|
||||
{
|
||||
int BoundPort;
|
||||
FSocket* Socket;
|
||||
void Close(std::string_view error);
|
||||
|
||||
// Copy data from the socket into the recv bio.
|
||||
//
|
||||
// If it detects an error or EOF, sets the RecentError flag.
|
||||
//
|
||||
void TransferSocketToRecvBIO();
|
||||
|
||||
// Copy data from the send bio to the socket.
|
||||
//
|
||||
// If it detects an error or EOF, sets the RecentError flag.
|
||||
//
|
||||
void TransferSendBIOToSocket();
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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);
|
||||
|
||||
FLpxChannel(FLpxSocketsI *lsi, FSocket* sock, int chid, SSL_CTX* ctx, EChanState state);
|
||||
~FLpxChannel() { Close(""); }
|
||||
};
|
||||
|
||||
class FLpxSocketsI : public FLpxSockets
|
||||
{
|
||||
public:
|
||||
// Fatal error status.
|
||||
std::string FatalError;
|
||||
|
||||
// We don't own the wrapper, we just have a pointer to it.
|
||||
// We require a guarantee that it outlives us.
|
||||
EngineWrapper* Luprex;
|
||||
|
||||
TArray<FLpxChannel> Channels;
|
||||
TArray<FLpxListener> Listeners;
|
||||
// A general-purpose character buffer.
|
||||
char ChBuf[DRV_SHORTSTRING_SIZE];
|
||||
|
||||
TArray<FLpxChannel> Channels;
|
||||
TArray<FLpxListener> Listeners;
|
||||
|
||||
// Pointer to the socket subsystem.
|
||||
ISocketSubsystem* Subsys;
|
||||
|
||||
SSL_CTX* ServerCTX;
|
||||
SSL_CTX* ClientSecureCTX;
|
||||
@@ -76,23 +135,510 @@ public:
|
||||
FLpxSocketsI(EngineWrapper* w);
|
||||
virtual ~FLpxSocketsI() override;
|
||||
|
||||
// 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);
|
||||
|
||||
// Open a connection and return a socket.
|
||||
FSocket* OpenConnection(const std::string& host, const std::string& port, std::string& err);
|
||||
|
||||
// Handle various phases of the operation.
|
||||
void HandleListenPorts();
|
||||
void HandleNewOutgoingSockets();
|
||||
void HandleSocketInputOutput();
|
||||
|
||||
// Main update routine.
|
||||
virtual void Update() override;
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
std::string SSLErrorString() {
|
||||
// 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 "";
|
||||
}
|
||||
}
|
||||
|
||||
static SSL_CTX* SSLNewContext(int verify) {
|
||||
SSL_CTX* ctx = SSL_CTX_new(TLS_method());
|
||||
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);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
static std::string SSLUseCertificateString(SSL_CTX* ctx, const char* str) {
|
||||
ERR_clear_error();
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, str);
|
||||
X509* certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
std::string result;
|
||||
if (SSL_CTX_use_certificate(ctx, certificate) <= 0)
|
||||
{
|
||||
result = SSLErrorString();
|
||||
}
|
||||
X509_free(certificate);
|
||||
BIO_free(bio);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SSLLoadDummyCert(SSL_CTX* ctx)
|
||||
{
|
||||
std::string err1 = SSLUseCertificateString(ctx, dummy_cert);
|
||||
std::string err2 = SSLUseCertificateString(ctx, dummy_key);
|
||||
if (!err1.empty()) return err1;
|
||||
if (!err1.empty()) return err2;
|
||||
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.
|
||||
int BIOSpace(BIO* bio) {
|
||||
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.
|
||||
void BIODiscard(BIO* b, int nbytes, char* chbuf) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
FLpxChannel::FLpxChannel(FLpxSocketsI* lsi, FSocket* sock, int chid, SSL_CTX* ctx, EChanState st)
|
||||
{
|
||||
LSI = lsi;
|
||||
ChannelID = chid;
|
||||
Socket = sock;
|
||||
RecvBIO = BIO_new(BIO_s_mem());
|
||||
SendBIO = BIO_new(BIO_s_mem());
|
||||
RecentError.clear();
|
||||
RetryWriteNBytes = 0;
|
||||
NeedAdvance = true;
|
||||
|
||||
if (st == CHAN_PLAINTEXT) {
|
||||
SSLState = nullptr;
|
||||
}
|
||||
else {
|
||||
SSLState = SSL_new(ctx);
|
||||
SSL_set_bio(SSLState, RecvBIO, SendBIO);
|
||||
}
|
||||
|
||||
State = st;
|
||||
NBytes = 0;
|
||||
Bytes = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
RecvBIO = nullptr;
|
||||
SendBIO = nullptr;
|
||||
|
||||
RecentError.clear();
|
||||
RetryWriteNBytes = 0;
|
||||
NeedAdvance = false;
|
||||
|
||||
// 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)
|
||||
{
|
||||
LSI->Luprex->play_notify_close(LSI->Luprex, ChannelID, err.size(), err.data());
|
||||
}
|
||||
|
||||
// Close everything else.
|
||||
State = CHAN_INACTIVE;
|
||||
ChannelID = -1;
|
||||
NBytes = 0;
|
||||
Bytes = nullptr;
|
||||
}
|
||||
|
||||
void FLpxChannel::TransferSocketToRecvBIO() {
|
||||
if ((State == CHAN_INACTIVE) || (!RecentError.empty())) {
|
||||
return;
|
||||
}
|
||||
int32 bytesread;
|
||||
bool ok = Socket->Recv((uint8*)LSI->ChBuf, DRV_SHORTSTRING_SIZE, bytesread);
|
||||
if (!ok)
|
||||
{
|
||||
RecentError = "EOF";
|
||||
return;
|
||||
}
|
||||
if (bytesread > 0)
|
||||
{
|
||||
int nstored = BIO_write(RecvBIO, LSI->ChBuf, bytesread);
|
||||
check(nstored == bytesread);
|
||||
NeedAdvance = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FLpxChannel::TransferSendBIOToSocket() {
|
||||
if ((State == CHAN_INACTIVE) || (!RecentError.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* data;
|
||||
int ndata = BIO_get_mem_data(SendBIO, &data);
|
||||
if (ndata > DRV_SHORTSTRING_SIZE) ndata = DRV_SHORTSTRING_SIZE;
|
||||
|
||||
// It is an error to call this function when there is nothing in the send BIO.
|
||||
check(ndata > 0);
|
||||
|
||||
int32 bytessent;
|
||||
bool ok = Socket->Send((const uint8*)data, ndata, bytessent);
|
||||
if (!ok)
|
||||
{
|
||||
RecentError = "Send failure on socket";
|
||||
return;
|
||||
}
|
||||
if (bytessent > 0)
|
||||
{
|
||||
BIODiscard(SendBIO, bytessent, LSI->ChBuf);
|
||||
NeedAdvance = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the channel if there's a serious OpenSSL error.
|
||||
//
|
||||
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;
|
||||
}
|
||||
|
||||
// Any other error is an actual error. Close
|
||||
// the channel.
|
||||
std::string errstr = SSLErrorString();
|
||||
if (errstr == "") errstr = "unknown error";
|
||||
Close(errstr);
|
||||
}
|
||||
|
||||
FLpxListener::FLpxListener(FLpxSocketsI *lsi, int bp, FSocket *sock)
|
||||
{
|
||||
LSI = lsi;
|
||||
BoundPort = bp;
|
||||
Socket = sock;
|
||||
}
|
||||
|
||||
FLpxListener::~FLpxListener()
|
||||
{
|
||||
if (Socket != nullptr)
|
||||
{
|
||||
Socket->Close();
|
||||
LSI->Subsys->DestroySocket(Socket);
|
||||
Socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FLpxSocketsI::SetError(const std::string& s)
|
||||
{
|
||||
if (FatalError.empty()) {
|
||||
FatalError = s;
|
||||
}
|
||||
}
|
||||
|
||||
FLpxSocketsI::FLpxSocketsI(EngineWrapper *w)
|
||||
{
|
||||
Luprex = w;
|
||||
ServerCTX = nullptr;
|
||||
ClientSecureCTX = nullptr;
|
||||
ClientInsecureCTX = nullptr;
|
||||
|
||||
Subsys = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
|
||||
if (Subsys == nullptr)
|
||||
{
|
||||
SetError("Cannot obtain the socket subsystem");
|
||||
}
|
||||
|
||||
ServerCTX = SSLNewContext(SSL_VERIFY_NONE);
|
||||
ClientSecureCTX = SSLNewContext(SSL_VERIFY_PEER);
|
||||
ClientInsecureCTX = SSLNewContext(SSL_VERIFY_NONE);
|
||||
|
||||
SetError(SSLLoadCertificateAuthorities(ClientSecureCTX));
|
||||
SetError(SSLLoadDummyCert(ServerCTX));
|
||||
|
||||
HandleListenPorts();
|
||||
}
|
||||
|
||||
FLpxSocketsI::~FLpxSocketsI()
|
||||
{
|
||||
if (ServerCTX != nullptr)
|
||||
{
|
||||
SSL_CTX_free(ServerCTX);
|
||||
}
|
||||
if (ClientSecureCTX != nullptr)
|
||||
{
|
||||
SSL_CTX_free(ClientSecureCTX);
|
||||
}
|
||||
if (ClientInsecureCTX != nullptr)
|
||||
{
|
||||
SSL_CTX_free(ClientInsecureCTX);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
//for (ChanInfo& chan : chans_) {
|
||||
// close_channel(chan, "");
|
||||
//}
|
||||
}
|
||||
|
||||
bool FLpxSocketsI::ListeningOnPort(int p)
|
||||
{
|
||||
for (const FLpxListener& l : Listeners)
|
||||
{
|
||||
if (l.BoundPort == p) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FLpxSocketsI::HandleListenPorts()
|
||||
{
|
||||
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))
|
||||
{
|
||||
// TODO: Open a listening socket.
|
||||
// Push the new port and socket onto Listeners.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FSocket* FLpxSocketsI::OpenConnection(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());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void FLpxSocketsI::HandleNewOutgoingSockets()
|
||||
{
|
||||
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;
|
||||
}
|
||||
FSocket *sock = OpenConnection(host, port, err);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FLpxSocketsI::HandleSocketInputOutput()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FLpxSocketsI::Update()
|
||||
{
|
||||
HandleNewOutgoingSockets();
|
||||
HandleSocketInputOutput();
|
||||
}
|
||||
|
||||
FLpxSockets* FLpxSockets::Create(EngineWrapper* w)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include <string>
|
||||
|
||||
// Class FLpxSockets
|
||||
//
|
||||
@@ -37,6 +38,9 @@ public:
|
||||
// Delete the luprex socket system. Cleanly closes all sockets.
|
||||
virtual ~FLpxSockets() {}
|
||||
|
||||
// Obtain any error status report.
|
||||
virtual std::string GetError() = 0;
|
||||
|
||||
// The update routine relays data into and out of
|
||||
// luprex via TCP/IP sockets.
|
||||
virtual void Update() = 0;
|
||||
|
||||
Reference in New Issue
Block a user