aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-08-05 03:39:51 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-08-08 12:11:36 -0300
commit20ca1cf66123c110cc14aa452a840e5f62cd74ff (patch)
tree5ce43bf569d54e04b5d0fab3dc9ec0708d061529 /src
parent634554a0df8dfd35630f17b5fee44c54b7af73a5 (diff)
downloadquerymanager-20ca1cf66123c110cc14aa452a840e5f62cd74ff.tar.gz
querymanager-20ca1cf66123c110cc14aa452a840e5f62cd74ff.zip
store world hostname and resolve at runtime
Diffstat (limited to 'src')
-rw-r--r--src/database.cc31
-rw-r--r--src/hostcache.cc105
-rw-r--r--src/querymanager.cc19
-rw-r--r--src/querymanager.hh14
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);