From 7dff6dfd600e346c7bbd9fbcf6064caae156c984 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Fri, 20 Jun 2025 18:19:12 -0300 Subject: finish `communication.cc` --- reference/game.c | 507 --------------------------------------------------- src/communication.cc | 398 +++++++++++++++++++++++++++++++++++++--- src/communication.hh | 14 +- src/connections.hh | 4 + src/crypto.hh | 1 + src/query.hh | 2 + src/stubs.hh | 1 + 7 files changed, 397 insertions(+), 530 deletions(-) diff --git a/reference/game.c b/reference/game.c index 71343d6..c954698 100644 --- a/reference/game.c +++ b/reference/game.c @@ -37401,513 +37401,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority return; } -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void IncrementActiveConnections(void) - -{ - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + 1; - Semaphore::up(&CommunicationThreadMutex); - return; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void DecrementActiveConnections(void) - -{ - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + -1; - Semaphore::up(&CommunicationThreadMutex); - return; -} - - - -void CommunicationThread(int Socket) - -{ - CONNECTIONSTATE CVar1; - bool bVar2; - TConnection *this; - __pid_t _Var3; - int iVar4; - int *piVar5; - TConnection *Connection; - char *Text; - int local_a0; - int Signal; - sigset_t SignalSet; - - this = AssignFreeConnection(); - if (this == (TConnection *)0x0) { - print(); - iVar4 = close(Socket); - if (-1 < iVar4) { - return; - } - piVar5 = __errno_location(); - error(&DAT_0810cf20,*piVar5); - return; - } - TConnection::Connect(this,Socket); - this->WaitingForACK = false; - _Var3 = getpid(); - iVar4 = fcntl(Socket,8,_Var3); - if (iVar4 < 0) { - error(&DAT_0810cea0,Socket); - iVar4 = close(Socket); - if (-1 < iVar4) goto LAB_080de426; - piVar5 = __errno_location(); - iVar4 = *piVar5; - Text = &DAT_0810cee0; - } - else { - iVar4 = fcntl(Socket,4,0x2800); - if (-1 < iVar4) { - sigfillset((sigset_t *)&Signal); - sigprocmask(2,(sigset_t *)&Signal,(sigset_t *)0x0); - alarm(5); - bVar2 = ReceiveCommand(this); - if (!bVar2) { - TConnection::Close(this,true); - } - this->SigIOPending = false; -switchD_080de377_caseD_0: - bVar2 = GameRunning(); - if ((!bVar2) || (this->ConnectionIsOk == false)) goto LAB_080de403; - sigwait((sigset_t *)&Signal,&local_a0); - switch(local_a0) { - default: - goto switchD_080de377_caseD_0; - case 1: - case 0xd: - break; - case 10: - if (this->SigIOPending != false) goto switchD_080de377_caseD_1d; - goto switchD_080de377_caseD_0; - case 0xc: - bVar2 = SendData(this); - if (bVar2) goto switchD_080de377_caseD_0; - break; - case 0xe: - if (this->State != CONNECTION_CONNECTED) goto switchD_080de377_caseD_0; - print(); - break; - case 0x1d: -switchD_080de377_caseD_1d: - if (this->WaitingForACK == false) { - this->SigIOPending = false; - bVar2 = ReceiveCommand(this); - if (!bVar2) { - bVar2 = true; - goto LAB_080de3ab; - } - } - else { - this->SigIOPending = true; - } - goto switchD_080de377_caseD_0; - } - bVar2 = false; -LAB_080de3ab: - TConnection::Close(this,bVar2); - goto switchD_080de377_caseD_0; - } - error(&DAT_0810ce20,Socket); - iVar4 = close(Socket); - if (-1 < iVar4) goto LAB_080de426; - piVar5 = __errno_location(); - iVar4 = *piVar5; - Text = &DAT_0810ce60; - } -LAB_080de44b: - error(Text,iVar4); -LAB_080de426: - TConnection::Free(this); - return; -LAB_080de403: - CVar1 = this->State; - while (CVar1 - CONNECTION_LOGIN < 4) { - DelayThread(1,0); - CVar1 = this->State; - } - if (this->ClosingIsDelayed != false) { - DelayThread(2,0); - } - iVar4 = close(Socket); - if (-1 < iVar4) goto LAB_080de426; - piVar5 = __errno_location(); - iVar4 = *piVar5; - Text = &DAT_0810cde0; - goto LAB_080de44b; -} - - - -int HandleConnection(void *Data) - -{ - __pid_t _Var1; - int Socket; - int iVar2; - void *pvVar3; - - pvVar3 = Data; - if ((void *)0x7fffffff < Data) { - pvVar3 = (void *)((int)Data + 0xffff); - } - iVar2 = (int)pvVar3 >> 0x10; - if (UseOwnStacks) { - _Var1 = getpid(); - LastUsingCommunicationThread[iVar2] = _Var1; - } - // try { // try from 080de581 to 080de585 has its CatchHandler @ 080de5f0 - CommunicationThread((int)((int)Data + iVar2 * -0x10000)); - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + -1; - Semaphore::up(&CommunicationThreadMutex); - if (UseOwnStacks != false) { - Semaphore::down(&CommunicationThreadMutex); - FreeCommunicationThreadStacks[NumberOfFreeCommunicationThreadStacks] = iVar2; - NumberOfFreeCommunicationThreadStacks = NumberOfFreeCommunicationThreadStacks + 1; - Semaphore::up(&CommunicationThreadMutex); - } - return 0; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -bool OpenSocket(void) - -{ - ushort __x; - int iVar1; - int *piVar2; - char *pcVar3; - undefined4 uVar4; - undefined4 uVar5; - undefined4 *__optval; - undefined4 local_2c; - linger l; - undefined1 local_1c [4]; - sockaddr_in ServerAddress; - - print(); - getpid(); - __optval = (undefined4 *)GamePort; - print(); - uVar5 = 0; - uVar4 = 1; - TCPSocket = socket(2,1,0); - if (TCPSocket < 0) { - pcVar3 = &DAT_0810d1c0; - } - else { - local_2c = 0; - __optval = &local_2c; - l.l_onoff = 0; - uVar5 = 0xd; - uVar4 = 1; - iVar1 = setsockopt(TCPSocket,1,0xd,__optval,8); - if (-1 < iVar1) { - local_1c._0_2_ = 0; - local_1c[2] = '\0'; - local_1c[3] = '\0'; - ServerAddress.sin_family = 0; - ServerAddress.sin_port = 0; - ServerAddress.sin_addr.s_addr = 0; - ServerAddress.sin_zero[0] = '\0'; - ServerAddress.sin_zero[1] = '\0'; - ServerAddress.sin_zero[2] = '\0'; - ServerAddress.sin_zero[3] = '\0'; - ServerAddress._0_4_ = inet_addr(GameAddress); - local_1c._2_2_ = (ushort)GamePort >> 8 | (ushort)GamePort << 8; - local_1c._0_2_ = 2; - iVar1 = setsockopt(TCPSocket,1,2,(sockaddr *)local_1c,0x10); - if (iVar1 == 0) { - iVar1 = bind(TCPSocket,(sockaddr *)local_1c,0x10); - if (iVar1 < 0) { - piVar2 = __errno_location(); - error("LaunchServer: Fehler %d bei bind.\n",*piVar2); - print(); - while (iVar1 = bind(TCPSocket,(sockaddr *)local_1c,0x10), iVar1 < 0) { - sleep(1); - } - } - piVar2 = __errno_location(); - *piVar2 = 0; - iVar1 = listen(TCPSocket,0x200); - if (-1 < iVar1) { - return true; - } - iVar1 = *piVar2; - pcVar3 = "LaunchServer: Fehler %d bei listen.\n"; - } - else { - piVar2 = __errno_location(); - iVar1 = *piVar2; - pcVar3 = "LaunchServer: Fehler %d bei setsockopt.\n"; - } - error(pcVar3,iVar1); - return false; - } - pcVar3 = "LaunchServer: Socket wurde nicht auf LINGER=0 gesetzt.\n"; - } - error(pcVar3,uVar4,uVar5,__optval); - return false; -} - - - -// WARNING: Variable defined which should be unmapped: cli_addr - -int AcceptorThreadLoop(void *param_1) - -{ - bool bVar1; - RESULT r; - int *piVar2; - void *Argument; - Thread TVar3; - int iVar4; - int NewSocket; - char *Text; - int local_28; - int StackNumber; - void *Stack; - socklen_t clilen; - sockaddr_in cli_addr; - - AcceptorThreadPID = getpid(); - print(); -LAB_080de8d6: - do { - while( true ) { - bVar1 = GameRunning(); - if (!bVar1) { - AcceptorThreadPID = 0; - if (0 < ActiveConnections) { - print(); - while (0 < ActiveConnections) { - sleep(1); - } - } - return 0; - } - Stack = (void *)0x10; - piVar2 = __errno_location(); - *piVar2 = 0; - Argument = (void *)accept(TCPSocket,(sockaddr *)&clilen,(socklen_t *)&Stack); - if ((int)Argument < 0) goto LAB_080deb36; - if (UseOwnStacks != false) break; - if (ActiveConnections < 0x44c) { - // try { // try from 080de940 to 080deb3a has its CatchHandler @ 080deba5 - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + 1; - Semaphore::up(&CommunicationThreadMutex); - TVar3 = StartThread(HandleConnection,Argument,0x10000,true); - if (TVar3 == 0) { - print(); - iVar4 = close((int)Argument); - if (iVar4 < 0) { - error(&DAT_0810d240,*piVar2); - } - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + -1; -LAB_080de9b1: - Semaphore::up(&CommunicationThreadMutex); - } - } - else { - print(); - iVar4 = close((int)Argument); - if (iVar4 < 0) { - iVar4 = *piVar2; - Text = &DAT_0810d280; - goto LAB_080dea07; - } - } - } - GetCommunicationThreadStack(&local_28,(void **)&StackNumber); - if (StackNumber != 0) { - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + 1; - Semaphore::up(&CommunicationThreadMutex); - TVar3 = StartThread(HandleConnection,(void *)(local_28 * 0x10000 + (int)Argument), - (void *)StackNumber,0x10000,true); - if (TVar3 == 0) { - print(); - iVar4 = close((int)Argument); - if (iVar4 < 0) { - error(&DAT_0810d2c0,*piVar2); - } - Semaphore::down(&CommunicationThreadMutex); - ActiveConnections = ActiveConnections + -1; - Semaphore::up(&CommunicationThreadMutex); - iVar4 = local_28; - Semaphore::down(&CommunicationThreadMutex); - FreeCommunicationThreadStacks[NumberOfFreeCommunicationThreadStacks] = iVar4; - NumberOfFreeCommunicationThreadStacks = NumberOfFreeCommunicationThreadStacks + 1; - goto LAB_080de9b1; - } - goto LAB_080de8d6; - } - print(); - iVar4 = close((int)Argument); - if (iVar4 < 0) { - iVar4 = *piVar2; - Text = &DAT_0810d320; - goto LAB_080dea07; - } - } while( true ); -LAB_080deb36: - bVar1 = GameRunning(); - if (bVar1) { - iVar4 = *piVar2; - Text = "AcceptorThreadLoop: Fehler %d beim Accept.\n"; -LAB_080dea07: - error(Text,iVar4); - } - goto LAB_080de8d6; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void CheckThreadlibVersion(void) - -{ - bool bVar1; - - bVar1 = FileExists("/etc/image-release"); - UseOwnStacks = !bVar1; - print(); - return; -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void InitCommunication(void) - -{ - bool bVar1; - int i; - int iVar2; - undefined4 *puVar3; - char *ctx; - - // try { // try from 080dec6d to 080ded9e has its CatchHandler @ 080dee0e - bVar1 = FileExists("/etc/image-release"); - UseOwnStacks = !bVar1; - if (UseOwnStacks) { - ctx = "Verwende eigene Stacks.\n"; - } - else { - ctx = "Verwende verkleinerte Bibliotheks-Stacks.\n"; - } - print(); - if (UseOwnStacks != false) { - ctx = (char *)0xaa; - memset(CommunicationThreadStacks,0xaa,0x44c0000); - iVar2 = 0; - do { - LastUsingCommunicationThread[iVar2] = 0; - FreeCommunicationThreadStacks[iVar2] = iVar2; - iVar2 = iVar2 + 1; - } while (iVar2 < 0x44c); - NumberOfFreeCommunicationThreadStacks = 0x44c; - } - TQueryManagerConnectionPool::init(&QueryManagerConnectionPool,(EVP_PKEY_CTX *)ctx); - ActiveConnections = 0; - TCPSocket = -1; - OpenSocket(); - if (TCPSocket == -1) { - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = "cannot open socket"; - } - else { - iVar2 = 0; - do { - LoadHistory[iVar2] = 0; - iVar2 = iVar2 + 1; - } while (iVar2 < 0x168); - LoadHistoryPointer = 0; - TotalSend = 0; - TotalRecv = 0; - TotalLoad = 0; - LagEnd = 0; - EarliestFreeAccountAdmissionRound = 0; - InitLog("netload"); - WaitinglistHead = (TWaitinglistEntry *)0x0; - TRSAPrivateKey::init - (&PrivateKey, - (EVP_PKEY_CTX *) - "12017580013707233233987537782574702577133548287527131234152948150506251412291888866940292054989907714155267326586216043845592229084368540020196135619327879" - ); - AcceptorThread = StartThread(AcceptorThreadLoop,(void *)0x0,false); - if (AcceptorThread != 0) { - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = "cannot start acceptor thread"; - } - // WARNING: Subroutine does not return - __cxa_throw(puVar3,char_const*::typeinfo,0); -} - - - -// WARNING: Unknown calling convention -- yet parameter storage is locked - -void ExitCommunication(void) - -{ - TConnection *Connection; - TConnection *this; - pid_t __pid; - int iVar1; - int *piVar2; - char *__status; - - print(); - this = GetFirstConnection(); - while (this != (TConnection *)0x0) { - __pid = TConnection::GetPID(this); - kill(__pid,1); - this = GetNextConnection(); - } - ProcessConnections(); - __status = "Alle Verbindungen beendet.\n"; - print(); - if ((TCPSocket != -1) && (iVar1 = close(TCPSocket), iVar1 < 0)) { - piVar2 = __errno_location(); - __status = (char *)*piVar2; - error(&DAT_0810d500); - } - if (AcceptorThread != 0) { - if (AcceptorThreadPID != 0) { - __status = (char *)0x1; - kill(AcceptorThreadPID,1); - } - JoinThread(AcceptorThread); - AcceptorThread = 0; - } - TQueryManagerConnectionPool::exit(&QueryManagerConnectionPool,(int)__status); - ExitCommunicationThreadStacks(); - return; -} - - - void __tcf_0(void *param_1) { diff --git a/src/communication.cc b/src/communication.cc index 6996b1c..b959bb1 100644 --- a/src/communication.cc +++ b/src/communication.cc @@ -8,8 +8,12 @@ #include "stubs.hh" +#include +#include +#include #include #include +#include // NOTE(fusion): We seem to add this value of 48 every time `NetLoad` is called, // and I assume it is to account for IPv4 (20 bytes) and TCP (20 bytes) headers @@ -18,7 +22,17 @@ #define PACKET_AVERAGE_SIZE_OVERHEAD 48 #define MAX_COMMUNICATION_THREADS 1100 -#define THREAD_OWN_STACK_SIZE ((int)KB(64)) +#define COMMUNICATION_THREAD_STACK_SIZE ((int)KB(64)) + +static const char RSA_PRIME_P[] = + "1201758001370723323398753778257470257713354828752713123415294815" + "0506251412291888866940292054989907714155267326586216043845592229" + "084368540020196135619327879"; + +static const char RSA_PRIME_Q[] = + "1189892136861686835188050824611210139447876026576932541274639840" + "5473436969889506919017477758618276066588858607419440134394668095" + "105156501566867770737187273"; static int TERMINALVERSION[3] = {770, 770, 770}; static int TCPSocket; @@ -42,7 +56,7 @@ static TWaitinglistEntry *WaitinglistHead; static Semaphore CommunicationThreadMutex(1); static bool UseOwnStacks; -static uint8 CommunicationThreadStacks[MAX_COMMUNICATION_THREADS][THREAD_OWN_STACK_SIZE]; +static uint8 CommunicationThreadStacks[MAX_COMMUNICATION_THREADS][COMMUNICATION_THREAD_STACK_SIZE]; static pid_t LastUsingCommunicationThread[MAX_COMMUNICATION_THREADS]; static int FreeCommunicationThreadStacks[MAX_COMMUNICATION_THREADS]; static int NumberOfFreeCommunicationThreadStacks; @@ -104,9 +118,9 @@ void ExitCommunicationThreadStacks(void){ } int HighestStackAddress = -1; - int LowestStackAddress = THREAD_OWN_STACK_SIZE; + int LowestStackAddress = COMMUNICATION_THREAD_STACK_SIZE; for(int i = 0; i < MAX_COMMUNICATION_THREADS; i += 1){ - for(int Addr = 0; Addr < THREAD_OWN_STACK_SIZE; Addr += 1){ + for(int Addr = 0; Addr < COMMUNICATION_THREAD_STACK_SIZE; Addr += 1){ if(CommunicationThreadStacks[i][Addr] != 0xAA){ if(Addr < LowestStackAddress){ LowestStackAddress = Addr; @@ -121,7 +135,7 @@ void ExitCommunicationThreadStacks(void){ // NOTE(fusion): It seems we want to track whether the stack size is // too small but I'd argue the method is not very robust. - if((HighestStackAddress - LowestStackAddress) > (THREAD_OWN_STACK_SIZE / 2)){ + if((HighestStackAddress - LowestStackAddress) > (COMMUNICATION_THREAD_STACK_SIZE / 2)){ error("Maximale Stack-Ausdehnung: %d..%d\n", LowestStackAddress, HighestStackAddress); } } @@ -129,19 +143,6 @@ void ExitCommunicationThreadStacks(void){ // Load History // ============================================================================= -void InitLoadHistory(void){ - for(int i = 0; i < NARRAY(LoadHistory); i += 1){ - LoadHistory[i] = 0; - } - LoadHistoryPointer = 0; - TotalLoad = 0; - TotalSend = 0; - TotalRecv = 0; - LagEnd = 0; - EarliestFreeAccountAdmissionRound = 0; - InitLog("netload"); -} - bool LagDetected(void){ return RoundNr <= LagEnd; } @@ -232,6 +233,23 @@ void NetLoadCheck(void){ } } +void InitLoadHistory(void){ + for(int i = 0; i < NARRAY(LoadHistory); i += 1){ + LoadHistory[i] = 0; + } + LoadHistoryPointer = 0; + TotalLoad = 0; + TotalSend = 0; + TotalRecv = 0; + LagEnd = 0; + EarliestFreeAccountAdmissionRound = 0; + InitLog("netload"); +} + +void ExitLoadHistory(void){ + // no-op +} + // Connection Output // ============================================================================= bool WriteToSocket(TConnection *Connection, uint8 *Buffer, int Size){ @@ -1120,13 +1138,24 @@ bool ReceiveCommand(TConnection *Connection){ uint8 Help[2]; int BytesRead = ReadFromSocket(Connection, Help, 2); if(BytesRead == 0){ + // NOTE(fusion): Peer has closed the connection and there was no + // more data to read. return false; }else if(BytesRead < 0){ + // NOTE(fusion): There was either no data to be read or a connection + // error. Since we can't differ, let the connection be closed elsewhere + // in case of errors. This is the only path that will not cause the + // connection to be closed, aside from successfully reading a packet. return true; - }else if(BytesRead == 1){ - NetLoad(PACKET_AVERAGE_SIZE_OVERHEAD + 1, false); + } + + // NOTE(fusion): It doesn't make sense to continue if we couldn't read + // the packet's length completely. We're already using TCP which doesn't + // drop data so it would only compound into more errors. + if(BytesRead != 2){ + NetLoad(PACKET_AVERAGE_SIZE_OVERHEAD + BytesRead, false); print(3, "Zu wenig Daten an Socket %d.\n", BytesRead); - return true; + return false; } // TODO(fusion): Size is encoded as a little endian uint16. We should @@ -1165,7 +1194,7 @@ bool ReceiveCommand(TConnection *Connection){ } if(Connection->State == CONNECTION_CONNECTED){ - alarm(0); // TODO(fusion): Not sure why. + alarm(0); Connection->InDataSize = Size; if(!HandleLogin(Connection)){ return false; @@ -1200,6 +1229,331 @@ bool ReceiveCommand(TConnection *Connection){ } } + // NOTE(fusion): We set `SigIOPending` here to signal that there could be more + // packets already queued up, so `CommunicationThread` can attempt to read them + // without waiting for another `SIGIO`. + // The only way to know there is no more data on a socket's receive buffer is + // when `read` returns `EAGAIN` which is handled when `ReadFromSocket` returns + // a negative value. Connection->SigIOPending = true; return true; } + +// Communication Thread +// ============================================================================= +void IncrementActiveConnections(void){ + CommunicationThreadMutex.down(); + ActiveConnections += 1; + CommunicationThreadMutex.up(); +} + +void DecrementActiveConnections(void){ + CommunicationThreadMutex.down(); + ActiveConnections += 1; + CommunicationThreadMutex.up(); +} + +void CommunicationThread(int Socket){ + TConnection *Connection = AssignFreeConnection(); + if(Connection == NULL){ + print(2, "Keine Verbindung mehr frei.\n"); + if(close(Socket) == -1){ + error("CommunicationThread: Fehler %d beim Schließen der Socket (1).\n", errno); + } + return; + } + + Connection->Connect(Socket); + Connection->WaitingForACK = false; + if(fcntl(Socket, F_SETOWN, getpid()) == -1){ + error("CommunicationThread: F_SETOWN fehlgeschlagen für Socket %d.\n", Socket); + if(close(Socket) == -1){ + error("CommunicationThread: Fehler %d beim Schließen der Socket (2).\n", errno); + } + Connection->Free(); + return; + } + + if(fcntl(Socket, F_SETFL, (O_NONBLOCK | O_ASYNC)) == -1){ + error("ConnectionThread: F_SETFL fehlgeschlagen für Socket %d.\n", Socket); + if(close(Socket) == -1){ + error("CommunicationThread: Fehler %d beim Schließen der Socket (3).\n", errno); + } + Connection->Free(); + return; + } + + sigset_t SignalSet; + sigfillset(&SignalSet); + sigprocmask(SIG_SETMASK, &SignalSet, NULL); + alarm(5); + + if(!ReceiveCommand(Connection)){ + Connection->Close(true); + } + + Connection->SigIOPending = false; + while(GameRunning() && Connection->ConnectionIsOk){ + int Signal; + sigwait(&SignalSet, &Signal); + switch(Signal){ + case SIGHUP: + case SIGPIPE:{ + Connection->Close(false); + break; + } + + case SIGUSR1: + case SIGIO:{ + if(Signal == SIGIO || Connection->SigIOPending){ + if(!Connection->WaitingForACK){ + Connection->SigIOPending = false; + if(!ReceiveCommand(Connection)){ + Connection->Close(true); + } + }else{ + Connection->SigIOPending = true; + } + } + break; + } + + case SIGUSR2:{ + if(!SendData(Connection)){ + Connection->Close(false); + } + break; + } + + case SIGALRM:{ + // NOTE(fusion): Login deadline. + if(Connection->State == CONNECTION_CONNECTED){ + print(2, "Login-TimeOut für Socket %d.\n", Socket); + Connection->Close(false); + } + break; + } + + default:{ + // no-op + break; + } + } + } + + while(Connection->Live()){ + DelayThread(1, 0); + } + + // TODO(fusion): Is this done to allow for queued data to be sent, almost + // like `SO_LINGER`? + if(Connection->ClosingIsDelayed){ + DelayThread(2, 0); + } + + if(close(Socket) == -1){ + error("CommunicationThread: Fehler %d beim Schließen der Socket (4).\n", errno); + } + + Connection->Free(); +} + +int HandleConnection(void *Data){ + int Socket = (uint16)((uintptr)Data); + int StackNumber = (uint16)((uintptr)Data >> 16); + + if(UseOwnStacks){ + AttachCommunicationThreadStack(StackNumber); + } + + CommunicationThread(Socket); + DecrementActiveConnections(); + + if(UseOwnStacks){ + ReleaseCommunicationThreadStack(StackNumber); + } + + return 0; +} + +// Acceptor Thread +// ============================================================================= +bool OpenSocket(void){ + print(1, "Starte Game-Server...\n"); + print(1, "Pid %d - horche an Port %d\n", getpid(), GamePort); + TCPSocket = socket(AF_INET, SOCK_STREAM, 0); + if(TCPSocket == -1){ + error("LaunchServer: Kann Socket nicht öffnen.\n"); + return false; + } + + struct linger linger = {}; + linger.l_onoff = 0; + linger.l_linger = 0; + if(setsockopt(TCPSocket, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)) == -1){ + error("LaunchServer: Socket wurde nicht auf LINGER=0 gesetzt.\n"); + return false; + } + + int reuseaddr = 1; + if(setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ + error("LaunchServer: Fehler %d bei setsockopt.\n", errno); + return false; + } + + struct sockaddr_in ServerAddress = {}; + ServerAddress.sin_family = AF_INET; + ServerAddress.sin_port = htons(GamePort); + ServerAddress.sin_addr.s_addr = inet_addr(GameAddress); + if(bind(TCPSocket, (struct sockaddr*)&ServerAddress, sizeof(struct sockaddr_in)) == -1){ + error("LaunchServer: Fehler %d bei bind.\n", errno); + print(1, "Bind Error Again -> Begin FloodBind :(\n"); + while(bind(TCPSocket, (struct sockaddr*)&ServerAddress, sizeof(struct sockaddr_in)) == -1){ + DelayThread(1, 0); + } + } + + if(listen(TCPSocket, 512) == -1){ + error("LaunchServer: Fehler %d bei listen.\n", errno); + return false; + } + + return true; +} + +int AcceptorThreadLoop(void *Unused){ + AcceptorThreadPID = getpid(); + print(1, "Warte auf Clients...\n"); + while(GameRunning()){ + struct sockaddr_in RemoteAddr = {}; + socklen_t RemoteAddrLen = sizeof(RemoteAddr); + int Socket = accept(TCPSocket, (struct sockaddr*)&RemoteAddr, &RemoteAddrLen); + if(Socket == -1){ + error("AcceptorThreadLoop: Fehler %d beim Accept.\n", errno); + continue; + } + + if(UseOwnStacks){ + int StackNumber; + void *Stack; + GetCommunicationThreadStack(&StackNumber, &Stack); + if(Stack == NULL){ + print(3,"Kein Stack-Bereich mehr frei.\n"); + if(close(Socket) == -1){ + error("AcceptorThreadLoop: Fehler %d beim Schließen der Socket (1).\n", errno); + } + }else{ + IncrementActiveConnections(); + void *Argument = (void*)(((uintptr)Socket & 0xFFFF) + | (((uintptr)StackNumber & 0xFFFF) << 16)); + ThreadHandle ConnectionThread = StartThread(HandleConnection, + Argument, Stack, COMMUNICATION_THREAD_STACK_SIZE, true); + if(ConnectionThread == INVALID_THREAD_HANDLE){ + DecrementActiveConnections(); + ReleaseCommunicationThreadStack(StackNumber); + if(close(Socket) == -1){ + error("AcceptorThreadLoop: Fehler %d beim Schließen der Socket (2).\n", errno); + } + } + } + }else{ + if(ActiveConnections >= MAX_COMMUNICATION_THREADS){ + print(3,"Keine Verbindung mehr frei.\n"); + if(close(Socket) == -1){ + error("AcceptorThreadLoop: Fehler %d beim Schließen der Socket (3).\n", errno); + } + }else{ + IncrementActiveConnections(); + void *Argument = (void*)((uintptr)Socket & 0xFFFF); + ThreadHandle ConnectionThread = StartThread(HandleConnection, + Argument, COMMUNICATION_THREAD_STACK_SIZE, true); + if(ConnectionThread == INVALID_THREAD_HANDLE){ + print(3, "Kann neuen Thread nicht anlegen.\n"); + DecrementActiveConnections(); + if(close(Socket) == -1){ + error("AcceptorThreadLoop: Fehler %d beim Schließen der Socket (4).\n", errno); + } + } + } + } + } + + AcceptorThreadPID = 0; + if(ActiveConnections > 0){ + print(3, "Warte auf Beendigung von %d Communication-Threads...\n", ActiveConnections); + while(ActiveConnections > 0){ + DelayThread(1, 0); + } + } + + return 0; +} + +// Initialization +// ============================================================================= +void CheckThreadlibVersion(void){ + // TODO(fusion): We'll probably remove `OwnStacks` support anyway but it + // seems to be using this file `/etc/image-release` as an heuristic to + // enable it. Not sure what this file is about. + UseOwnStacks = FileExists("/etc/image-release"); + if(UseOwnStacks){ + print(2, "Verwende eigene Stacks.\n"); + }else{ + print(2, "Verwende verkleinerte Bibliotheks-Stacks.\n"); + } +} + +void InitCommunication(void){ + CheckThreadlibVersion(); + InitCommunicationThreadStacks(); + InitLoadHistory(); + + WaitinglistHead = NULL; + TCPSocket = -1; + AcceptorThread = INVALID_THREAD_HANDLE; + AcceptorThreadPID = 0; + ActiveConnections = 0; + QueryManagerConnectionPool.init(); + PrivateKey.init(RSA_PRIME_P, RSA_PRIME_Q); + + OpenSocket(); + if(TCPSocket == -1){ + throw "cannot open socket"; + } + + AcceptorThread = StartThread(AcceptorThreadLoop, NULL, false); + if(AcceptorThread == INVALID_THREAD_HANDLE){ + throw "cannot start acceptor thread"; + } +} + +void ExitCommunication(void){ + print(3, "Beende alle Verbindungen...\n"); + TConnection *Connection = GetFirstConnection(); + while(Connection != NULL){ + kill(Connection->GetPID(), SIGHUP); + Connection = GetNextConnection(); + } + + ProcessConnections(); + print(3, "Alle Verbindungen beendet.\n"); + + if(TCPSocket != -1){ + if(close(TCPSocket) == -1){ + error("ExitCommunication: Fehler %d beim Schließen der Socket.\n", errno); + } + TCPSocket = -1; + } + + if(AcceptorThread != INVALID_THREAD_HANDLE){ + if(AcceptorThreadPID != 0){ + kill(AcceptorThreadPID, SIGHUP); + } + JoinThread(AcceptorThread); + AcceptorThread = INVALID_THREAD_HANDLE; + } + + QueryManagerConnectionPool.exit(); + ExitLoadHistory(); + ExitCommunicationThreadStacks(); +} diff --git a/src/communication.hh b/src/communication.hh index bfeeeb4..f466b9e 100644 --- a/src/communication.hh +++ b/src/communication.hh @@ -20,11 +20,12 @@ void ReleaseCommunicationThreadStack(int StackNumber); void InitCommunicationThreadStacks(void); void ExitCommunicationThreadStacks(void); -void InitLoadHistory(void); bool LagDetected(void); void NetLoad(int Amount, bool Send); void NetLoadSummary(void); void NetLoadCheck(void); +void InitLoadHistory(void); +void ExitLoadHistory(void); bool WriteToSocket(TConnection *Connection, uint8 *Buffer, int Size); bool SendLoginMessage(TConnection *Connection, int Type, const char *Message, int WaitingTime); @@ -45,4 +46,15 @@ TPlayerData *PerformRegistration(TConnection *Connection, char *PlayerName, bool HandleLogin(TConnection *Connection); bool ReceiveCommand(TConnection *Connection); +void IncrementActiveConnections(void); +void DecrementActiveConnections(void); +void CommunicationThread(int Socket); +int HandleConnection(void *Data); +bool OpenSocket(void); +int AcceptorThreadLoop(void *Unused); + +void CheckThreadlibVersion(void); +void InitCommunication(void); +void ExitCommunication(void); + #endif //TIBIA_COMMUNICATION_HH_ diff --git a/src/connections.hh b/src/connections.hh index 021943b..79462c5 100644 --- a/src/connections.hh +++ b/src/connections.hh @@ -27,6 +27,10 @@ struct TConnection { void EmergencyPing(void); void Login(void); const char *GetName(void); + void Connect(int Socket); + void Close(bool Delay); + void Free(void); + pid_t GetPID(void); // TODO(fusion): Maybe rename this later? bool Live(void) const { diff --git a/src/crypto.hh b/src/crypto.hh index 7561ce5..7159a37 100644 --- a/src/crypto.hh +++ b/src/crypto.hh @@ -33,6 +33,7 @@ struct vlong_montgomery{ struct TRSAPrivateKey{ TRSAPrivateKey(void); ~TRSAPrivateKey(void); + void init(const char *PrimeP, const char *PrimeQ); void decrypt(uint8 *Data); // single 128 bytes block // DATA diff --git a/src/query.hh b/src/query.hh index 92c3828..e518b69 100644 --- a/src/query.hh +++ b/src/query.hh @@ -37,6 +37,8 @@ struct TQueryManagerConnection{ struct TQueryManagerConnectionPool{ TQueryManagerConnectionPool(int Connections); + void init(void); + void exit(void); // DATA // ================= diff --git a/src/stubs.hh b/src/stubs.hh index cc4b261..a749c8c 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -17,6 +17,7 @@ typedef void TSendMailsFunction(TPlayerData *PlayerData); extern void AbortWriter(void); extern void AddBuddyOrder(TCreature *Creature, uint32 CharacterID); +extern TConnection *AssignFreeConnection(void); extern void RemoveBuddyOrder(TCreature *Creature, uint32 BuddyID); extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3); extern void ChangeNPCState(TCreature *Npc, int NewState, bool Stimulus); -- cgit v1.2.3