diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2026-03-20 17:50:54 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2026-03-20 17:50:54 -0300 |
| commit | 27ae2780288cacf6de913e049de0138c0a4266ec (patch) | |
| tree | 2587765b88fadb5d5e83806a53f588fff29d62b1 | |
| parent | 66a3212bc112ace10c008fddee9d27a99b1e95d8 (diff) | |
| download | login-27ae2780288cacf6de913e049de0138c0a4266ec.tar.gz login-27ae2780288cacf6de913e049de0138c0a4266ec.zip | |
support for proxy headers
| -rw-r--r-- | README.md | 9 | ||||
| -rw-r--r-- | src/common.hh | 4 | ||||
| -rw-r--r-- | src/connections.cc | 96 |
3 files changed, 96 insertions, 13 deletions
@@ -9,5 +9,14 @@ make -B DEBUG=1 # rebuild in debug mode make clean # remove `build` directory ``` +### Extra Features (Advanced) +There are also some extra features that can be enabled by appending the following definitions to the `CFLAGS` variable in your `Makefile`. They're completely optional but can be useful in specific scenarios. + +- `-DTIBIA772=1` + - Check for client version 7.72 rather than 7.7. The login protocols for both versions are the same, but because the game protocols are not, there is no reason to allow both to go through. + +- `-DALLOW_LOCAL_PROXY=1` + - Allow **local** connections to send a proxy header. Ideally we'd have a list of trusted proxies to prevent clients from spoofing their addresses. In reality, it is almost always simpler to have a **local** relay. This is also the only way to get a multi-route proxy without having to drastically change the way connections are handled. + ## Running Similar to the game server, the login server won't boot up if it's not able to connect to the [Query Manager](https://github.com/fusion32/tibia-querymanager). That said, running it is straighforward, requiring only the RSA private key `tibia.pem` and `config.cfg` files to be in the working directory. For testing purposes you could simply compile and launch the application from the shell, but if you plan to run the game server on a dedicated machine, it is recommended that it is setup as a service. There is a *systemd* configuration file (`tibia-login.service`) in the repository that may be used for that purpose. The process is very similar to the one described in the [Game Server](https://github.com/fusion32/tibia-game) so I won't repeat myself here. diff --git a/src/common.hh b/src/common.hh index fd87888..25259f7 100644 --- a/src/common.hh +++ b/src/common.hh @@ -562,9 +562,13 @@ struct TConnection { ConnectionState State; int Socket; int IPAddress; + int RemotePort; int StartTime; int RWSize; int RWPosition; +#if ALLOW_LOCAL_PROXY + bool ReadingProxyHeader; +#endif uint32 RandomSeed; uint32 XTEA[4]; char RemoteAddress[32]; diff --git a/src/connections.cc b/src/connections.cc index a1111df..339e6dc 100644 --- a/src/connections.cc +++ b/src/connections.cc @@ -129,6 +129,7 @@ static TConnection *AssignConnection(int Socket, uint32 Addr, uint16 Port){ Connection->State = CONNECTION_READING; Connection->Socket = Socket; Connection->IPAddress = (int)Addr; + Connection->RemotePort = (int)Port; Connection->StartTime = GetMonotonicUptime(); Connection->RandomSeed = (uint32)rand(); StringBufFormat(Connection->RemoteAddress, @@ -137,7 +138,7 @@ static TConnection *AssignConnection(int Socket, uint32 Addr, uint16 Port){ ((Connection->IPAddress >> 16) & 0xFF), ((Connection->IPAddress >> 8) & 0xFF), ((Connection->IPAddress >> 0) & 0xFF), - (int)Port); + Connection->RemotePort); LOG("Connection %s assigned to slot %d", Connection->RemoteAddress, ConnectionIndex); } @@ -184,21 +185,87 @@ static void CheckConnectionInput(TConnection *Connection, int Events){ Connection->RWPosition += BytesRead; if(Connection->RWPosition >= ReadSize){ - if(Connection->RWSize != 0){ - Connection->State = CONNECTION_PROCESSING; - break; - }else if(Connection->RWPosition == 2){ - int PayloadSize = (int)BufferRead16LE(Connection->Buffer); - if(PayloadSize <= 0 || PayloadSize > (int)sizeof(Connection->Buffer)){ - CloseConnection(Connection); - break; + ASSERT(Connection->RWPosition == ReadSize); + if(Connection->RWSize == 0){ +#if ALLOW_LOCAL_PROXY + if(Connection->Buffer[0] == 0x0D && Connection->Buffer[1] == 0x0A){ + ASSERT(sizeof(Connection->Buffer) >= 28); + Connection->RWSize = 28; + Connection->ReadingProxyHeader = true; + }else if(Connection->Buffer[0] == 0xD9 && Connection->Buffer[1] == 0xFF){ + ASSERT(sizeof(Connection->Buffer) >= 6); + Connection->RWSize = 6; + Connection->ReadingProxyHeader = true; + }else +#endif + { + int PayloadSize = (int)BufferRead16LE(Connection->Buffer); + if(PayloadSize <= 0 || PayloadSize > (int)sizeof(Connection->Buffer)){ + CloseConnection(Connection); + break; + } + + Connection->RWSize = PayloadSize; + Connection->RWPosition = 0; + } + } +#if ALLOW_LOCAL_PROXY + else if(Connection->ReadingProxyHeader){ + uint8 *SourceAddr = NULL; + if(Connection->Buffer[0] == 0x0D && Connection->Buffer[1] == 0x0A){ + ASSERT(ReadSize == 28); + + // NOTE(fusion): HAProxy-V2 header. + static const uint8_t HAProxyV2Signature[] = { + 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, + 0x55, 0x49, 0x54, 0x0A, + }; + + if(memcmp(Connection->Buffer, HAProxyV2Signature, 12) != 0){ + LOG_ERR("Invalid proxy header from %s", Connection->RemoteAddress); + CloseConnection(Connection); + break; + } + + SourceAddr = Connection->Buffer + 16; + }else if(Connection->Buffer[0] == 0xD9 && Connection->Buffer[1] == 0xFF){ + ASSERT(ReadSize == 6); + SourceAddr = Connection->Buffer + 2; } - Connection->RWSize = PayloadSize; + if(SourceAddr != NULL){ + if(Connection->IPAddress == INADDR_LOOPBACK){ + LOG("Connection proxy header %s -> %d.%d.%d.%d:%d", + Connection->RemoteAddress, + (int)SourceAddr[0], (int)SourceAddr[1], + (int)SourceAddr[2], (int)SourceAddr[3], + Connection->RemotePort); + + Connection->IPAddress = BufferRead32BE(SourceAddr); + StringBufFormat(Connection->RemoteAddress, + "%d.%d.%d.%d:%d", + (int)SourceAddr[0], (int)SourceAddr[1], + (int)SourceAddr[2], (int)SourceAddr[3], + Connection->RemotePort); + }else{ + LOG_ERR("Received proxy header (%02X, %02X) from unknown host %s", + (int)Connection->Buffer[0], (int)Connection->Buffer[1], + Connection->RemoteAddress); + } + }else{ + LOG_ERR("Ignoring unknown proxy header (%02X, %02X) from %s", + (int)Connection->Buffer[0], (int)Connection->Buffer[1], + Connection->RemoteAddress); + } + + Connection->RWSize = 0; Connection->RWPosition = 0; - }else{ - PANIC("Invalid input state (State: %d, RWSize: %d, RWPosition: %d)", - Connection->State, Connection->RWSize, Connection->RWPosition); + Connection->ReadingProxyHeader = false; + } +#endif + else{ + Connection->State = CONNECTION_PROCESSING; + break; } } } @@ -254,8 +321,11 @@ static void CheckConnectionOutput(TConnection *Connection, int Events){ break; } + // NOTE(fusion): Both login and status protocols will serve a single + // request so we always close the connection after it is served. Connection->RWPosition += BytesWritten; if(Connection->RWPosition >= Connection->RWSize){ + ASSERT(Connection->RWPosition == Connection->RWSize); CloseConnection(Connection); break; } |
