|
|
/*++
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
|