aboutsummaryrefslogtreecommitdiff
path: root/src/hostcache.cc
blob: 5cbc2fafa926eb8ff5ecda113a7d9863b076f3d7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#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);
	g_CachedHostNames = (THostCacheEntry*)calloc(
			g_Config.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_Config.MaxCachedHostNames; i += 1){
		THostCacheEntry *Current = &g_CachedHostNames[i];

		if((g_MonotonicTimeMS - Current->ResolveTime) >= g_Config.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(!StringBufCopy(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;
	}
}