windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/common/iisassoc.cxx
2020-09-26 16:20:57 +08:00

755 lines
14 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
iisassoc.cxx
Abstract:
This module implements the IIS_ASSOCIATION class.
Author:
Keith Moore (keithmo) 16-Jan-1997
Revision History:
--*/
#include "tcpdllp.hxx"
#pragma hdrstop
#include <iisassoc.hxx>
//
// Private constants.
//
#define HASH_FROM_CONTEXT(ctx) \
( \
(m_HashIpAddress ? ctx->IpAddressHash : 0) + \
(m_HashHostName ? ctx->HostNameHash : 0) \
)
//
// Private types.
//
typedef struct _HASH_ENTRY {
LIST_ENTRY HashBucketListEntry;
DWORD IpAddress;
DWORD Hash;
LPVOID Context;
CHAR HostName[1]; // Expands as needed. MUST BE LAST FIELD IN STRUCTURE!
} HASH_ENTRY, *PHASH_ENTRY;
//
// Private globals.
//
//
// Private prototypes.
//
//
// Public functions.
//
IIS_ASSOCIATION::IIS_ASSOCIATION(
IN BOOL HashIpAddress,
IN BOOL HashHostName,
IN INT NumHashBuckets
) :
m_HashIpAddress( HashIpAddress ),
m_HashHostName( HashHostName ),
m_NumHashBuckets( NumHashBuckets ),
m_HashBuckets( NULL )
/*++
Routine Description:
IIS_ASSOCIATION constructor.
Arguments:
HashIpAddress - TRUE if this association should hash the IP address.
HashHostName - TRUE if this association should hash the host name.
NumHashBuckets - The number of hash buckets to create for this
association.
Return Value:
None.
--*/
{
//
// Sanity check.
//
DBG_ASSERT( HashIpAddress || HashHostName );
DBG_ASSERT( NumHashBuckets > 0 );
//
// Initialize the hash buckets.
//
m_HashBuckets = new LIST_ENTRY[m_NumHashBuckets];
if( m_HashBuckets == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
} else {
for( INT i = 0 ; i < m_NumHashBuckets ; i++ ) {
InitializeListHead( &m_HashBuckets[i] );
}
}
} // IIS_ASSOCIATION::IIS_ASSOCIATION
IIS_ASSOCIATION::~IIS_ASSOCIATION()
/*++
Routine Description:
IIS_ASSOCIATION destructor.
Arguments:
None.
Return Value:
None.
--*/
{
INT i;
PLIST_ENTRY hashBucket;
PLIST_ENTRY listEntry;
PHASH_ENTRY hashEntry;
//
// Purge the hash bucket entries.
//
for( i = 0, hashBucket = m_HashBuckets ;
i < m_NumHashBuckets ;
i++, hashBucket++ ) {
while( !IsListEmpty( hashBucket ) ) {
listEntry = RemoveHeadList( hashBucket );
hashEntry = CONTAINING_RECORD(
listEntry,
HASH_ENTRY,
HashBucketListEntry
);
delete hashEntry;
}
}
//
// Delete the hash bucket array.
//
delete m_HashBuckets;
} // IIS_ASSOCIATION::~IIS_ASSOCIATION
DWORD
IIS_ASSOCIATION::AddDescriptor(
IN DWORD IpAddress,
IN const CHAR * HostName,
IN LPVOID Context,
IN OUT PHASH_CONTEXT HashContext OPTIONAL
)
/*++
Routine Description:
Adds a new descriptor to the association.
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
Context - A context to associate with the descriptor.
HashContext - An optional hash context from a previous call to one
of the association functions. This is used to avoid redundant
hash calculations. If NULL, then a temporary hash context is
created and used.
Return Value:
DWORD - Completion status. 0 if successful, !0 otherwise.
--*/
{
HASH_CONTEXT localContext;
PHASH_ENTRY hashEntry;
//
// If a hash context was specified, use it. Otherwise, initialize a
// local context and use it instead.
//
if( HashContext == NULL ) {
HashContext = &localContext;
InitializeHashContext( &localContext );
}
//
// Get the hash.
//
CalculateHash(
IpAddress,
HostName,
HashContext
);
//
// Try to find the descriptor in the hash table. If it's there, then
// fail the request. Otherwise, create a new hash entry and stick it
// in the table.
//
hashEntry = FindDescriptor(
IpAddress,
HostName,
HashContext
);
if( hashEntry == NULL ) {
hashEntry = CreateHashEntry(
IpAddress,
HostName,
HashContext
);
if( hashEntry != NULL ) {
InsertHeadList(
&m_HashBuckets[hashEntry->Hash % m_NumHashBuckets],
&hashEntry->HashBucketListEntry
);
hashEntry->Context = Context;
return NO_ERROR;
}
return ERROR_NOT_ENOUGH_MEMORY;
}
return ERROR_DUP_NAME;
} // IIS_ASSOCIATION::AddDescriptor
DWORD
IIS_ASSOCIATION::LookupDescriptor(
IN DWORD IpAddress,
IN const CHAR * HostName OPTIONAL,
OUT LPVOID *Context,
IN OUT PHASH_CONTEXT HashContext
)
/*++
Routine Description:
Searches the association for the specified descriptor.
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
Context - Recieves the context associated with the descriptor if
successful.
HashContext - An optional hash context from a previous call to one
of the association functions. This is used to avoid redundant
hash calculations. If NULL, then a temporary hash context is
created and used.
Return Value:
DWORD - Completion status. 0 if successful, !0 otherwise.
--*/
{
HASH_CONTEXT localContext;
PHASH_ENTRY hashEntry;
//
// If a hash context was specified, use it. Otherwise, initialize a
// local context and use it instead.
//
if( HashContext == NULL ) {
HashContext = &localContext;
InitializeHashContext( &localContext );
}
//
// Get the hash.
//
CalculateHash(
IpAddress,
HostName,
HashContext
);
//
// Try to find the descriptor in the hash table. If it's there, then
// return the context to the caller. Otherwise, fail the request.
//
hashEntry = FindDescriptor(
IpAddress,
HostName,
HashContext
);
if( hashEntry != NULL ) {
*Context = hashEntry->Context;
return NO_ERROR;
}
return ERROR_INVALID_PARAMETER;
} // IIS_ASSOCIATION::LookupDescriptor
DWORD
IIS_ASSOCIATION::RemoveDescriptor(
IN DWORD IpAddress,
IN const CHAR * HostName OPTIONAL,
OUT LPVOID *Context,
IN OUT PHASH_CONTEXT HashContext
)
/*++
Routine Description:
Removes a descriptor from the association.
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
Context - Receives the context associated with the descriptor if
successful.
HashContext - An optional hash context from a previous call to one
of the association functions. This is used to avoid redundant
hash calculations. If NULL, then a temporary hash context is
created and used.
Return Value:
DWORD - Completion status. 0 if successful, !0 otherwise.
--*/
{
HASH_CONTEXT localContext;
PHASH_ENTRY hashEntry;
//
// If a hash context was specified, use it. Otherwise, initialize a
// local context and use it instead.
//
if( HashContext == NULL ) {
HashContext = &localContext;
InitializeHashContext( &localContext );
}
//
// Get the hash.
//
CalculateHash(
IpAddress,
HostName,
HashContext
);
//
// Try to find the descriptor in the hash table. If it's there, then
// remove it from the hash table and return the context to the caller.
// Otherwise, fail the request.
//
hashEntry = FindDescriptor(
IpAddress,
HostName,
HashContext
);
if( hashEntry != NULL ) {
*Context = hashEntry->Context;
RemoveEntryList(
&hashEntry->HashBucketListEntry
);
delete hashEntry;
return NO_ERROR;
}
return ERROR_INVALID_PARAMETER;
} // IIS_ASSOCIATION::RemoveDescriptor
VOID
IIS_ASSOCIATION::EnumDescriptors(
IN LPFN_ASSOCIATION_CALLBACK Callback,
IN LPVOID CallbackContext
)
/*++
Routine Description:
Enumerates the descriptors in the association.
Arguments:
Callback - A function to call for every descriptor.
CallbackContext - A context to pass back to the callback.
Return Value:
None.
--*/
{
INT i;
PLIST_ENTRY listEntry;
PLIST_ENTRY hashBuckets;
PHASH_ENTRY hashEntry;
DBG_ASSERT( Callback != NULL );
for( i = 0, hashBuckets = m_HashBuckets ;
i < m_NumHashBuckets ;
i++, hashBuckets++ ) {
listEntry = hashBuckets->Flink;
while( listEntry != hashBuckets ) {
hashEntry = CONTAINING_RECORD(
listEntry,
HASH_ENTRY,
HashBucketListEntry
);
listEntry = listEntry->Flink;
if( !(Callback)(
CallbackContext,
hashEntry->Context,
hashEntry->IpAddress,
hashEntry->HostName
) ) {
break;
}
}
}
} // IIS_ASSOCIATION::EnumDescriptors
//
// Private functions.
//
VOID
IIS_ASSOCIATION::CalculateHash(
IN DWORD IpAddress,
IN const CHAR * HostName OPTIONAL,
OUT PHASH_CONTEXT HashContext
)
/*++
Routine Description:
Calculates the hash of the IP address (if HashIpAddress == TRUE) and/or
the host name (if HashHostName == TRUE).
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
HashContext - Will receive the updated hash.
Return Value:
None.
--*/
{
//
// Calculate the IP address hash if needed and not already calculated.
//
if( HashContext->IpAddressHash == 0 && m_HashIpAddress ) {
HashContext->IpAddressHash = IpAddress;
if( HashContext->IpAddressHash == 0 ) {
HashContext->IpAddressHash++;
}
}
//
// Calculate the host name hash if needed and not already calculated.
//
if( HashContext->HostNameHash == 0 && m_HashHostName ) {
DWORD hash = 0;
DBG_ASSERT( HostName != NULL );
while( *HostName ) {
CHAR ch;
ch = *HostName++;
//
// This is basically toupper() for 7-bit ASCII. This is
// OK for DNS names.
//
if( ch >= 'a' && ch <= 'z' ) {
ch -= ( 'a' - 'A' );
}
hash = ( hash * 5 ) + (DWORD)ch;
}
if( hash == 0 ) {
hash++;
}
HashContext->HostNameHash = hash;
}
} // IIS_ASSOCIATION::CalculateHash
PHASH_ENTRY
IIS_ASSOCIATION::FindDescriptor(
IN DWORD IpAddress,
IN const CHAR * HostName OPTIONAL,
IN PHASH_CONTEXT HashContext
)
/*++
Routine Description:
Finds a descriptor in the association.
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
HashContext - The current hash value.
Return Value:
PHASH_ENTRY - Pointer to the hash entry if successful, NULL otherwise.
--*/
{
DWORD hash;
PLIST_ENTRY bucket;
PLIST_ENTRY scan;
PHASH_ENTRY entry;
//
// Get the correct bucket based on the hash value.
//
hash = HASH_FROM_CONTEXT(HashContext);
bucket = &m_HashBuckets[hash % m_NumHashBuckets];
//
// Scan the bucket, looking for a match.
//
for( scan = bucket->Flink ; scan != bucket ; scan = scan->Flink ) {
entry = CONTAINING_RECORD(
scan,
HASH_ENTRY,
HashBucketListEntry
);
if( entry->Hash != hash ) {
continue;
}
if( m_HashIpAddress &&
entry->IpAddress != IpAddress ) {
continue;
}
if( m_HashHostName ) {
DBG_ASSERT( HostName != NULL );
if( _stricmp(
entry->HostName,
HostName
) ) {
continue;
}
}
//
// Success!
//
return entry;
}
//
// If we made it this far, then the item is not in the hash table.
//
return NULL;
} // IIS_ASSOCIATION::FindDescriptor
PHASH_ENTRY
IIS_ASSOCIATION::CreateHashEntry(
IN DWORD IpAddress,
IN const CHAR * HostName OPTIONAL,
IN PHASH_CONTEXT HashContext
)
/*++
Routine Description:
Creats a new hash entry.
Arguments:
IpAddress - The descriptor IP address. Ignored if the association
was not created with HashIpAddress == TRUE.
HostName - The descriptor host name. Ignored if the association was
not created with HashHostName == TRUE.
HashContext - The current hash value.
Return Value:
PHASH_ENTRY - Pointer to the newly created hash entry if successful,
NULL otherwise.
--*/
{
PHASH_ENTRY entry;
entry = (PHASH_ENTRY)( new BYTE[sizeof(*entry) + strlen(HostName)] );
if( entry != NULL ) {
entry->IpAddress = IpAddress;
entry->Hash = HASH_FROM_CONTEXT(HashContext);
strcpy( entry->HostName, HostName );
}
return entry;
} // IIS_ASSOCIATION::CreateHashEntry