aboutsummaryrefslogtreecommitdiff
path: root/src/hostcache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/hostcache.cc')
-rw-r--r--src/hostcache.cc105
1 files changed, 105 insertions, 0 deletions
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;
+ }
+}