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.
754 lines
15 KiB
754 lines
15 KiB
/*++
|
|
|
|
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
|
|
|