diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/database.cc | 31 | ||||
| -rw-r--r-- | src/hostcache.cc | 105 | ||||
| -rw-r--r-- | src/querymanager.cc | 19 | ||||
| -rw-r--r-- | src/querymanager.hh | 14 |
4 files changed, 157 insertions, 12 deletions
diff --git a/src/database.cc b/src/database.cc index b873732..f79bd82 100644 --- a/src/database.cc +++ b/src/database.cc @@ -236,7 +236,7 @@ bool GetWorlds(DynamicArray<TWorld> *Worlds){ bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig){ ASSERT(WorldConfig != NULL); sqlite3_stmt *Stmt = PrepareQuery( - "SELECT Type, RebootTime, IPAddress, Port, MaxPlayers," + "SELECT Type, RebootTime, Host, Port, MaxPlayers," " PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer" " FROM Worlds WHERE WorldID = ?1"); if(Stmt == NULL){ @@ -255,9 +255,16 @@ bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig){ return false; } + int IPAddress; + const char *HostName = (const char*)sqlite3_column_text(Stmt, 2); + if(HostName == NULL || !ResolveHostName(HostName, &IPAddress)){ + LOG_ERR("Failed to resolve world %d host name \"%s\"", WorldID, HostName); + return false; + } + WorldConfig->Type = sqlite3_column_int(Stmt, 0); WorldConfig->RebootTime = sqlite3_column_int(Stmt, 1); - WorldConfig->IPAddress = sqlite3_column_int(Stmt, 2); + WorldConfig->IPAddress = IPAddress; WorldConfig->Port = sqlite3_column_int(Stmt, 3); WorldConfig->MaxPlayers = sqlite3_column_int(Stmt, 4); WorldConfig->PremiumPlayerBuffer = sqlite3_column_int(Stmt, 5); @@ -483,7 +490,7 @@ bool ActivatePendingPremiumDays(int AccountID){ bool GetCharacterEndpoints(int AccountID, DynamicArray<TCharacterEndpoint> *Characters){ sqlite3_stmt *Stmt = PrepareQuery( - "SELECT C.Name, W.Name, W.IPAddress, W.Port" + "SELECT C.Name, W.Name, W.Host, W.Port" " FROM Characters AS C" " INNER JOIN Worlds AS W ON W.WorldID = C.WorldID" " WHERE C.AccountID = ?1"); @@ -499,12 +506,20 @@ bool GetCharacterEndpoints(int AccountID, DynamicArray<TCharacterEndpoint> *Char } while(sqlite3_step(Stmt) == SQLITE_ROW){ + int WorldAddress; + const char *CharacterName = (const char*)sqlite3_column_text(Stmt, 0); + const char *WorldName = (const char*)sqlite3_column_text(Stmt, 1); + const char *HostName = (const char*)sqlite3_column_text(Stmt, 2); + if(HostName == NULL || !ResolveHostName(HostName, &WorldAddress)){ + LOG_ERR("Failed to resolve world \"%s\" host name \"%s\" for character \"%s\"", + WorldName, HostName, CharacterName); + continue; + } + TCharacterEndpoint Character = {}; - StringCopy(Character.Name, sizeof(Character.Name), - (const char*)sqlite3_column_text(Stmt, 0)); - StringCopy(Character.WorldName, sizeof(Character.WorldName), - (const char*)sqlite3_column_text(Stmt, 1)); - Character.WorldAddress = sqlite3_column_int(Stmt, 2); + StringCopy(Character.Name, sizeof(Character.Name), CharacterName); + StringCopy(Character.WorldName, sizeof(Character.WorldName), WorldName); + Character.WorldAddress = WorldAddress; Character.WorldPort = sqlite3_column_int(Stmt, 3); Characters->Push(Character); } diff --git a/src/hostcache.cc b/src/hostcache.cc new file mode 100644 index 0000000..5d5d46a --- /dev/null +++ b/src/hostcache.cc @@ -0,0 +1,105 @@ +#include "querymanager.hh" + +// TODO(fusion): Support windows eventually? +#if OS_LINUX +# include <netdb.h> +#else +# error "Operating system not currently supported." +#endif + +struct THostCacheEntry{ + char HostName[100]; + bool Resolved; + int IPAddress; + int ResolveTime; +}; + +static THostCacheEntry *g_CachedHostNames; + +bool InitHostCache(void){ + ASSERT(g_CachedHostNames == NULL); + LOG("Max cached host names: %d", g_MaxCachedHostNames); + LOG("Host name expire time: %dms", g_HostNameExpireTime); + g_CachedHostNames = (THostCacheEntry*)calloc( + g_MaxCachedHostNames, sizeof(THostCacheEntry)); + return true; +} + +void ExitHostCache(void){ + if(g_CachedHostNames != NULL){ + free(g_CachedHostNames); + g_CachedHostNames = NULL; + } +} + +static bool DoResolveHostName(const char *HostName, int *OutAddr){ + ASSERT(HostName != NULL && OutAddr != NULL); + addrinfo *Result = NULL; + addrinfo Hints = {}; + Hints.ai_family = AF_INET; + Hints.ai_socktype = SOCK_STREAM; + int ErrCode = getaddrinfo(HostName, NULL, &Hints, &Result); + if(ErrCode != 0){ + LOG_ERR("Failed to resolve hostname \"%s\": %s", HostName, gai_strerror(ErrCode)); + return false; + } + + bool Resolved = false; + for(addrinfo *AddrInfo = Result; + AddrInfo != NULL; + AddrInfo = AddrInfo->ai_next){ + if(AddrInfo->ai_family == AF_INET && AddrInfo->ai_socktype == SOCK_STREAM){ + ASSERT(AddrInfo->ai_addrlen == sizeof(sockaddr_in)); + *OutAddr = ntohl(((sockaddr_in*)AddrInfo->ai_addr)->sin_addr.s_addr); + Resolved = true; + break; + } + } + freeaddrinfo(Result); + return Resolved; +} + +bool ResolveHostName(const char *HostName, int *OutAddr){ + ASSERT(HostName != NULL && !StringEmpty(HostName)); + THostCacheEntry *Entry = NULL; + int LeastRecentlyUsedIndex = 0; + int LeastRecentlyUsedTime = g_CachedHostNames[0].ResolveTime; + for(int i = 0; i < g_MaxCachedHostNames; i += 1){ + THostCacheEntry *Current = &g_CachedHostNames[i]; + + if((g_MonotonicTimeMS - Current->ResolveTime) >= g_HostNameExpireTime){ + memset(Current, 0, sizeof(THostCacheEntry)); + } + + if(Current->ResolveTime < LeastRecentlyUsedTime){ + LeastRecentlyUsedIndex = i; + LeastRecentlyUsedTime = Current->ResolveTime; + } + + if(StringEq(HostName, Current->HostName)){ + Entry = Current; + break; + } + } + + if(Entry == NULL){ + // NOTE(fusion): We also cache failures. + Entry = &g_CachedHostNames[LeastRecentlyUsedIndex]; + if(!StringCopy(Entry->HostName, sizeof(Entry->HostName), HostName)){ + LOG_WARN("Hostname \"%s\" was improperly cached because it was" + " too long (Length: %d, MaxLength: %d)", HostName, + (int)strlen(HostName), (int)sizeof(Entry->HostName)); + } + Entry->Resolved = DoResolveHostName(HostName, &Entry->IPAddress); + Entry->ResolveTime = g_MonotonicTimeMS; + } + + if(Entry && Entry->Resolved){ + if(OutAddr){ + *OutAddr = Entry->IPAddress; + } + return true; + }else{ + return false; + } +} diff --git a/src/querymanager.cc b/src/querymanager.cc index 1ed818e..6f4cfc9 100644 --- a/src/querymanager.cc +++ b/src/querymanager.cc @@ -15,14 +15,20 @@ int g_ShutdownSignal = 0; // Time int g_MonotonicTimeMS = 0; -// Config +// Database Config char g_DatabaseFile[1024] = "tibia.db"; int g_MaxCachedStatements = 100; + +// HostCache Config +int g_MaxCachedHostNames = 100; +int g_HostNameExpireTime = 30 * 60 * 1000; // milliseconds + +// Connection Config int g_UpdateRate = 20; int g_QueryManagerPort = 7174; char g_QueryManagerPassword[30] = ""; int g_MaxConnections = 50; -int g_MaxConnectionIdleTime = 60000; +int g_MaxConnectionIdleTime = 60 * 1000; // milliseconds int g_MaxConnectionPacketSize = (int)MB(1); void LogAdd(const char *Prefix, const char *Format, ...){ @@ -370,6 +376,10 @@ bool ReadConfig(const char *FileName){ ReadStringConfig(g_DatabaseFile, (int)sizeof(g_DatabaseFile), Val); }else if(StringEqCI(Key, "MaxCachedStatements")){ ReadIntegerConfig(&g_MaxCachedStatements, Val); + }else if(StringEqCI(Key, "MaxCachedHostNames")){ + ReadIntegerConfig(&g_MaxCachedHostNames, Val); + }else if(StringEqCI(Key, "HostNameExpireTime")){ + ReadDurationConfig(&g_HostNameExpireTime, Val); }else if(StringEqCI(Key, "UpdateRate")){ ReadIntegerConfig(&g_UpdateRate, Val); }else if(StringEqCI(Key, "QueryManagerPort")){ @@ -430,9 +440,12 @@ int main(int argc, const char **argv){ return EXIT_FAILURE; } + atexit(ExitHostCache); atexit(ExitDatabase); atexit(ExitConnections); - if(!InitDatabase() || !InitConnections()){ + if(!InitHostCache() + || !InitDatabase() + || !InitConnections()){ return EXIT_FAILURE; } diff --git a/src/querymanager.hh b/src/querymanager.hh index 04f74e9..c1e48eb 100644 --- a/src/querymanager.hh +++ b/src/querymanager.hh @@ -78,9 +78,15 @@ typedef size_t usize; // Time extern int g_MonotonicTimeMS; -// Config +// Database Config extern char g_DatabaseFile[1024]; extern int g_MaxCachedStatements; + +// HostCache Config +extern int g_MaxCachedHostNames; +extern int g_HostNameExpireTime; + +// Connection Config extern int g_UpdateRate; extern int g_QueryManagerPort; extern char g_QueryManagerPassword[30]; @@ -934,6 +940,12 @@ bool CheckDatabaseSchema(void); bool InitDatabase(void); void ExitDatabase(void); +// hostcache.cc +//============================================================================== +bool InitHostCache(void); +void ExitHostCache(void); +bool ResolveHostName(const char *HostName, int *OutAddr); + // sha256.cc //============================================================================== void SHA256(const uint8 *Input, int InputBytes, uint8 *Digest); |
