2020-09-30 17:12:32 +02:00

1399 lines
41 KiB
C++

127/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
rescache.cxx
Abstract:
Contains functions which manipulate hostent cache for winsock gethostbyObject calls
Contents:
InitializeHostentCache
TerminateHostentCache
QueryHostentCache
CacheHostent
FlushHostentCache
ReleaseHostentCacheEntry
ThrowOutHostentCacheEntry
(RemoveCacheEntry)
(ResolverCacheHit)
(HostentMatch)
(CreateCacheEntry)
(CompareHostentNames)
(BytesInHostent)
(CopyHostentToBuffer)
Author:
Richard L Firth (rfirth) 10-Jul-1994
Environment:
Win-16/32 user level
Revision History:
rfirth 10-Jul-1994
Created
--*/
// includes
#include "wininetp.h"
// private manifests
// private macros
#define SET_EXPIRATION_TIME(cacheEntry)
// private data
PRIVATE BOOL HostentCacheInitialized = FALSE;
// DnsCachingEnabled - caching is enabled by default
PRIVATE BOOL DnsCachingEnabled = TRUE;
// DnsCacheTimeout - number of seconds before a cache entry expires. This value
// is added to the current time (in seconds) to get the expiry time
PRIVATE DWORD DnsCacheTimeout = DEFAULT_DNS_CACHE_TIMEOUT;
// MaximumDnsCacheEntries - the maximum number of RESOLVER_CACHE_ENTRYs in the
// cache before we start throwing out the LRU
PRIVATE DWORD MaximumDnsCacheEntries = DEFAULT_DNS_CACHE_ENTRIES;
// CurrentDnsCacheEntries - the number of RESOLVER_CACHE_ENTRYs currently in the
// cache
PRIVATE DWORD CurrentDnsCacheEntries = 0;
// ResolverCache - serialized list of RESOLVER_CACHE_ENTRYs, kept in MRU order.
// We only need to remove the tail of the list to remove the LRU entry
PRIVATE SERIALIZED_LIST ResolverCache = {0};
// private prototypes
PRIVATE VOID RemoveCacheEntry(IN LPRESOLVER_CACHE_ENTRY lpCacheEntry);
PRIVATE BOOL ResolverCacheHit(IN LPRESOLVER_CACHE_ENTRY lpCacheEntry, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL);
PRIVATE BOOL HostentMatch(IN LPHOSTENT Hostent, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL);
PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry(IN LPSTR lpszHostName, IN LPHOSTENT Hostent, IN DWORD TimeToLive);
PRIVATE BOOL CompareHostentNames(IN LPHOSTENT Hostent, IN LPSTR Name);
PRIVATE DWORD BytesInHostent(IN LPHOSTENT Hostent);
PRIVATE DWORD CopyHostentToBuffer(OUT PCHAR Buffer, IN UINT BufferLength, IN PHOSTENT Hostent);
#if INET_DEBUG
PRIVATE DEBUG_FUNCTION LPSTR CacheTimestr(IN DWORD Time);
PRIVATE DEBUG_FUNCTION LPSTR CacheHostentStr(IN LPHOSTENT Hostent);
PRIVATE DEBUG_FUNCTION LPSTR CacheMapIpAddress(IN LPBYTE Address);
#endif
// functions
VOID InitializeHostentCache(VOID)
/*++
Routine Description:
Initializes the hostent cache:
* Initializes the cache list anchor
* loads the cache
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "InitializeHostentCache", NULL));
if (!(BOOL)InterlockedExchange((LPLONG)&HostentCacheInitialized, TRUE)) {
InternetReadRegistryDword("DnsCacheEnabled", (LPDWORD)&DnsCachingEnabled);
InternetReadRegistryDword("DnsCacheEntries", &MaximumDnsCacheEntries);
InternetReadRegistryDword("DnsCacheTimeout", &DnsCacheTimeout);
InitializeSerializedList(&ResolverCache);
// if the size of the cache in the registry is 0 then its the same as no caching
if (MaximumDnsCacheEntries == 0) {
DnsCachingEnabled = FALSE;
}
} else {// shouldn't be calling this more than once
INET_ASSERT(FALSE);
}
DEBUG_LEAVE(0);
}
VOID TerminateHostentCache(VOID)
/*++
Routine Description:
Free up all resources allocated by InitializeHostentCache()
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "TerminateHostentCache", NULL));
if (InterlockedExchange((LPLONG)&HostentCacheInitialized, FALSE)) {
DnsCachingEnabled = FALSE;// short-circuit any other cache attempts (shouldn't be any by now)
FlushHostentCache(); // and clear out the list
TerminateSerializedList(&ResolverCache);// we are done with the serialized list
}
DEBUG_LEAVE(0);
}
BOOL QueryHostentCache(IN LPSTR Name OPTIONAL,
IN LPBYTE Address OPTIONAL,
OUT LPHOSTENT* Hostent,
OUT LPDWORD TimeToLive
)
/*++
Routine Description:
Checks if Name is stored in the last resolved name cache.
If the entry is found, but has expired then it is removed from the cache
Arguments:
Name - pointer to name string
Address - pointer to IP address
Hostent - pointer to returned pointer to hostent
TimeToLive - pointer to returned time to live
Return Value:
BOOL
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Bool,
"QueryHostentCache",
"%q, %s, %#x, %#x",
Name,
CacheMapIpAddress(Address),
Hostent,
TimeToLive));
BOOL found;
if (!DnsCachingEnabled) {
DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n"));
*Hostent = NULL;
found = FALSE;
goto quit;
}
LockSerializedList(&ResolverCache);
LPRESOLVER_CACHE_ENTRY cacheEntry;
LPRESOLVER_CACHE_ENTRY previousEntry;
DWORD timeNow;
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache);
timeNow = (DWORD)time(NULL);
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) {
// on every cache lookup, purge any stale entries. LIVE_FOREVER means
// that we don't expect the entry's net address to expire, but it
// DOESN'T mean that we can't throw out the entry if its the LRU and
// we're at maximum cache capacity. We can't do this if the item is
// still in-use. In this case, we mark it stale
if ((cacheEntry->ExpirationTime != LIVE_FOREVER) && (cacheEntry->ExpirationTime <= timeNow)) {
// if reference count not zero then another thread is using
// this entry - mark as stale else delete it
if (cacheEntry->ReferenceCount != 0) {
INET_ASSERT(cacheEntry->State == ENTRY_IN_USE);
cacheEntry->State = ENTRY_DELETE;
} else {
// this entry is stale; throw it out
// "my hovercraft is full of eels"
DEBUG_PRINT(SOCKETS,
INFO,
("throwing out stale DNS entry %q, expiry = %s\n",
cacheEntry->Hostent.h_name,
CacheTimestr(cacheEntry->ExpirationTime)));
// BUGBUG - what happens if ExpirationTime == timeNow?
previousEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Blink;
RemoveCacheEntry(cacheEntry);
cacheEntry = previousEntry;
}
} else if (ResolverCacheHit(cacheEntry, Name, Address) &&
((cacheEntry->State == ENTRY_UNUSED) || (cacheEntry->State == ENTRY_IN_USE))) {
// we found the entry, and it still has time to live. Make it the
// head of the list (MRU first), set the state to in-use and increase the reference count
RemoveFromSerializedList(&ResolverCache, &cacheEntry->ListEntry);
InsertAtHeadOfSerializedList(&ResolverCache, &cacheEntry->ListEntry);
cacheEntry->State = ENTRY_IN_USE;
++cacheEntry->ReferenceCount;
*Hostent = &cacheEntry->Hostent;
found = TRUE;
DEBUG_PRINT(SOCKETS, INFO, ("entry found in DNS cache\n"));
goto done;
}
cacheEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink;
}
*Hostent = NULL;
found = FALSE;
DEBUG_PRINT(SOCKETS, INFO, ("didn't find entry in DNS cache\n"));
done:
UnlockSerializedList(&ResolverCache);
quit:
DEBUG_LEAVE(found);
return found;
}
VOID CacheHostent(IN LPSTR lpszHostName, IN LPHOSTENT Hostent, IN DWORD TimeToLive)
/*++
Routine Description:
Adds a hostent structure to the cache. Creates a new structure and links it
into the cache list, displacing the LRU entry if required. If we cannot
create the entry, no action is taken, no errors returned
Arguments:
lpszHostName - the name we originally requested be resolved. May be
different than the names returned by the resolver, e.g.
"proxy" => "proxy1.microsoft.com, proxy2.microsoft.com"
Hostent - pointer to hostent to add to cache
TimeToLive - amount of time this information has to live. Can be:
LIVE_FOREVER - don't timeout (but can be discarded)
LIVE_DEFAULT - use the default value
anything else - number of seconds to live
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "CacheHostent", "%q, %#x, %d", lpszHostName, Hostent, TimeToLive));
if (!DnsCachingEnabled) {
DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n"));
goto quit;
}
LockSerializedList(&ResolverCache);
// check that the entry is not already in the cache - 2 or more threads may
// have been simultaneously resolving the same name
LPHOSTENT lpHostent;
DWORD ttl;
INET_ASSERT(lpszHostName != NULL);
if (!QueryHostentCache(lpszHostName, NULL, &lpHostent, &ttl)) {
LPRESOLVER_CACHE_ENTRY cacheEntry;
// remove as many entries as we can beginning at the tail of the list.
// We try to remove enough to get the cache size back below the limit.
// This may consist of removing expired entries or entries marked as
// DELETE. If there are expired, in-use entries then we mark them as
// DELETE. This may result in the cache list growing until those threads
// which have referenced cache entries release them
cacheEntry = (LPRESOLVER_CACHE_ENTRY)TailOfSerializedList(&ResolverCache);
while ((CurrentDnsCacheEntries >= MaximumDnsCacheEntries)
&& (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache))) {
// cache has maximum entries: throw out the Least Recently Used (its
// the one at the back of the queue, ma'am) but only if no-one else is currently accessing it
if ((cacheEntry->State != ENTRY_IN_USE) && (cacheEntry->ReferenceCount == 0)) {
INET_ASSERT((cacheEntry->State == ENTRY_UNUSED) || (cacheEntry->State == ENTRY_DELETE));
DEBUG_PRINT(SOCKETS, INFO, ("throwing out LRU %q\n", cacheEntry->Hostent.h_name));
LPRESOLVER_CACHE_ENTRY nextEntry;
nextEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink;
RemoveCacheEntry(cacheEntry);
cacheEntry = nextEntry;
} else if (cacheEntry->State == ENTRY_IN_USE) {
// this entry needs to be freed when it is released
cacheEntry->State = ENTRY_DELETE;
}
cacheEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Blink;
}
// add the entry at the head of the queue - it is the Most Recently Used
// after all. If we fail to allocate memory, its no problem: it'll just
// take a little longer if this entry would have been hit before we needed to throw out another entry
if (cacheEntry = CreateCacheEntry(lpszHostName, Hostent, TimeToLive)) {
DEBUG_PRINT(SOCKETS,
INFO,
("caching %q, expiry = %s\n",
CacheHostentStr(&cacheEntry->Hostent),
CacheTimestr(cacheEntry->ExpirationTime)));
InsertAtHeadOfSerializedList(&ResolverCache, &cacheEntry->ListEntry);
++CurrentDnsCacheEntries;
}
} else {
// this entry is already in the cache. 2 or more threads must have been
// resolving the same name simultaneously. We just bump the expiration
// time to the more recent value
DEBUG_PRINT(SOCKETS, WARNING, ("found %q already in the cache!?\n", Hostent->h_name));
ReleaseHostentCacheEntry(lpHostent);
}
UnlockSerializedList(&ResolverCache);
quit:
DEBUG_LEAVE(0);
}
VOID FlushHostentCache(VOID)
/*++
Routine Description:
Removes all entries in DNS hostent cache
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "FlushHostentCache", NULL));
LPRESOLVER_CACHE_ENTRY cacheEntry;
LPRESOLVER_CACHE_ENTRY previousEntry;
LockSerializedList(&ResolverCache);
previousEntry = (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache);
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache);
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) {
if (cacheEntry->State == ENTRY_UNUSED) {
RemoveCacheEntry(cacheEntry);
} else {
DEBUG_PRINT(SOCKETS, WARNING, ("cache entry %#x (%q) still in-use\n", cacheEntry->HostName));
cacheEntry->State = ENTRY_DELETE;
previousEntry = cacheEntry;
}
cacheEntry = (LPRESOLVER_CACHE_ENTRY)previousEntry->ListEntry.Flink;
}
UnlockSerializedList(&ResolverCache);
DEBUG_LEAVE(0);
}
VOID ReleaseHostentCacheEntry(IN LPHOSTENT lpHostent)
/*++
Routine Description:
Either mark a hostent unused or if it is stale, delete it
Arguments:
lpHostent - pointer to hostent to free
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "ReleaseHostentCacheEntry", "%#x", lpHostent));
LPRESOLVER_CACHE_ENTRY cacheEntry = CONTAINING_RECORD(lpHostent, RESOLVER_CACHE_ENTRY, Hostent);
LockSerializedList(&ResolverCache);
// reference count should never go below zero!
INET_ASSERT(cacheEntry->ReferenceCount > 0);
if (--cacheEntry->ReferenceCount <= 0) {
// last releaser gets to decide what to do - mark unused or delete
if (cacheEntry->State == ENTRY_IN_USE) {
cacheEntry->State = ENTRY_UNUSED;
} else if (cacheEntry->State == ENTRY_DELETE) {
// entry is already stale - throw it out
RemoveCacheEntry(cacheEntry);
} else {
// unused? or bogus value? Someone changed state while refcount
// not zero?
INET_ASSERT((cacheEntry->State == ENTRY_IN_USE) || (cacheEntry->State == ENTRY_DELETE));
}
}
UnlockSerializedList(&ResolverCache);
DEBUG_LEAVE(0);
}
VOID ThrowOutHostentCacheEntry(IN LPHOSTENT lpHostent)
/*++
Routine Description:
Removes this entry from the DNS cache, based on the host name. We assume
that the entry came from the cache, so unless it has been already purged,
we should be able to throw it out
Arguments:
lpHostent - pointer to host entry containing details (name) of entry to throw out
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
None,
"ThrowOutHostentCacheEntry",
"%#x [%q]",
lpHostent,
lpHostent->h_name));
if (DnsCachingEnabled) {
LockSerializedList(&ResolverCache);
LPRESOLVER_CACHE_ENTRY cacheEntry;
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache);
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) {
if (HostentMatch(&cacheEntry->Hostent, lpHostent->h_name, NULL)) {
// if the entry is unused then we can delete it, else we have
// to leave it to the thread with the last reference
if (cacheEntry->State == ENTRY_UNUSED) {
RemoveCacheEntry(cacheEntry);
} else {
cacheEntry->State = ENTRY_DELETE;
}
break;
}
}
UnlockSerializedList(&ResolverCache);
} else {
DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n"));
}
DEBUG_LEAVE(0);
}
PRIVATE VOID RemoveCacheEntry(IN LPRESOLVER_CACHE_ENTRY lpCacheEntry)
/*++
Routine Description:
Takes a cache entry off the list and frees it
N.B.: This function must be called with the resolver cache serialized list already locked
Arguments:
lpCacheEntry - currently queued entry to remove
--*/
{
DEBUG_ENTER((DBG_SOCKETS, None, "RemoveCacheEntry", "%#x", lpCacheEntry));
RemoveFromSerializedList(&ResolverCache, &lpCacheEntry->ListEntry);
INET_ASSERT(lpCacheEntry->ReferenceCount == 0);
INET_ASSERT((lpCacheEntry->State == ENTRY_UNUSED) || (lpCacheEntry->State == ENTRY_DELETE));
DEBUG_PRINT(SOCKETS,
INFO,
("throwing out %q, expiry = %s\n",
CacheHostentStr(&lpCacheEntry->Hostent),
CacheTimestr(lpCacheEntry->ExpirationTime)));
lpCacheEntry = (LPRESOLVER_CACHE_ENTRY)FREE_MEMORY((HLOCAL)lpCacheEntry);
INET_ASSERT(lpCacheEntry == NULL);
--CurrentDnsCacheEntries;
DEBUG_PRINT(SOCKETS,
INFO,
("CurrentDnsCacheEntries = %d\n",
CurrentDnsCacheEntries));
INET_ASSERT((CurrentDnsCacheEntries >= 0) && (CurrentDnsCacheEntries <= MaximumDnsCacheEntries));
DEBUG_LEAVE(0);
}
PRIVATE BOOL ResolverCacheHit(IN LPRESOLVER_CACHE_ENTRY lpCacheEntry,
IN LPSTR Name OPTIONAL,
IN LPBYTE Address OPTIONAL
)
/*++
Routine Description:
Checks this RESOLVER_CACHE_ENTRY for a match with Name or Address. If Name,
can match with name or alias(es) in hostent, or with originally resolved name
Arguments:
lpCacheEntry - pointer to RESOLVER_CACHE_ENTRY to check
Name - optional name to check
Address - optional server address to check
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Bool,
"ResolverCacheHit",
"%#x, %q, %s",
lpCacheEntry,
Name,
CacheMapIpAddress(Address)));
BOOL found;
if ((Name != NULL)
&& (lpCacheEntry->HostName != NULL)
&& (lstrcmpi(lpCacheEntry->HostName, Name) == 0)) {
DEBUG_PRINT(SOCKETS, INFO, ("matched name %q\n", lpCacheEntry->HostName));
found = TRUE;
} else {
found = FALSE;
}
if (!found) {
found = HostentMatch(&lpCacheEntry->Hostent, Name, Address);
}
DEBUG_LEAVE(found);
return found;
}
PRIVATE BOOL HostentMatch(IN LPHOSTENT Hostent, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL)
/*++
Routine Description:
Compares a hostent structure for a match with a host name or address
Arguments:
Hostent - pointer to hostent to compare
Name - pointer to name string
Address - pointer to IP address in network byte order
--*/
{
DEBUG_ENTER((DBG_SOCKETS, Bool, "HostentMatch", "%#x, %q, %s", Hostent, Name, CacheMapIpAddress(Address)));
BOOL found;
if (Name) {
found = CompareHostentNames(Hostent, Name);
} else {
LPBYTE* addressList = (LPBYTE*)Hostent->h_addr_list;
LPBYTE address;
found = FALSE;
while (address = *addressList++) {
if (*(LPDWORD)address == *(LPDWORD)Address) {
DEBUG_PRINT(SOCKETS, INFO, ("matched %s\n", CacheMapIpAddress(address)));
found = TRUE;
break;
}
}
}
if (found) {
DEBUG_PRINT(SOCKETS, INFO, ("hostent = %q\n", CacheHostentStr(Hostent)));
}
DEBUG_LEAVE(found);
return found;
}
PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry(IN LPSTR lpszHostName,
IN LPHOSTENT Hostent,
IN DWORD TimeToLive
)
/*++
Routine Description:
Allocates a RESOLVER_CACHE_ENTRY and packs it with the hostent information and sets the ExpirationTime
Arguments:
lpszHostName - name we resolved
Hostent - pointer to hostent to add
TimeToLive - amount of time before this hostent expires
Return Value:
LPRESOLVER_CACHE_ENTRY
--*/
{
LPRESOLVER_CACHE_ENTRY cacheEntry;
UINT hostentSize;
// BytesInHostent gives us the size of the fixed and variable parts of the hostent structure
hostentSize = (UINT)BytesInHostent(Hostent);
INET_ASSERT(lpszHostName != NULL);
// only copy lpszHostName if it is different to the names in the hostent
UINT hostNameSize;
if (!CompareHostentNames(Hostent, lpszHostName)) {
hostNameSize = lstrlen(lpszHostName) + 1;
} else {
hostNameSize = 0;
}
// allocate space for the cache entry (take off the size of the fixed part
// of the hostent - BytesInHostent already accounted for it)
cacheEntry = (LPRESOLVER_CACHE_ENTRY)ALLOCATE_MEMORY(LMEM_FIXED,
sizeof(RESOLVER_CACHE_ENTRY)
- sizeof(HOSTENT)
+ hostentSize
+ hostNameSize);
if (cacheEntry != NULL) {
CopyHostentToBuffer((PCHAR)&cacheEntry->Hostent, hostentSize, Hostent);
// copy the host name to the end of the buffer if required
if (hostNameSize != 0) {
cacheEntry->HostName = (LPSTR)&cacheEntry->Hostent + hostentSize;
RtlCopyMemory(cacheEntry->HostName, lpszHostName, hostNameSize);
} else {
cacheEntry->HostName = NULL;
}
// calculate the expiration time as the current time (in seconds since
// 1/1/70) + number of seconds to live OR indefinite if TimeToLive is
// specified as LIVE_FOREVER, which is what we use if the host
// information didn't originate from DNS
cacheEntry->ExpirationTime = (TimeToLive == LIVE_FOREVER)
? LIVE_FOREVER
: time(NULL) + ((TimeToLive == LIVE_DEFAULT) ? DnsCacheTimeout : TimeToLive);
cacheEntry->State = ENTRY_UNUSED;// the entry state is initially unused
cacheEntry->ReferenceCount = 0;// and reference is zero
}
return cacheEntry;
}
PRIVATE BOOL CompareHostentNames(IN LPHOSTENT Hostent, IN LPSTR Name)
/*++
Routine Description:
Compares a prospective host name against all names in a hostent
Arguments:
Hostent - pointer to hostent containing names to compare
Name - prospective host name
--*/
{
DEBUG_ENTER((DBG_SOCKETS, Bool, "CompareHostentNames", "%#x, %q", Hostent, Name));
BOOL found;
if (!lstrcmpi(Hostent->h_name, Name)) {
DEBUG_PRINT(SOCKETS, INFO, ("matched name %q\n", Hostent->h_name));
found = TRUE;
goto done;
}
LPSTR alias;
LPSTR* aliasList;
aliasList = Hostent->h_aliases;
while (alias = *aliasList++) {
if (!lstrcmpi(alias, Name)) {
DEBUG_PRINT(SOCKETS, INFO, ("matched alias %q\n", alias));
found = TRUE;
goto done;
}
}
DEBUG_PRINT(SOCKETS, WARNING, ("%q not matched\n", Name));
found = FALSE;
done:
DEBUG_LEAVE(found);
return found;
}
PRIVATE DWORD BytesInHostent(IN LPHOSTENT Hostent)
{
DWORD total;
int i;
total = sizeof(HOSTENT);
total += lstrlen(Hostent->h_name) + 1;
// Account for the NULL terminator pointers at the end of the alias and address arrays.
total += sizeof(char*) + sizeof(char*);
for (i = 0; Hostent->h_aliases[i] != NULL; i++) {
total += lstrlen(Hostent->h_aliases[i]) + 1 + sizeof(char*);
}
for (i = 0; Hostent->h_addr_list[i] != NULL; i++) {
total += Hostent->h_length + sizeof(char*);
}
// Pad the answer to an eight-byte boundary.
return (total + 7) & ~7;
}
PRIVATE DWORD CopyHostentToBuffer(OUT PCHAR Buffer, IN UINT BufferLength, IN LPHOSTENT Hostent)
{
UINT requiredBufferLength;
UINT bytesFilled;
PCHAR currentLocation = Buffer;
UINT aliasCount;
UINT addressCount;
UINT i;
PHOSTENT outputHostent = (PHOSTENT)Buffer;
// Determine how many bytes are needed to fully copy the structure.
requiredBufferLength = (UINT)BytesInHostent(Hostent);
// Zero the user buffer.
if ((DWORD)BufferLength > requiredBufferLength) {
RtlZeroMemory(Buffer, requiredBufferLength);
} else {
RtlZeroMemory(Buffer, BufferLength);
}
// Copy over the hostent structure if it fits.
bytesFilled = sizeof(*Hostent);
if (bytesFilled > (DWORD)BufferLength) {
return requiredBufferLength;
}
RtlCopyMemory(currentLocation, Hostent, sizeof(*Hostent));
currentLocation = Buffer + bytesFilled;
outputHostent->h_name = NULL;
outputHostent->h_aliases = NULL;
outputHostent->h_addr_list = NULL;
// Count the host's aliases and set up an array to hold pointers to them.
for (aliasCount = 0; Hostent->h_aliases[aliasCount] != NULL; aliasCount++);
bytesFilled += (aliasCount + 1) * sizeof(char FAR*);
if (bytesFilled > (DWORD)BufferLength) {
Hostent->h_aliases = NULL;
return requiredBufferLength;
}
outputHostent->h_aliases = (char FAR * FAR*)currentLocation;
currentLocation = Buffer + bytesFilled;
// Count the host's addresses and set up an array to hold pointers to them.
for (addressCount = 0; Hostent->h_addr_list[addressCount] != NULL; addressCount++);
bytesFilled += (addressCount + 1) * sizeof(void FAR*);
if (bytesFilled > (DWORD)BufferLength) {
Hostent->h_addr_list = NULL;
return requiredBufferLength;
}
outputHostent->h_addr_list = (char FAR * FAR*)currentLocation;
currentLocation = Buffer + bytesFilled;
// Start filling in addresses. Do addresses before filling in the
// host name and aliases in order to avoid alignment problems.
for (i = 0; i < addressCount; i++) {
bytesFilled += Hostent->h_length;
if (bytesFilled > (DWORD)BufferLength) {
outputHostent->h_addr_list[i] = NULL;
return requiredBufferLength;
}
outputHostent->h_addr_list[i] = currentLocation;
RtlCopyMemory(currentLocation, Hostent->h_addr_list[i], Hostent->h_length);
currentLocation = Buffer + bytesFilled;
}
outputHostent->h_addr_list[i] = NULL;
// Copy the host name if it fits.
bytesFilled += lstrlen(Hostent->h_name) + 1;
if (bytesFilled > (DWORD)BufferLength) {
return requiredBufferLength;
}
outputHostent->h_name = currentLocation;
RtlCopyMemory(currentLocation, Hostent->h_name, lstrlen(Hostent->h_name) + 1);
currentLocation = Buffer + bytesFilled;
// Start filling in aliases.
for (i = 0; i < aliasCount; i++) {
bytesFilled += lstrlen(Hostent->h_aliases[i]) + 1;
if (bytesFilled > (DWORD)BufferLength) {
outputHostent->h_aliases[i] = NULL;
return requiredBufferLength;
}
outputHostent->h_aliases[i] = currentLocation;
RtlCopyMemory(currentLocation, Hostent->h_aliases[i], lstrlen(Hostent->h_aliases[i]) + 1);
currentLocation = Buffer + bytesFilled;
}
outputHostent->h_aliases[i] = NULL;
return requiredBufferLength;
}
#if INET_DEBUG
// CAVEAT - can only call these functions once per printf() etc. because of
// static buffers (but still thread-safe)
PRIVATE DEBUG_FUNCTION LPSTR CacheTimestr(IN DWORD Time)
{
// previous code - writes formatted human-sensible date/time to buffer
//LPSTR p;
// remove the LF from the time string returned by ctime()
//p = ctime((const time_t *)&Time);
//p[strlen(p) - 1] = '\0';
//return p;
// abbreviated CRT version - just write # seconds since 1970 to buffer
static char buf[16];
wsprintf(buf, "%d", Time);
return (LPSTR)buf;
}
PRIVATE DEBUG_FUNCTION LPSTR CacheHostentStr(IN LPHOSTENT Hostent)
{
static char buf[1024];
LPSTR p;
p = buf;
p += wsprintf(p, "n=%s", Hostent->h_name);
for (LPSTR* aliases = (LPSTR*)Hostent->h_aliases; *aliases; ++aliases) {
p += wsprintf(p, ", a=%s", *aliases);
}
for (LPBYTE* addrs = (LPBYTE*)Hostent->h_addr_list; *addrs; ++addrs) {
p += wsprintf(p, ", i=%s", CacheMapIpAddress(*addrs));
}
return (LPSTR)buf;
}
PRIVATE DEBUG_FUNCTION LPSTR CacheMapIpAddress(IN LPBYTE Address)
{
if (!Address) {
return "";
}
static char buf[16];
wsprintf(buf, "%d.%d.%d.%d", Address[0] & 0xff, Address[1] & 0xff, Address[2] & 0xff, Address[3] & 0xff);
return (LPSTR)buf;
}
#endif
#if defined(RNR_SUPPORTED)
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
rescache.c
Abstract:
Contains name resolution cache
Contents:
Author:
Shishir Pardikar 2-14-96
Environment:
Win32 user mode
Revision History:
2-14-96 shishirp
Created
--*/
//BUGBUG: This include should be removed, duplicate of above
#ifndef SPX_SUPPORT
#include <wininetp.h>
#endif
// private manifests
#define NAMERES_CACHE_USED 0x00000001
#define NAMERES_CACHE_USES_GUID 0x00000002
#define ENTERCRIT_NAMERESCACHE() EnterCriticalSection(&vcritNameresCache)
#define LEAVECRIT_NAMERESCACHE() LeaveCriticalSection(&vcritNameresCache)
#define IS_EMPTY(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USED) == 0)
#define USES_GUID(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USES_GUID))
#define DEFAULT_NAMERES_CACHE_ENTRIES 10// number of cache entries
// expiry time for an addresslist
#define DEFAULT_EXPIRY_DELTA (24 * 60 * 60 * (LONGLONG)10000000)
// structure definition
typedef struct tagNAMERES_CACHE {
DWORD dwFlags; // general flags to be used as needed
DWORD dwNameSpace; // namespace ??
GUID sGuid; // GUID describing service type
LPSTR lpszName; // ptr to name that needs resolution
FILETIME ftLastUsedTime;// last accesstime, mainly for purging
FILETIME ftCreationTime;// When it was created
ADDRESS_INFO_LIST sAddrList; // List of address (defined in ixport.h)
} NAMERES_CACHE, far* LPNAMERES_CACHE;
// private variables for name resolution cache
// Name cache size allocated in init
LPNAMERES_CACHE vlpNameresCache = NULL;
// Number of elements allowed in the nameres cache
int vcntNameresCacheEntries = DEFAULT_NAMERES_CACHE_ENTRIES;
// time in 100ns after which an address is expired
LONGLONG vftExpiryDelta = DEFAULT_EXPIRY_DELTA;
BOOL vfNameresCacheInited = FALSE;
CRITICAL_SECTION vcritNameresCache;// serialization
// private function prototypes
PRIVATE
DWORD
CreateNameresCacheEntry(
int indx,
DWORD dwNameSpace,
LPGUID lpGuid,
LPSTR lpszName,
INT cntAddresses,
LPCSADDR_INFO lpCsaddrInfo
);
PRIVATE DWORD DeleteNameresCacheEntry(int indx);
PRIVATE int FindNameresCacheEntry(DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName);
PRIVATE int FindNameresCacheEntryByAddr(int cntAddr, LPCSADDR_INFO lpCsaddrInfo);
PRIVATE int PurgeEntries(BOOL fForce // purge atleast one entry
);
PRIVATE DWORD CopyCsaddr(LPCSADDR_INFO lpSrc, int cntAddr, LPCSADDR_INFO* lplpDst);
// functions
DWORD InitNameresCache(VOID)
/*++
Routine Description:
Init name resolution cache. This routine a) allocates a table of name cache entries b)
Arguments:
None.
Return Value:
ERROR_SUCCESS if successful.
--*/
{
if (vfNameresCacheInited) {
return (ERROR_SUCCESS);
}
// first try to alloc the memory, if it fails just quit
vlpNameresCache = (LPNAMERES_CACHE)ALLOCATE_MEMORY(LPTR, vcntNameresCacheEntries * sizeof(NAMERES_CACHE));
if (!vlpNameresCache) {
return (ERROR_NOT_ENOUGH_MEMORY);
}
InitializeCriticalSection(&vcritNameresCache);
ENTERCRIT_NAMERESCACHE();
vfNameresCacheInited = TRUE;
LEAVECRIT_NAMERESCACHE();
return (ERROR_SUCCESS);
}
DWORD
AddNameresCacheEntry(
DWORD dwNameSpace,
LPGUID lpGuid,
LPSTR lpName,
int cntAddresses,
LPCSADDR_INFO lpCsaddrInfo
)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int indx;
DWORD dwError = ERROR_SUCCESS;
if (!vfNameresCacheInited) {
return (ERROR_INVALID_PARAMETER);
}
ENTERCRIT_NAMERESCACHE();
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpName);
// if indx is valid, delete the entry, do some purging too
if (indx != -1) {
DeleteNameresCacheEntry(indx);
PurgeEntries(FALSE);
} else {
// create atleast one hole
indx = PurgeEntries(TRUE);
}
INET_ASSERT((indx >= 0 && (indx < vcntNameresCacheEntries)));
dwError = CreateNameresCacheEntry(indx, dwNameSpace, lpGuid, lpName, cntAddresses, lpCsaddrInfo);
LEAVECRIT_NAMERESCACHE();
return (dwError);
}
DWORD RemoveNameresCacheEntry(DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int indx;
DWORD dwError = ERROR_INVALID_PARAMETER;
if (vfNameresCacheInited) {
ENTERCRIT_NAMERESCACHE();
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpszName);
if (indx != -1) {
DeleteNameresCacheEntry(indx);
dwError = ERROR_SUCCESS;
} else {
dwError = ERROR_FILE_NOT_FOUND; //yuk
}
LEAVECRIT_NAMERESCACHE();
}
return (dwError);
}
#ifdef MAYBE
DWORD RemoveNameresCacheEntryByAddr(int cntAddresses, LPCSADDR_INFO lpCsaddrInfo)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int indx;
DWORD dwError = ERROR_INVALID_PARAMETER;
if (vfNameresCacheInited) {
ENTERCRIT_NAMERESCACHE();
indx = FindNameresCacheEntryByAddr(cntAddresses, lpCsaddrInfo);
if (indx != -1) {
DeleteNameresCacheEntry(indx);
dwError = ERROR_SUCCESS;
} else {
dwError = ERROR_FILE_NOT_FOUND;
}
LEAVECRIT_NAMERESCACHE();
}
return (dwError);
}
#endif //MAYBE
DWORD
GetNameresCacheEntry(
DWORD dwNameSpace,
LPGUID lpGuid,
LPSTR lpName,
INT* lpcntAddresses,
LPCSADDR_INFO* lplpCsaddrInfo
)
/*++
Routine Description:
This routine looks up the cache and returns the list of addresses corresponding to lpGuid/lpName.
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int indx;
DWORD dwError = ERROR_FILE_NOT_FOUND; // lame error
if (!vfNameresCacheInited) {
return (ERROR_INVALID_PARAMETER);
}
ENTERCRIT_NAMERESCACHE();
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpName);// is this entry already cached?
if (indx != -1) {
// yes, let use give back the info
*lpcntAddresses = vlpNameresCache[indx].sAddrList.AddressCount;
if ((dwError = CopyCsaddr(vlpNameresCache[indx].sAddrList.Addresses, *lpcntAddresses, lplpCsaddrInfo))
!= ERROR_SUCCESS) {
goto bailout;
}
// update the last used time, we will use this to age out the entries
GetCurrentGmtTime(&(vlpNameresCache[indx].ftLastUsedTime));
dwError = ERROR_SUCCESS;
}
bailout:
LEAVECRIT_NAMERESCACHE();
return (dwError);
}
DWORD DeinitNameresCache(VOID)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int i;
if (vfNameresCacheInited) {
ENTERCRIT_NAMERESCACHE();
for (i = 0; i < vcntNameresCacheEntries; ++i) {
if (!IS_EMPTY(i)) {
DeleteNameresCacheEntry(i);
}
}
FREE_MEMORY(vlpNameresCache);
vlpNameresCache = NULL;
vfNameresCacheInited = FALSE;
LEAVECRIT_NAMERESCACHE();
DeleteCriticalSection(&vcritNameresCache);
}
return (ERROR_SUCCESS);
}
PRIVATE
DWORD
CreateNameresCacheEntry(
int indx,
DWORD dwNameSpace,
LPGUID lpGuid,
LPSTR lpszName,
int cntAddresses,
LPCSADDR_INFO lpCsaddrInfo
)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
DWORD dwError = ERROR_NOT_ENOUGH_MEMORY;
INET_ASSERT((indx >= 0 && (indx < vcntNameresCacheEntries)));
INET_ASSERT(IS_EMPTY(indx));
memset(&vlpNameresCache[indx], 0, sizeof(vlpNameresCache[indx]));
// we could get a name or a guid
// do it for name first before doing it for GUID
// BUGBUG in future we should consider name+GUID+port
if (lpszName) {
vlpNameresCache[indx].lpszName = (LPSTR)ALLOCATE_MEMORY(LPTR, lstrlen(lpszName) + 1);
if (!vlpNameresCache[indx].lpszName) {
goto bailout;
}
strcpy(vlpNameresCache[indx].lpszName, lpszName);
} else if (lpGuid) {
INET_ASSERT(FALSE); // rigth now. In future this should go away
memcpy(&(vlpNameresCache[indx].sGuid), lpGuid, sizeof(GUID));
vlpNameresCache[indx].dwFlags |= NAMERES_CACHE_USES_GUID;
} else {
dwError = ERROR_INVALID_PARAMETER;
goto bailout;
}
INET_ASSERT(cntAddresses > 0);
if (CopyCsaddr(lpCsaddrInfo, cntAddresses, &(vlpNameresCache[indx].sAddrList.Addresses))
!= ERROR_SUCCESS) {
goto bailout;
}
vlpNameresCache[indx].sAddrList.AddressCount = cntAddresses;
vlpNameresCache[indx].dwFlags |= NAMERES_CACHE_USED;// mark this as being non-empty
// set the creation and last-used times as now
GetCurrentGmtTime(&(vlpNameresCache[indx].ftCreationTime));
vlpNameresCache[indx].ftLastUsedTime = vlpNameresCache[indx].ftCreationTime;
dwError = ERROR_SUCCESS;
bailout:
if (dwError != ERROR_SUCCESS) {
if (vlpNameresCache[indx].sAddrList.Addresses) {
FREE_MEMORY(vlpNameresCache[indx].sAddrList.Addresses);
vlpNameresCache[indx].sAddrList.Addresses = NULL;
}
if (vlpNameresCache[indx].lpszName) {
FREE_MEMORY(vlpNameresCache[indx].lpszName);
vlpNameresCache[indx].lpszName = NULL;
}
memset(&vlpNameresCache[indx], 0, sizeof(vlpNameresCache[indx]));
}
return (dwError);
}
PRIVATE DWORD DeleteNameresCacheEntry(int indx)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
INET_ASSERT((indx >= 0) && (indx < vcntNameresCacheEntries));
if (vlpNameresCache[indx].lpszName) {
FREE_MEMORY(vlpNameresCache[indx].lpszName);
}
INET_ASSERT(vlpNameresCache[indx].sAddrList.Addresses);
FREE_MEMORY(vlpNameresCache[indx].sAddrList.Addresses);
memset(&vlpNameresCache[indx], 0, sizeof(NAMERES_CACHE));
return (ERROR_SUCCESS);
}
#ifdef MAYBE
PRIVATE int FindNameresCacheEntryByAddr(int cntAddr, LPCSADDR_INFO lpCsaddrInfo)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int i;
for (i = 0; i < vcntNameresCacheEntries; ++i) {
if (!IS_EMPTY(i) && // not empty
(vlpNameresCache[i].sAddrList.AddressCount == cntAddr) && // count is the same
(!memcmp(vlpNameresCache[i].sAddrList.Addresses, // list matches
lpCsaddrInfo,
cntAddr * sizeof(CSADDR_INFO)))) {
return (i);
}
}
return (-1);
}
#endif //MAYBE
PRIVATE int FindNameresCacheEntry(DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName)
/*++
Return Value:
ERROR_SUCCESS if successful.
--*/
{
int i;
for (i = 0; i < vcntNameresCacheEntries; ++i) {
if (!IS_EMPTY(i)) {
if (vlpNameresCache[i].dwNameSpace == dwNameSpace) {
if (!USES_GUID(i)) {
INET_ASSERT(vlpNameresCache[i].lpszName);
if (lpszName && !lstrcmpi(lpszName, vlpNameresCache[i].lpszName)) {
return (i);
}
} else {
if (lpGuid && !memcmp(lpGuid, &vlpNameresCache[i].sGuid, sizeof(GUID))) {
return (i);
}
}
}
}
}
return (-1);
}
PRIVATE int PurgeEntries(BOOL fForce // purge atleast one entry
)
/*++
Return Value:
index of a free entry
--*/
{
int i, indxlru = -1, indxHole = -1;
FILETIME ft;
BOOL fFoundHole = FALSE;
GetCurrentGmtTime(&ft);
for (i = 0; i < vcntNameresCacheEntries; ++i) {
if (!IS_EMPTY(i)) {
// purge stale entries
if ((FT2LL(ft) - FT2LL(vlpNameresCache[i].ftCreationTime)) > FT2LL(vftExpiryDelta)) {
DeleteNameresCacheEntry(i);
indxHole = i;
} else if (FT2LL(vlpNameresCache[i].ftLastUsedTime) <= FT2LL(ft)) {
ft = vlpNameresCache[i].ftLastUsedTime;
indxlru = i; // LRU entry if we need to purge it
}
} else {
indxHole = i;
}
}
// if there is no hole, purge the LRU entry if forced
if (indxHole == -1) {
INET_ASSERT(indxlru != -1);
if (fForce) {
DeleteNameresCacheEntry(indxlru);
indxHole = indxlru;
}
}
return (indxHole);
}
PRIVATE DWORD CopyCsaddr(LPCSADDR_INFO lpSrc, int cntAddr, LPCSADDR_INFO* lplpDst)
{
int i;
LPCSADDR_INFO lpDst;
UINT uSize;
// BUGBUG assumes the way Compressaddress (ixport.cxx) allocates memory
uSize = LocalSize(lpSrc);
if (!uSize) {
return (GetLastError());
}
*lplpDst = (LPCSADDR_INFO)ALLOCATE_MEMORY(LPTR, uSize);
if (!*lplpDst) {
return (ERROR_NOT_ENOUGH_MEMORY);
}
lpDst = *lplpDst;
memcpy(lpDst, lpSrc, uSize);
// now start doing fixups
for (i = 0; i < cntAddr; ++i) {
lpDst[i].LocalAddr.lpSockaddr = (LPSOCKADDR)((LPBYTE)lpDst + ((DWORD)(lpSrc[i].LocalAddr.lpSockaddr) - (DWORD)lpSrc));
lpDst[i].RemoteAddr.lpSockaddr = (LPSOCKADDR)((LPBYTE)lpDst + ((DWORD)(lpSrc[i].RemoteAddr.lpSockaddr) - (DWORD)lpSrc));
}
return (ERROR_SUCCESS);
}
#endif // defined(RNR_SUPPORTED)