Implemented a lot of LuprexSockets
This commit is contained in:
@@ -6,16 +6,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#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()
|
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
||||||
{
|
{
|
||||||
@@ -180,7 +170,7 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
}
|
}
|
||||||
std::string_view srcpakv = srcpak.view();
|
std::string_view srcpakv = srcpak.view();
|
||||||
char* argv[1];
|
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(), "");
|
Luprex.play_initialize(&Luprex, 1, argv, srcpakv.size(), srcpakv.data(), "");
|
||||||
if (Luprex.error[0])
|
if (Luprex.error[0])
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
|
|
||||||
#include "LuprexSockets.hpp"
|
#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
|
#define UI UI_ST
|
||||||
THIRD_PARTY_INCLUDES_START
|
THIRD_PARTY_INCLUDES_START
|
||||||
@@ -13,7 +19,14 @@ THIRD_PARTY_INCLUDES_START
|
|||||||
#include <openssl/conf.h>
|
#include <openssl/conf.h>
|
||||||
THIRD_PARTY_INCLUDES_END
|
THIRD_PARTY_INCLUDES_END
|
||||||
#undef UI
|
#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 {
|
enum EChanState {
|
||||||
CHAN_INACTIVE,
|
CHAN_INACTIVE,
|
||||||
@@ -23,9 +36,25 @@ enum EChanState {
|
|||||||
CHAN_SSL_READWRITE,
|
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.
|
// A communication socket.
|
||||||
class FLpxChannel
|
class FLpxChannel
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
FLpxSocketsI* LSI;
|
||||||
int ChannelID;
|
int ChannelID;
|
||||||
FSocket* Socket;
|
FSocket* Socket;
|
||||||
SSL* SSLState;
|
SSL* SSLState;
|
||||||
@@ -49,25 +78,55 @@ class FLpxChannel
|
|||||||
EChanState State;
|
EChanState State;
|
||||||
uint32_t NBytes;
|
uint32_t NBytes;
|
||||||
const char* Bytes;
|
const char* Bytes;
|
||||||
};
|
|
||||||
|
|
||||||
// A port-listening socket.
|
void Close(std::string_view error);
|
||||||
class FLpxListener
|
|
||||||
{
|
// Copy data from the socket into the recv bio.
|
||||||
int BoundPort;
|
//
|
||||||
FSocket* Socket;
|
// 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
|
class FLpxSocketsI : public FLpxSockets
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// Fatal error status.
|
||||||
|
std::string FatalError;
|
||||||
|
|
||||||
// We don't own the wrapper, we just have a pointer to it.
|
// We don't own the wrapper, we just have a pointer to it.
|
||||||
// We require a guarantee that it outlives us.
|
// We require a guarantee that it outlives us.
|
||||||
EngineWrapper* Luprex;
|
EngineWrapper* Luprex;
|
||||||
|
|
||||||
TArray<FLpxChannel> Channels;
|
// A general-purpose character buffer.
|
||||||
TArray<FLpxListener> Listeners;
|
char ChBuf[DRV_SHORTSTRING_SIZE];
|
||||||
|
|
||||||
|
TArray<FLpxChannel> Channels;
|
||||||
|
TArray<FLpxListener> Listeners;
|
||||||
|
|
||||||
|
// Pointer to the socket subsystem.
|
||||||
|
ISocketSubsystem* Subsys;
|
||||||
|
|
||||||
SSL_CTX* ServerCTX;
|
SSL_CTX* ServerCTX;
|
||||||
SSL_CTX* ClientSecureCTX;
|
SSL_CTX* ClientSecureCTX;
|
||||||
@@ -76,23 +135,510 @@ public:
|
|||||||
FLpxSocketsI(EngineWrapper* w);
|
FLpxSocketsI(EngineWrapper* w);
|
||||||
virtual ~FLpxSocketsI() override;
|
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;
|
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)
|
FLpxSocketsI::FLpxSocketsI(EngineWrapper *w)
|
||||||
{
|
{
|
||||||
Luprex = w;
|
Luprex = w;
|
||||||
ServerCTX = nullptr;
|
ServerCTX = nullptr;
|
||||||
ClientSecureCTX = nullptr;
|
ClientSecureCTX = nullptr;
|
||||||
ClientInsecureCTX = 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()
|
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()
|
void FLpxSocketsI::Update()
|
||||||
{
|
{
|
||||||
|
HandleNewOutgoingSockets();
|
||||||
|
HandleSocketInputOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
FLpxSockets* FLpxSockets::Create(EngineWrapper* w)
|
FLpxSockets* FLpxSockets::Create(EngineWrapper* w)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// Class FLpxSockets
|
// Class FLpxSockets
|
||||||
//
|
//
|
||||||
@@ -37,6 +38,9 @@ public:
|
|||||||
// Delete the luprex socket system. Cleanly closes all sockets.
|
// Delete the luprex socket system. Cleanly closes all sockets.
|
||||||
virtual ~FLpxSockets() {}
|
virtual ~FLpxSockets() {}
|
||||||
|
|
||||||
|
// Obtain any error status report.
|
||||||
|
virtual std::string GetError() = 0;
|
||||||
|
|
||||||
// The update routine relays data into and out of
|
// The update routine relays data into and out of
|
||||||
// luprex via TCP/IP sockets.
|
// luprex via TCP/IP sockets.
|
||||||
virtual void Update() = 0;
|
virtual void Update() = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user