mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-12-13 03:32:28 +00:00
613 lines
14 KiB
C++
613 lines
14 KiB
C++
#include "net/connection/WowConnection.hpp"
|
|
#include "net/connection/WowConnectionNet.hpp"
|
|
#include "net/connection/WowConnectionResponse.hpp"
|
|
#include <common/Time.hpp>
|
|
#include <storm/Error.hpp>
|
|
#include <storm/Memory.hpp>
|
|
#include <storm/String.hpp>
|
|
#include <storm/Thread.hpp>
|
|
#include <algorithm>
|
|
#include <new>
|
|
|
|
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
|
|
uint64_t WowConnection::s_countTotalBytes;
|
|
int32_t WowConnection::s_destroyed;
|
|
WowConnectionNet* WowConnection::s_network;
|
|
ATOMIC32 WowConnection::s_numWowConnections;
|
|
bool (*WowConnection::s_verifyAddr)(const NETADDR*);
|
|
|
|
int32_t WowConnection::CreateSocket() {
|
|
int32_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
// TODO
|
|
|
|
return sock;
|
|
}
|
|
|
|
int32_t WowConnection::InitOsNet(bool (*fcn)(const NETADDR*), void (*threadinit)(), int32_t numThreads, bool useEngine) {
|
|
if (!WowConnection::s_network) {
|
|
// TODO s_usedSocketBits logic
|
|
// TODO WDataStore::StaticInitialize();
|
|
|
|
WowConnection::s_verifyAddr = fcn;
|
|
WowConnection::s_destroyed = 0;
|
|
|
|
numThreads = std::min(numThreads, 32);
|
|
|
|
auto networkMem = SMemAlloc(sizeof(WowConnectionNet), __FILE__, __LINE__, 0x0);
|
|
auto network = new (networkMem) WowConnectionNet(numThreads, threadinit);
|
|
|
|
WowConnection::s_network = network;
|
|
WowConnection::s_network->PlatformInit(useEngine);
|
|
WowConnection::s_network->Start();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
WowConnection::WowConnection(WowConnectionResponse* response, void (*func)(void)) {
|
|
// TODO
|
|
|
|
this->Init(response, func);
|
|
|
|
this->m_sock = -1;
|
|
}
|
|
|
|
WowConnection::WowConnection(int32_t sock, sockaddr_in* addr, WowConnectionResponse* response) {
|
|
// TODO
|
|
|
|
this->Init(response, nullptr);
|
|
|
|
// TODO
|
|
|
|
this->m_sock = sock;
|
|
this->m_connState = WOWC_CONNECTED;
|
|
}
|
|
|
|
void WowConnection::AcquireResponseRef() {
|
|
this->m_responseLock.Enter();
|
|
|
|
STORM_ASSERT(this->m_responseRef == 0 || this->GetState() == WOWC_LISTENING);
|
|
|
|
this->m_responseRef++;
|
|
this->m_responseRefThread = SGetCurrentThreadId();
|
|
|
|
this->m_responseLock.Leave();
|
|
}
|
|
|
|
void WowConnection::AddRef() {
|
|
SInterlockedIncrement(&this->m_refCount);
|
|
}
|
|
|
|
void WowConnection::CheckAccept() {
|
|
for (int32_t i = 0; i < 10000; i++) {
|
|
NETADDR addr;
|
|
socklen_t addrLen = sizeof(addr);
|
|
|
|
int32_t sock = accept(this->m_sock, reinterpret_cast<sockaddr*>(&addr), &addrLen);
|
|
if (sock < 0) {
|
|
break;
|
|
}
|
|
|
|
if (WowConnection::s_verifyAddr) {
|
|
NETADDR verifyAddr;
|
|
socklen_t verifyAddrLen = sizeof(verifyAddr);
|
|
|
|
getpeername(sock, reinterpret_cast<sockaddr*>(&verifyAddr), &verifyAddrLen);
|
|
if (!WowConnection::s_verifyAddr(&verifyAddr)) {
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
closesocket(sock);
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
close(sock);
|
|
#endif
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// TODO
|
|
// RegisterSocket(sock);
|
|
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
u_long mode = 1;
|
|
ioctlsocket(sock, FIONBIO, &mode);
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
|
|
auto connMem = SMemAlloc(sizeof(WowConnection), __FILE__, __LINE__, 0x0);
|
|
auto conn = new (connMem) WowConnection(sock, reinterpret_cast<sockaddr_in*>(&addr), this->m_response);
|
|
conn->AddRef();
|
|
|
|
this->AddRef();
|
|
this->AcquireResponseRef();
|
|
|
|
this->m_lock.Leave();
|
|
|
|
if (this->m_response) {
|
|
this->m_response->WCConnected(this, conn, OsGetAsyncTimeMs(), &conn->m_peer);
|
|
}
|
|
|
|
WowConnection::s_network->Add(conn);
|
|
WowConnection::s_network->PlatformChangeState(conn, conn->GetState());
|
|
|
|
conn->Release();
|
|
|
|
this->m_lock.Enter();
|
|
|
|
this->ReleaseResponseRef();
|
|
|
|
this->Release();
|
|
}
|
|
}
|
|
|
|
void WowConnection::CheckConnect() {
|
|
int32_t err;
|
|
socklen_t errLen = sizeof(err);
|
|
if (getsockopt(this->m_sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&err), &errLen)) {
|
|
return;
|
|
}
|
|
|
|
if (err) {
|
|
WowConnection::s_network->Remove(this);
|
|
|
|
WowConnection::CloseSocket(this->m_sock);
|
|
this->m_sock = -1;
|
|
|
|
this->SetState(WOWC_DISCONNECTED);
|
|
this->AddRef();
|
|
this->AcquireResponseRef();
|
|
|
|
this->m_lock.Leave();
|
|
|
|
if (this->m_response) {
|
|
this->m_response->WCCantConnect(this, OsGetAsyncTimeMsPrecise(), &this->m_peer);
|
|
}
|
|
} else {
|
|
this->SetState(WOWC_CONNECTED);
|
|
this->AddRef();
|
|
this->AcquireResponseRef();
|
|
|
|
this->m_lock.Leave();
|
|
|
|
socklen_t peerLen = sizeof(this->m_peer.peerAddr);
|
|
getpeername(this->m_sock, reinterpret_cast<sockaddr*>(&this->m_peer.peerAddr), &peerLen);
|
|
|
|
socklen_t selfLen = sizeof(this->m_peer.selfAddr);
|
|
getsockname(this->m_sock, reinterpret_cast<sockaddr*>(&this->m_peer.selfAddr), &selfLen);
|
|
|
|
if (this->m_response) {
|
|
this->m_response->WCConnected(this, nullptr, OsGetAsyncTimeMsPrecise(), &this->m_peer);
|
|
}
|
|
}
|
|
|
|
this->m_lock.Enter();
|
|
|
|
this->ReleaseResponseRef();
|
|
this->Release();
|
|
}
|
|
|
|
void WowConnection::CloseSocket(int32_t sock) {
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
closesocket(sock);
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
close(sock);
|
|
#endif
|
|
|
|
if (sock >= 0) {
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
bool WowConnection::Connect(char const* address, int32_t retryMs) {
|
|
char host[256];
|
|
auto port = SStrChr(address, ':');
|
|
|
|
if (port) {
|
|
this->m_connectPort = SStrToInt(port + 1);
|
|
|
|
size_t portIndex = port - address + 1;
|
|
portIndex = std::min(portIndex, sizeof(host));
|
|
SStrCopy(host, address, portIndex);
|
|
} else {
|
|
this->m_connectPort = 0;
|
|
|
|
SStrCopy(host, address, sizeof(host));
|
|
}
|
|
|
|
this->Connect(host, this->m_connectPort, retryMs);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WowConnection::Connect(char const* address, uint16_t port, int32_t retryMs) {
|
|
auto connectAddress = inet_addr(address);
|
|
|
|
if (connectAddress == -1 || connectAddress == 0) {
|
|
auto entry = gethostbyname(address);
|
|
if (entry) {
|
|
auto addrs = reinterpret_cast<uint8_t**>(entry->h_addr_list);
|
|
auto addr0 = addrs[0];
|
|
this->m_connectAddress = addr0[0]
|
|
| addr0[1] << 8
|
|
| addr0[2] << 16
|
|
| addr0[3] << 24;
|
|
} else {
|
|
this->m_connectAddress = 0;
|
|
}
|
|
} else {
|
|
this->m_connectAddress = connectAddress;
|
|
}
|
|
|
|
this->m_connectPort = port;
|
|
|
|
this->StartConnect();
|
|
|
|
return true;
|
|
}
|
|
|
|
void WowConnection::Disconnect() {
|
|
this->m_lock.Enter();
|
|
|
|
if (this->m_sock >= 0 && this->GetState() == WOWC_CONNECTED) {
|
|
this->m_connState = WOWC_DISCONNECTING;
|
|
|
|
if (WowConnection::s_network) {
|
|
WowConnection::s_network->PlatformChangeState(this, WOWC_CONNECTED);
|
|
}
|
|
}
|
|
|
|
this->m_lock.Leave();
|
|
}
|
|
|
|
void WowConnection::DoDisconnect() {
|
|
this->m_lock.Enter();
|
|
|
|
if (this->m_sock >= 0) {
|
|
WowConnection::s_network->Remove(this);
|
|
this->CloseSocket(this->m_sock);
|
|
}
|
|
|
|
this->SetState(WOWC_DISCONNECTED);
|
|
|
|
this->AddRef();
|
|
this->AcquireResponseRef();
|
|
|
|
this->m_lock.Leave();
|
|
|
|
if (this->m_response && this->m_sock >= 0) {
|
|
// TODO
|
|
// this->m_response->Vfunc4(this, OsGetAsyncTimeMsPrecise());
|
|
}
|
|
|
|
this->m_lock.Enter();
|
|
|
|
this->m_sock = -1;
|
|
this->ReleaseResponseRef();
|
|
|
|
this->m_lock.Leave();
|
|
|
|
this->Release();
|
|
}
|
|
|
|
void WowConnection::DoExceptions() {
|
|
this->AddRef();
|
|
this->m_lock.Enter();
|
|
|
|
if (this->GetState() == WOWC_CONNECTING) {
|
|
this->CheckConnect();
|
|
}
|
|
|
|
this->m_lock.Leave();
|
|
this->Release();
|
|
}
|
|
|
|
void WowConnection::DoMessageReads() {
|
|
// TODO
|
|
}
|
|
|
|
void WowConnection::DoReads() {
|
|
this->AddRef();
|
|
|
|
this->m_lock.Enter();
|
|
|
|
if (this->m_connState == WOWC_LISTENING) {
|
|
this->CheckAccept();
|
|
} else if (this->m_connState == WOWC_CONNECTED) {
|
|
if (this->m_type == WOWC_TYPE_STREAM) {
|
|
this->DoStreamReads();
|
|
} else {
|
|
this->DoMessageReads();
|
|
}
|
|
}
|
|
|
|
this->m_lock.Leave();
|
|
|
|
this->Release();
|
|
}
|
|
|
|
void WowConnection::DoStreamReads() {
|
|
uint32_t startTime = OsGetAsyncTimeMsPrecise();
|
|
uint8_t buf[4096];
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
int32_t bytesRead;
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
ssize_t bytesRead;
|
|
#endif
|
|
|
|
while (1) {
|
|
while (1) {
|
|
bytesRead = recv(this->m_sock, reinterpret_cast<char*>(buf), sizeof(buf), 0);
|
|
|
|
if (bytesRead >= 0) {
|
|
break;
|
|
}
|
|
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
if (WSAGetLastError() != WSAEINTR) {
|
|
break;
|
|
}
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
if (errno != EINTR) {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (bytesRead <= 0) {
|
|
break;
|
|
}
|
|
|
|
this->AcquireResponseRef();
|
|
this->m_lock.Leave();
|
|
|
|
if (this->m_response) {
|
|
this->m_response->WCDataReady(this, OsGetAsyncTimeMs(), buf, bytesRead);
|
|
}
|
|
|
|
this->m_lock.Enter();
|
|
this->ReleaseResponseRef();
|
|
|
|
if (this->GetState() == WOWC_DISCONNECTING || (OsGetAsyncTimeMsPrecise() - startTime) >= 5) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool shouldDisconnect = false;
|
|
#if defined(WHOA_SYSTEM_WIN)
|
|
shouldDisconnect = bytesRead >= 0 || WSAGetLastError() != WSAEWOULDBLOCK;
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
shouldDisconnect = bytesRead >= 0 || errno != EAGAIN;
|
|
#endif
|
|
|
|
if (shouldDisconnect) {
|
|
this->AcquireResponseRef();
|
|
|
|
WowConnection::s_network->Remove(this);
|
|
this->CloseSocket(this->m_sock);
|
|
this->SetState(WOWC_DISCONNECTED);
|
|
|
|
this->m_lock.Leave();
|
|
|
|
if (this->m_response && this->m_sock >= 0) {
|
|
this->m_response->WCDisconnected(this, OsGetAsyncTimeMs(), &this->m_peer);
|
|
}
|
|
|
|
this->m_lock.Enter();
|
|
|
|
this->m_sock = -1;
|
|
|
|
this->ReleaseResponseRef();
|
|
}
|
|
}
|
|
|
|
void WowConnection::DoWrites() {
|
|
this->AddRef();
|
|
|
|
this->m_lock.Enter();
|
|
|
|
if (this->m_connState == WOWC_CONNECTING) {
|
|
this->CheckConnect();
|
|
} else {
|
|
// TODO
|
|
}
|
|
|
|
// TODO
|
|
|
|
this->m_lock.Leave();
|
|
|
|
this->Release();
|
|
}
|
|
|
|
WOW_CONN_STATE WowConnection::GetState() {
|
|
return this->m_connState;
|
|
}
|
|
|
|
void WowConnection::Init(WowConnectionResponse* response, void (*func)(void)) {
|
|
SInterlockedIncrement(&WowConnection::s_numWowConnections);
|
|
|
|
this->m_refCount = 1;
|
|
this->m_responseRef = 0;
|
|
|
|
// TODO
|
|
|
|
this->m_connState = WOWC_UNINITIALIZED;
|
|
|
|
// TODO
|
|
|
|
this->m_response = response;
|
|
|
|
// TODO
|
|
|
|
this->m_connectAddress = 0;
|
|
this->m_connectPort = 0;
|
|
|
|
// TODO
|
|
|
|
this->m_serviceFlags = 0x0;
|
|
this->m_serviceCount = 0;
|
|
|
|
// TODO
|
|
|
|
this->m_readBuffer = nullptr;
|
|
this->m_readBytes = 0;
|
|
this->m_readBufferSize = 0;
|
|
|
|
// TODO
|
|
|
|
this->SetState(WOWC_INITIALIZED);
|
|
this->m_type = WOWC_TYPE_MESSAGES;
|
|
}
|
|
|
|
void WowConnection::Release() {
|
|
if (SInterlockedDecrement(&this->m_refCount) <= 0) {
|
|
if (WowConnection::s_network) {
|
|
WowConnection::s_network->Delete(this);
|
|
} else {
|
|
// TODO SMemFree
|
|
delete this;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WowConnection::ReleaseResponseRef() {
|
|
this->m_responseLock.Enter();
|
|
|
|
STORM_ASSERT(this->m_responseRef > 0);
|
|
|
|
this->m_responseRef--;
|
|
|
|
// TODO
|
|
// dwordD4 = (void *)this->dwordD4;
|
|
// if (dwordD4) {
|
|
// this->m_response = dwordD4;
|
|
// this->dwordD4 = 0;
|
|
// }
|
|
|
|
this->m_responseLock.Leave();
|
|
}
|
|
|
|
WC_SEND_RESULT WowConnection::SendRaw(uint8_t* data, int32_t len, bool a4) {
|
|
WowConnection::s_countTotalBytes += len;
|
|
|
|
this->m_lock.Enter();
|
|
|
|
// TODO
|
|
|
|
if (len > 0 && this->m_connState == WOWC_CONNECTED) {
|
|
STORM_ASSERT(this->m_sock >= 0);
|
|
|
|
#if defined (WHOA_SYSTEM_WIN)
|
|
// TODO
|
|
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
if (this->m_sendList.Head()) {
|
|
// TODO
|
|
} else {
|
|
auto written = write(this->m_sock, data, len);
|
|
|
|
if (written <= 0) {
|
|
// TODO
|
|
} else if (written == len) {
|
|
this->m_lock.Leave();
|
|
return WC_SEND_SENT;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
this->m_lock.Leave();
|
|
|
|
return WC_SEND_ERROR;
|
|
}
|
|
|
|
void WowConnection::SetEncryptionType(WC_ENCRYPT_TYPE encryptType) {
|
|
// TODO
|
|
}
|
|
|
|
void WowConnection::SetState(WOW_CONN_STATE state) {
|
|
WOW_CONN_STATE oldState = this->m_connState;
|
|
this->m_connState = state;
|
|
|
|
if (WowConnection::s_network) {
|
|
WowConnection::s_network->PlatformChangeState(this, oldState);
|
|
}
|
|
}
|
|
|
|
void WowConnection::SetType(WOWC_TYPE type) {
|
|
this->m_lock.Enter();
|
|
this->m_type = type;
|
|
this->m_lock.Leave();
|
|
}
|
|
|
|
void WowConnection::StartConnect() {
|
|
if (this->m_sock >= 0) {
|
|
if (this->m_netlink.IsLinked()) {
|
|
WowConnection::s_network->Remove(this);
|
|
}
|
|
|
|
this->CloseSocket(this->m_sock);
|
|
this->m_sock = -1;
|
|
}
|
|
|
|
this->m_lock.Enter();
|
|
|
|
this->m_sock = WowConnection::CreateSocket();
|
|
|
|
if (this->m_sock < 0) {
|
|
this->SetState(WOWC_ERROR);
|
|
this->m_lock.Leave();
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined(WHOA_SYSTEM_MAC)
|
|
fcntl(this->m_sock, F_SETFL, O_NONBLOCK);
|
|
|
|
uint32_t opt = 1;
|
|
setsockopt(this->m_sock, SOL_SOCKET, 4130, &opt, sizeof(opt));
|
|
|
|
sockaddr_in addr;
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(this->m_connectPort);
|
|
addr.sin_addr.s_addr = this->m_connectAddress;
|
|
|
|
if (!this->m_netlink.IsLinked()) {
|
|
WowConnection::s_network->Add(this);
|
|
}
|
|
|
|
this->SetState(WOWC_CONNECTING);
|
|
|
|
if (connect(this->m_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) >= 0) {
|
|
this->m_lock.Leave();
|
|
|
|
return;
|
|
}
|
|
|
|
if (errno == EAGAIN || errno == EINTR || errno == EINPROGRESS) {
|
|
this->m_lock.Leave();
|
|
|
|
return;
|
|
}
|
|
|
|
WowConnection::s_network->Remove(this);
|
|
this->CloseSocket(this->m_sock);
|
|
this->m_sock = -1;
|
|
|
|
this->SetState(WOWC_ERROR);
|
|
|
|
this->m_lock.Leave();
|
|
#endif
|
|
}
|