Objectives
Host against which tests are to be executed will (in most cases) be given in configuration as human-readable domain names. Hereafter every test will need to resolve the host address using gethostbyname (or the reentrant version gethostbyname_r) C library function. The first reason for caching results from its execution is speed concern. Resolving the domain name every time a test is executed will lead to kernel overhead, which could possibly slow down other tasks (the watchcub itself). Secondly, failure to resolve the domain name should not lead to failure of the whole test.
Theory of Operation
The dns cache will provide some Resolve method, which will be a wrapper of the gethostbyname_r libc function. It will maintain a table where for every DomainName, HostAddress and ExpirationTimestamp will be written. When some plugin calls Resolve for some DomainName, the HostAddress (or 0 if not found) will be returned and after that in case the ExpirationTimestamp is already passed (< current timestamp) the DomainName will be enqued for resolving in some ResolveQueue. Meanwhile NUMBER_OF_RESOLVER_THREADS threads will wait for something to enter the queue. When this happens, one of them gets it out and resolves the IP, and in case resolving was successful one, updates the record in the cache table.
The Basic Algorithm
PROCEDURE Resolve( DomainName )
# check if someone didn't pass an IP address for resolving
IF IS_IP( DomainName ) THEN
RETURN STR_TO_IP( DomainName )
END IF
# get what's inside the cache table
(HostAddress, ExpirationTimestamp) = TABLE_LOOKUP( DNSCacheTable, DomainName )
# check if this entry should be Refreshed
IF ExpirationTimestamp < NOW() OR HostAddress = 0 THEN
# the HostAddress for this DomainName should be refreshed
LOCK( ResolveQueue )
ENQUEUE( ResolveQueue, DomainName )
UNLOCK( ResolveQueue )
END IF
RETURN HostAddress
END PROCEDURE
PROCEDURE ResolverThreadRoutine()
# spin an endless loop
WHILE ( TRUE )
SLEEP( CHECK_INTERVAL )
# check in the ResolveQueue and while there is something for resolving -- do resolve it
WHILE ( TRUE )
DomainName := NIL
LOCK( ResolveQueue )
IF NON_EMPTY( ResolveQueue ) THEN
DomainName := POP_FIRST( ResolveQueue )
END IF
UNLOCK( ResolveQueue )
IF NOT DomainName THEN
BREAK
END IF
(HostAddress, StatusCode) := gethostbyname_r( DomainName )
IF StatusCode = HOST_FOUND THEN
DNSCacheTable[DomainName] := ( HostAddress, NOW() + EXP_INTERVAL )
ELSE
DNSCacheTable[DomainName].ExpirationTimestamp += RETRY_INTERVAL
END IF
END WHILE
END WHILE
END PROCEDURE