You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2223 lines
49 KiB
2223 lines
49 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rescache.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains functions which manipulate hostent cache for winsock gethostbyObject
|
|
calls
|
|
|
|
Contents:
|
|
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 LONG MaximumDnsCacheEntries = DEFAULT_DNS_CACHE_ENTRIES;
|
|
|
|
//
|
|
// 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 prototypes
|
|
//
|
|
|
|
PRIVATE
|
|
VOID
|
|
RemoveCacheEntry(
|
|
IN SERIALIZED_LIST* pResolverCache,
|
|
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,
|
|
IN VOID** pAlloc=NULL,
|
|
IN DWORD dwAllocSize=0
|
|
);
|
|
|
|
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
|
|
//
|
|
|
|
|
|
BOOL
|
|
QueryHostentCache(
|
|
SERIALIZED_LIST* pResolverCache,
|
|
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 || !LockSerializedList(pResolverCache))
|
|
{
|
|
DEBUG_PRINT(SOCKETS,
|
|
WARNING,
|
|
("DNS caching disabled or unable to serialize access\n"
|
|
));
|
|
|
|
*Hostent = NULL;
|
|
found = FALSE;
|
|
goto quit;
|
|
}
|
|
|
|
LPRESOLVER_CACHE_ENTRY cacheEntry;
|
|
LPRESOLVER_CACHE_ENTRY previousEntry;
|
|
DWORD timeNow;
|
|
|
|
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache);
|
|
timeNow = (DWORD)time(NULL);
|
|
|
|
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) {
|
|
|
|
//
|
|
// 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(pResolverCache, 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
|
|
//
|
|
|
|
if (RemoveFromSerializedList(pResolverCache, &cacheEntry->ListEntry))
|
|
{
|
|
if (InsertAtHeadOfSerializedList(pResolverCache, &cacheEntry->ListEntry))
|
|
{
|
|
cacheEntry->State = ENTRY_IN_USE;
|
|
++cacheEntry->ReferenceCount;
|
|
*Hostent = &cacheEntry->Hostent;
|
|
found = TRUE;
|
|
|
|
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("entry found in DNS cache\n"
|
|
));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("entry found in DNS cache, but removed due to not enough memory\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(pResolverCache);
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(found);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
VOID
|
|
CacheHostent(
|
|
SERIALIZED_LIST* pResolverCache,
|
|
IN LPSTR lpszHostName,
|
|
IN LPHOSTENT Hostent,
|
|
IN DWORD TimeToLive,
|
|
IN VOID** pAlloc,
|
|
IN DWORD dwAllocSize
|
|
)
|
|
|
|
/*++
|
|
|
|
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
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
None,
|
|
"CacheHostent",
|
|
"%q, %#x, %d",
|
|
lpszHostName,
|
|
Hostent,
|
|
TimeToLive
|
|
));
|
|
|
|
if (!DnsCachingEnabled || !LockSerializedList(pResolverCache))
|
|
{
|
|
DEBUG_PRINT(SOCKETS,
|
|
WARNING,
|
|
("DNS caching disabled or unable to serialize access\n"
|
|
));
|
|
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// 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(pResolverCache, 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(pResolverCache);
|
|
|
|
while ((pResolverCache->ElementCount >= MaximumDnsCacheEntries)
|
|
&& (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache))) {
|
|
|
|
//
|
|
// 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(pResolverCache, 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, pAlloc, dwAllocSize)) {
|
|
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("caching %q, expiry = %s\n",
|
|
CacheHostentStr(&cacheEntry->Hostent),
|
|
CacheTimestr(cacheEntry->ExpirationTime)
|
|
));
|
|
|
|
if (!InsertAtHeadOfSerializedList(pResolverCache, &cacheEntry->ListEntry))
|
|
{
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("Unable to cache new entry due to not enough memory\n"
|
|
));
|
|
}
|
|
}
|
|
} 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(pResolverCache, lpHostent);
|
|
|
|
}
|
|
|
|
UnlockSerializedList(pResolverCache);
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
VOID
|
|
FlushHostentCache(
|
|
SERIALIZED_LIST* pResolverCache
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes all entries in DNS hostent cache
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
None,
|
|
"FlushHostentCache",
|
|
NULL
|
|
));
|
|
|
|
LPRESOLVER_CACHE_ENTRY cacheEntry;
|
|
LPRESOLVER_CACHE_ENTRY previousEntry;
|
|
|
|
if (LockSerializedList(pResolverCache))
|
|
{
|
|
previousEntry = (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache);
|
|
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache);
|
|
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) {
|
|
if (cacheEntry->State == ENTRY_UNUSED) {
|
|
RemoveCacheEntry(pResolverCache, 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(pResolverCache);
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
VOID
|
|
ReleaseHostentCacheEntry(
|
|
SERIALIZED_LIST* pResolverCache,
|
|
IN LPHOSTENT lpHostent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Either mark a hostent unused or if it is stale, delete it
|
|
|
|
Arguments:
|
|
|
|
lpHostent - pointer to hostent to free
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
None,
|
|
"ReleaseHostentCacheEntry",
|
|
"%#x",
|
|
lpHostent
|
|
));
|
|
|
|
LPRESOLVER_CACHE_ENTRY cacheEntry = CONTAINING_RECORD(lpHostent,
|
|
RESOLVER_CACHE_ENTRY,
|
|
Hostent
|
|
);
|
|
|
|
if (LockSerializedList(pResolverCache))
|
|
{
|
|
|
|
//
|
|
// 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(pResolverCache, 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(pResolverCache);
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
VOID
|
|
ThrowOutHostentCacheEntry(
|
|
SERIALIZED_LIST* pResolverCache,
|
|
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
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
None,
|
|
"ThrowOutHostentCacheEntry",
|
|
"%#x [%q]",
|
|
lpHostent,
|
|
lpHostent->h_name
|
|
));
|
|
|
|
if (DnsCachingEnabled && LockSerializedList(pResolverCache)) {
|
|
|
|
LPRESOLVER_CACHE_ENTRY cacheEntry;
|
|
|
|
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache);
|
|
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) {
|
|
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(pResolverCache, cacheEntry);
|
|
} else {
|
|
cacheEntry->State = ENTRY_DELETE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
UnlockSerializedList(pResolverCache);
|
|
} else {
|
|
|
|
DEBUG_PRINT(SOCKETS,
|
|
WARNING,
|
|
("DNS caching disabled or unable to serialize access\n"
|
|
));
|
|
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
VOID
|
|
RemoveCacheEntry(
|
|
SERIALIZED_LIST* pResolverCache,
|
|
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
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_SOCKETS,
|
|
None,
|
|
"RemoveCacheEntry",
|
|
"%#x",
|
|
lpCacheEntry
|
|
));
|
|
|
|
if (RemoveFromSerializedList(pResolverCache, &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);
|
|
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("CurrentDnsCacheEntries = %d\n",
|
|
pResolverCache->ElementCount
|
|
));
|
|
|
|
INET_ASSERT((pResolverCache->ElementCount >= 0)
|
|
&& (pResolverCache->ElementCount <= MaximumDnsCacheEntries));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_PRINT(SOCKETS,
|
|
INFO,
|
|
("unable to throw out %q due to insufficient resources, expiry = %s\n",
|
|
CacheHostentStr(&lpCacheEntry->Hostent),
|
|
CacheTimestr(lpCacheEntry->ExpirationTime)
|
|
));
|
|
}
|
|
|
|
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
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
|
|
{
|
|
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
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
|
|
{
|
|
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,
|
|
IN VOID** pAlloc,
|
|
IN DWORD dwAllocSize
|
|
)
|
|
|
|
/*++
|
|
|
|
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)
|
|
//
|
|
|
|
DWORD dwSize = sizeof(RESOLVER_CACHE_ENTRY)
|
|
- sizeof(HOSTENT)
|
|
+ hostentSize
|
|
+ hostNameSize;
|
|
|
|
if (dwSize <= dwAllocSize)
|
|
{
|
|
cacheEntry = (LPRESOLVER_CACHE_ENTRY)(*pAlloc);
|
|
*pAlloc = NULL;
|
|
}
|
|
else
|
|
{
|
|
cacheEntry = (LPRESOLVER_CACHE_ENTRY)ALLOCATE_MEMORY(LMEM_FIXED,
|
|
dwSize
|
|
);
|
|
}
|
|
|
|
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 = (DWORD)((TimeToLive == LIVE_FOREVER)
|
|
? LIVE_FOREVER
|
|
: time(NULL)
|
|
+ ((TimeToLive == LIVE_DEFAULT)
|
|
? DnsCacheTimeout
|
|
: TimeToLive));
|
|
|
|
//
|
|
// the entry state is initially unused
|
|
//
|
|
|
|
cacheEntry->State = ENTRY_UNUSED;
|
|
|
|
//
|
|
// and reference is zero
|
|
//
|
|
|
|
cacheEntry->ReferenceCount = 0;
|
|
}
|
|
|
|
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
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
|
|
{
|
|
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() (vcritNameresCache.Lock())
|
|
#define LEAVECRIT_NAMERESCACHE() (vcritNameresCache.Unlock())
|
|
#define IS_EMPTY(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USED) == 0)
|
|
#define USES_GUID(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USES_GUID))
|
|
|
|
// number of cache entries
|
|
#define DEFAULT_NAMERES_CACHE_ENTRIES 10
|
|
|
|
// 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;
|
|
|
|
// serialization
|
|
CCritSec vcritNameresCache;
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
if (!vcritNameresCache.Init() || !ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
FREE_MEMORY(vlpNameresCache);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
vfNameresCacheInited = TRUE;
|
|
|
|
LEAVECRIT_NAMERESCACHE();
|
|
|
|
return (ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
AddNameresCacheEntry(
|
|
DWORD dwNameSpace,
|
|
LPGUID lpGuid,
|
|
LPSTR lpName,
|
|
int cntAddresses,
|
|
LPCSADDR_INFO lpCsaddrInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
if (!vfNameresCacheInited) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (!ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
DWORD dwError = ERROR_INVALID_PARAMETER;
|
|
|
|
if (vfNameresCacheInited) {
|
|
|
|
if (!ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpszName);
|
|
|
|
if (indx != -1) {
|
|
|
|
DeleteNameresCacheEntry(indx);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
dwError = ERROR_FILE_NOT_FOUND; //yuk
|
|
}
|
|
|
|
LEAVECRIT_NAMERESCACHE();
|
|
}
|
|
quit:
|
|
return (dwError);
|
|
}
|
|
|
|
|
|
#ifdef MAYBE
|
|
|
|
DWORD
|
|
RemoveNameresCacheEntryByAddr(
|
|
int cntAddresses,
|
|
LPCSADDR_INFO lpCsaddrInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
DWORD dwError = ERROR_INVALID_PARAMETER;
|
|
|
|
if (vfNameresCacheInited) {
|
|
if (!ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
|
|
indx = FindNameresCacheEntryByAddr(cntAddresses, lpCsaddrInfo);
|
|
|
|
if (indx != -1) {
|
|
|
|
DeleteNameresCacheEntry(indx);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
LEAVECRIT_NAMERESCACHE();
|
|
}
|
|
quit:
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
int indx;
|
|
DWORD dwError = ERROR_FILE_NOT_FOUND; // poor error
|
|
|
|
if (!vfNameresCacheInited) {
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (!ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
// is this entry already cached?
|
|
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpName);
|
|
|
|
|
|
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();
|
|
|
|
quit:
|
|
return (dwError);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DeinitNameresCache(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
if (vfNameresCacheInited) {
|
|
if (!ENTERCRIT_NAMERESCACHE())
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
for (i = 0; i < vcntNameresCacheEntries; ++i) {
|
|
if (!IS_EMPTY(i)) {
|
|
DeleteNameresCacheEntry(i);
|
|
}
|
|
}
|
|
|
|
FREE_MEMORY(vlpNameresCache);
|
|
|
|
vlpNameresCache = NULL;
|
|
|
|
vfNameresCacheInited = FALSE;
|
|
|
|
LEAVECRIT_NAMERESCACHE();
|
|
vcritNameresCache.FreeLock();
|
|
}
|
|
return (ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
DWORD
|
|
CreateNameresCacheEntry(
|
|
int indx,
|
|
DWORD dwNameSpace,
|
|
LPGUID lpGuid,
|
|
LPSTR lpszName,
|
|
int cntAddresses,
|
|
LPCSADDR_INFO lpCsaddrInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
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;
|
|
|
|
// mark this as being non-empty
|
|
vlpNameresCache[indx].dwFlags |= NAMERES_CACHE_USED;
|
|
|
|
// 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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
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)
|