#include "net/connection/WowConnection.hpp" #include "net/connection/WowConnectionNet.hpp" #include "net/connection/WowConnectionResponse.hpp" #include #include #include #include #include #include #include #if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) #include #include #include #include #include #include #include #endif #if defined(WHOA_SYSTEM_WIN) #include #include #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(&addr), &addrLen); if (sock < 0) { break; } if (WowConnection::s_verifyAddr) { NETADDR verifyAddr; socklen_t verifyAddrLen = sizeof(verifyAddr); getpeername(sock, reinterpret_cast(&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(&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(&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(&this->m_peer.peerAddr), &peerLen); socklen_t selfLen = sizeof(this->m_peer.selfAddr); getsockname(this->m_sock, reinterpret_cast(&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(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(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(&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 }