Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1802 lines
48 KiB

/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
misc.h
Abstract:
This module contains miscellaneous constants & declarations.
Author:
Keith Moore (keithmo) 10-Jun-1998
Henry Sanders (henrysa) 17-Jun-1998 Merge with old httputil.h
Paul McDaniel (paulmcd) 30-Mar-1999 added refcounted eresource
Revision History:
--*/
#ifndef _MISC_H_
#define _MISC_H_
#define UL_MAX_HTTP_STATUS_CODE 999
#define UL_MAX_HTTP_STATUS_CODE_LENGTH 3
#define HTTP_PREFIX_ANSI "http://"
#define HTTP_PREFIX_ANSI_LENGTH (sizeof(HTTP_PREFIX_ANSI)-sizeof(CHAR))
#define HTTPS_PREFIX_ANSI "https://"
#define HTTPS_PREFIX_ANSI_LENGTH (sizeof(HTTPS_PREFIX_ANSI)-sizeof(CHAR))
#define HTTP_PREFIX_COLON_INDEX 4 // the colon location for http: (+4)
#define HTTPS_PREFIX_COLON_INDEX 5 // the colon location for https: (+5)
//
// Note that the length of the strong wildcard prefix is the same as
// that of the (weak) wildcard prefix.
//
#define HTTPS_WILD_PREFIX L"https://*:"
#define HTTPS_WILD_PREFIX_LENGTH (sizeof(HTTPS_WILD_PREFIX)-sizeof(WCHAR))
#define HTTP_WILD_PREFIX L"http://*:"
#define HTTP_WILD_PREFIX_LENGTH (sizeof(HTTP_WILD_PREFIX)-sizeof(WCHAR))
#define HTTPS_STRONG_WILD_PREFIX L"https://+:"
#define HTTP_STRONG_WILD_PREFIX L"http://+:"
#define HTTP_IP_PREFIX L"http://"
#define HTTP_IP_PREFIX_LENGTH (sizeof(HTTP_IP_PREFIX)-sizeof(WCHAR))
#define HTTPS_IP_PREFIX L"https://"
#define HTTPS_IP_PREFIX_LENGTH (sizeof(HTTPS_IP_PREFIX)-sizeof(WCHAR))
NTSTATUS
InitializeHttpUtil(
VOID
);
//
// Our presumed cache-line size.
//
#define CACHE_LINE_SIZE UL_CACHE_LINE
//
// # of 100ns ticks per second ( 1ns = (1 / (10^9))s )
//
#define C_NS_TICKS_PER_SEC ((LONGLONG) (10 * 1000 * 1000))
//
// # of 100ns ticks per minute ( 1ns = (1 / ((10^9) * 60)) min )
//
#define C_NS_TICKS_PER_MIN ((LONGLONG) (C_NS_TICKS_PER_SEC * 60))
//
// # of millisecs per hour ( 1 ms = (1 / ((10^3) * 60 * 60)) hour )
//
#define C_MS_TICKS_PER_HOUR ((LONGLONG) (1000 * 60 * 60))
//
// # of 100ns ticks per milli second ( 1ns = (1 / (10^4)) milli sec )
//
#define C_NS_TICKS_PER_MSEC ((LONGLONG) (10 * 1000))
//
// # of seconds per year (aprox)
// 1 year = (60 sec/min * 60 min/hr * 24 hr/day * 365 day/year)
//
#define C_SECS_PER_YEAR ((ULONG) (60 * 60 * 24 * 365))
//
// Alignment macros.
//
#define ROUND_UP( val, pow2 ) \
( ( (ULONG_PTR)(val) + (pow2) - 1 ) & ~( (pow2) - 1 ) )
//
// Macros for swapping the bytes in a long and a short.
//
#define SWAP_LONG RtlUlongByteSwap
#define SWAP_SHORT RtlUshortByteSwap
#define VALID_BOOLEAN_VALUE(x) ((x) == TRUE || (x) == FALSE)
//
// Context values stored in PFILE_OBJECT->FsContext2 to identify a handle
// as a control channel, filter channel or an app pool.
//
// BUGBUG: Can these be spoofed?
//
#define UL_CONTROL_CHANNEL_CONTEXT ((PVOID) MAKE_SIGNATURE('CTRL'))
#define UL_CONTROL_CHANNEL_CONTEXT_X ((PVOID) MAKE_SIGNATURE('Xctr'))
#define UL_FILTER_CHANNEL_CONTEXT ((PVOID) MAKE_SIGNATURE('FLTR'))
#define UL_FILTER_CHANNEL_CONTEXT_X ((PVOID) MAKE_SIGNATURE('Xflt'))
#define UL_APP_POOL_CONTEXT ((PVOID) MAKE_SIGNATURE('APPP'))
#define UL_APP_POOL_CONTEXT_X ((PVOID) MAKE_SIGNATURE('Xapp'))
#define UC_SERVER_CONTEXT ((PVOID) MAKE_SIGNATURE('SERV'))
#define UC_SERVER_CONTEXT_X ((PVOID) MAKE_SIGNATURE('Xerv'))
#define IS_SERVER( pFileObject ) \
( (pFileObject)->FsContext2 == UC_SERVER_CONTEXT )
#define IS_EX_SERVER( pFileObject ) \
( (pFileObject)->FsContext2 == UC_SERVER_CONTEXT_X )
#define MARK_VALID_SERVER( pFileObject ) \
( (pFileObject)->FsContext2 = UC_SERVER_CONTEXT )
#define MARK_INVALID_SERVER( pFileObject ) \
( (pFileObject)->FsContext2 = UC_SERVER_CONTEXT_X )
#define IS_CONTROL_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 == UL_CONTROL_CHANNEL_CONTEXT )
#define IS_EX_CONTROL_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 == UL_CONTROL_CHANNEL_CONTEXT_X )
#define MARK_VALID_CONTROL_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_CONTROL_CHANNEL_CONTEXT )
#define MARK_INVALID_CONTROL_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_CONTROL_CHANNEL_CONTEXT_X )
#define GET_CONTROL_CHANNEL( pFileObject ) \
((PUL_CONTROL_CHANNEL)((pFileObject)->FsContext))
#define GET_PP_CONTROL_CHANNEL( pFileObject ) \
((PUL_CONTROL_CHANNEL *)&((pFileObject)->FsContext))
#define IS_FILTER_PROCESS( pFileObject ) \
( (pFileObject)->FsContext2 == UL_FILTER_CHANNEL_CONTEXT )
#define IS_EX_FILTER_PROCESS( pFileObject ) \
( (pFileObject)->FsContext2 == UL_FILTER_CHANNEL_CONTEXT_X )
#define MARK_VALID_FILTER_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_FILTER_CHANNEL_CONTEXT )
#define MARK_INVALID_FILTER_CHANNEL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_FILTER_CHANNEL_CONTEXT_X )
#define GET_FILTER_PROCESS( pFileObject ) \
((PUL_FILTER_PROCESS)((pFileObject)->FsContext))
#define GET_PP_FILTER_PROCESS( pFileObject ) \
((PUL_FILTER_PROCESS *)&((pFileObject)->FsContext))
#define IS_APP_POOL( pFileObject ) \
( (pFileObject)->FsContext2 == UL_APP_POOL_CONTEXT )
#define IS_EX_APP_POOL( pFileObject ) \
( (pFileObject)->FsContext2 == UL_APP_POOL_CONTEXT_X )
#define MARK_VALID_APP_POOL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_APP_POOL_CONTEXT )
#define MARK_INVALID_APP_POOL( pFileObject ) \
( (pFileObject)->FsContext2 = UL_APP_POOL_CONTEXT_X )
#define GET_APP_POOL_PROCESS( pFileObject ) \
((PUL_APP_POOL_PROCESS)((pFileObject)->FsContext))
#define GET_PP_APP_POOL_PROCESS( pFileObject ) \
((PUL_APP_POOL_PROCESS *)&((pFileObject)->FsContext))
#define IS_APP_POOL_FO( pFileObject ) \
((pFileObject->DeviceObject->DriverObject == g_UlDriverObject) && \
(IS_APP_POOL(pFileObject)))
#define IS_FILTER_PROCESS_FO(pFileObject) \
((pFileObject->DeviceObject->DriverObject == g_UlDriverObject) && \
(IS_FILTER_PROCESS(pFileObject)))
//
// A locked doubly-linked list
//
typedef struct DECLSPEC_ALIGN(UL_CACHE_LINE) _LOCKED_LIST_HEAD
{
UL_SPIN_LOCK SpinLock;
ULONG Count;
LIST_ENTRY ListHead;
} LOCKED_LIST_HEAD, *PLOCKED_LIST_HEAD;
//
// Manipulators for LOCKED_LIST_HEADs
//
__inline
VOID
UlInitalizeLockedList(
IN PLOCKED_LIST_HEAD pListHead,
IN PSTR pListName
)
{
UNREFERENCED_PARAMETER(pListName);
InitializeListHead(&pListHead->ListHead);
pListHead->Count = 0;
UlInitializeSpinLock(&pListHead->SpinLock, pListName);
} // UlInitalizeLockedList
__inline
VOID
UlDestroyLockedList(
IN PLOCKED_LIST_HEAD pListHead
)
{
UNREFERENCED_PARAMETER(pListHead);
ASSERT(IsListEmpty(&pListHead->ListHead));
ASSERT(pListHead->Count == 0);
ASSERT(UlDbgSpinLockUnowned(&pListHead->SpinLock));
} // UlDestroyLockedList
__inline
BOOLEAN
UlLockedListInsertHead(
IN PLOCKED_LIST_HEAD pListHead,
IN PLIST_ENTRY pListEntry,
IN ULONG ListLimit
)
{
KIRQL OldIrql;
UlAcquireSpinLock(&pListHead->SpinLock, &OldIrql);
ASSERT(NULL == pListEntry->Flink);
if (HTTP_LIMIT_INFINITE != ListLimit && (pListHead->Count + 1) >= ListLimit)
{
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return FALSE;
}
pListHead->Count += 1;
InsertHeadList(
&pListHead->ListHead,
pListEntry
);
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return TRUE;
} // UlLockedListInsertHead
__inline
BOOLEAN
UlLockedListInsertTail(
IN PLOCKED_LIST_HEAD pListHead,
IN PLIST_ENTRY pListEntry,
IN ULONG ListLimit
)
{
KIRQL OldIrql;
UlAcquireSpinLock(&pListHead->SpinLock, &OldIrql);
ASSERT(NULL == pListEntry->Flink);
if (HTTP_LIMIT_INFINITE != ListLimit && (pListHead->Count + 1) >= ListLimit)
{
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return FALSE;
}
pListHead->Count += 1;
InsertTailList(
&pListHead->ListHead,
pListEntry
);
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return TRUE;
} // UlLockedListInsertTail
__inline
PLIST_ENTRY
UlLockedListRemoveHead(
IN PLOCKED_LIST_HEAD pListHead
)
{
KIRQL OldIrql;
PLIST_ENTRY pEntry = NULL;
UlAcquireSpinLock(&pListHead->SpinLock, &OldIrql);
if (!IsListEmpty(&pListHead->ListHead))
{
pEntry = RemoveHeadList(&pListHead->ListHead);
ASSERT(NULL != pEntry);
pEntry->Flink = NULL;
pListHead->Count -= 1;
ASSERT(HTTP_LIMIT_INFINITE != pListHead->Count);
}
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return pEntry;
} // UlLockedListRemoveHead
__inline
BOOLEAN
UlLockedListRemoveEntry(
IN PLOCKED_LIST_HEAD pListHead,
IN PLIST_ENTRY pListEntry
)
{
KIRQL OldIrql;
UlAcquireSpinLock(&pListHead->SpinLock, &OldIrql);
if (NULL == pListEntry->Flink)
{
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return FALSE;
}
RemoveEntryList(pListEntry);
pListEntry->Flink = NULL;
pListHead->Count -= 1;
ASSERT(HTTP_LIMIT_INFINITE != pListHead->Count);
UlReleaseSpinLock(&pListHead->SpinLock, OldIrql);
return TRUE;
} // UlLockedListRemoveEntry
//
// Miscellaneous validators, etc.
//
#define IS_VALID_DEVICE_OBJECT( pDeviceObject ) \
( ((pDeviceObject) != NULL) && \
((pDeviceObject)->Type == IO_TYPE_DEVICE) && \
((pDeviceObject)->Size == sizeof(DEVICE_OBJECT)) )
#define IS_VALID_FILE_OBJECT( pFileObject ) \
( ((pFileObject) != NULL) && \
((pFileObject)->Type == IO_TYPE_FILE) && \
((pFileObject)->Size == sizeof(FILE_OBJECT)) )
#define IS_VALID_IRP( pIrp ) \
( ((pIrp) != NULL) && \
((pIrp)->Type == IO_TYPE_IRP) && \
((pIrp)->Size >= IoSizeOfIrp((pIrp)->StackCount)) )
//
// IP Based routing token looks like L"https://1.1.1.1:80:1.1.1.1".
// Space is calculated including the terminated null and the second
// column. It is in bytes.
//
#define MAX_IP_BASED_ROUTING_TOKEN_LENGTH \
(HTTPS_IP_PREFIX_LENGTH \
+ MAX_IP_ADDR_AND_PORT_STRING_LEN * sizeof(WCHAR) \
+ sizeof(WCHAR) + MAX_IP_ADDR_PLUS_BRACKETS_STRING_LEN * sizeof(WCHAR) \
+ sizeof(WCHAR))
//
// Make sure that the maximum possible IP Based Routing token can fit to the
// default provided routing token space in the request structure. This is
// necessary to avoid the memory allocation per hit, when there is an IP bound
// site in the cgroup tree.
//
C_ASSERT(DEFAULT_MAX_ROUTING_TOKEN_LENGTH >= MAX_IP_BASED_ROUTING_TOKEN_LENGTH);
NTSTATUS
TimeFieldsToHttpDate(
IN PTIME_FIELDS pTime,
OUT PWSTR pBuffer,
IN ULONG BufferLength
);
BOOLEAN
StringTimeToSystemTime(
IN PCSTR pTimeString,
IN USHORT TimeStringLength,
OUT LARGE_INTEGER *pTime
);
ULONG
HttpUnicodeToUTF8(
IN PCWSTR lpSrcStr,
IN LONG cchSrc,
OUT LPSTR lpDestStr,
IN LONG cchDest
);
NTSTATUS
HttpUTF8ToUnicode(
IN LPCSTR lpSrcStr,
IN LONG cchSrc,
OUT LPWSTR lpDestStr,
IN OUT PLONG pcchDest,
IN ULONG dwFlags
);
typedef enum _FIND_ETAG_STATUS
{
ETAG_FOUND,
ETAG_NOT_FOUND,
ETAG_PARSE_ERROR,
} FIND_ETAG_STATUS;
FIND_ETAG_STATUS
FindInETagList(
IN PUCHAR pLocalETag,
IN PUCHAR pETagList,
IN BOOLEAN fWeakCompare
);
USHORT
HostAddressAndPortToString(
OUT PUCHAR IpAddressString,
IN PVOID TdiAddress,
IN USHORT TdiAddressType
);
USHORT
HostAddressAndPortToStringW(
PWCHAR IpAddressString,
PVOID TdiAddress,
USHORT TdiAddressType
);
USHORT
HostAddressToStringW(
OUT PWCHAR IpAddressStringW,
IN PVOID TdiAddress,
IN USHORT TdiAddressType
);
USHORT
HostAddressAndPortToRoutingTokenW(
OUT PWCHAR IpAddressStringW,
IN PVOID TdiAddress,
IN USHORT TdiAddressType
);
/***************************************************************************++
Routine Description:
Stores the decimal representation of an unsigned 32-bit
number in a character buffer, followed by a terminator
character. Returns a pointer to the next position in the
output buffer, to make appending strings easy; i.e., you
can use the result of UlStrPrintUlong as the argument to the
next call to UlStrPrintUlong. Note: the string is >not<
zero-terminated unless you passed in '\0' as chTerminator
Arguments:
psz - output buffer; assumed to be large enough to hold the number.
n - the number to print into psz, a 32-bit unsigned integer
chTerminator - character to append after the decimal representation of n
Return Value:
pointer to end of string
History:
GeorgeRe 19-Sep-2000
--***************************************************************************/
__inline
PCHAR
UlStrPrintUlong(
OUT PCHAR psz,
IN ULONG n,
IN CHAR chTerminator)
{
CHAR digits[MAX_ULONG_STR];
int i = 0;
ASSERT(psz != NULL);
digits[i++] = chTerminator;
// Build the string in reverse
do
{
digits[i++] = (CHAR) (n % 10) + '0';
n /= 10;
} while (n != 0);
while (--i >= 0)
*psz++ = digits[i];
// Back up to the nul terminator, if present
if (chTerminator == '\0')
{
--psz;
ASSERT(*psz == '\0');
}
return psz;
} // UlStrPrintUlong
/***************************************************************************++
Routine Description:
Identical to the above function except it writes to a WCHAR buffer and
it pads zeros to the beginning of the number.
--***************************************************************************/
__inline
PWCHAR
UlStrPrintUlongW(
OUT PWCHAR pwsz,
IN ULONG n,
IN LONG padding,
IN WCHAR wchTerminator)
{
WCHAR digits[MAX_ULONG_STR];
int i = 0;
ASSERT(pwsz != NULL);
digits[i++] = wchTerminator;
// Build the string in reverse
do
{
digits[i++] = (WCHAR) (n % 10) + L'0';
n /= 10;
} while (n != 0);
// Padd Zeros to the beginning
while( padding && --padding >= (i-1))
*pwsz++ = L'0';
// Reverse back
while (--i >= 0)
*pwsz++ = digits[i];
// Back up to the nul terminator, if present
if (wchTerminator == L'\0')
{
--pwsz;
ASSERT(*pwsz == L'\0');
}
return pwsz;
} // UlStrPrintUlongW
__inline
PCHAR
UlStrPrintUlongPad(
OUT PCHAR psz,
IN ULONG n,
IN LONG padding,
IN CHAR chTerminator)
{
CHAR digits[MAX_ULONG_STR];
int i = 0;
ASSERT(psz != NULL);
digits[i++] = chTerminator;
// Build the string in reverse
do
{
digits[i++] = (CHAR) (n % 10) + '0';
n /= 10;
} while (n != 0);
// Padd Zeros to the beginning
while( padding && --padding >= (i-1))
*psz++ = '0';
// Reverse back
while (--i >= 0)
*psz++ = digits[i];
// Back up to the nul terminator, if present
if (chTerminator == '\0')
{
--psz;
ASSERT(*psz == '\0');
}
return psz;
} // UlStrPrintUlongPad
/***************************************************************************++
Routine Description:
Stores the decimal representation of an unsigned 64-bit
number in a character buffer, followed by a terminator
character. Returns a pointer to the next position in the
output buffer, to make appending strings easy; i.e., you
can use the result of UlStrPrintUlonglong as the argument to the
next call to UlStrPrintUlonglong. Note: the string is >not<
zero-terminated unless you passed in '\0' as chTerminator
Arguments:
psz - output buffer; assumed to be large enough to hold the number.
n - the number to print into psz, a 64-bit unsigned integer
chTerminator - character to append after the decimal representation of n
Return Value:
pointer to end of string
History:
GeorgeRe 19-Sep-2000
--***************************************************************************/
__inline
PCHAR
UlStrPrintUlonglong(
OUT PCHAR psz,
IN ULONGLONG n,
IN CHAR chTerminator)
{
CHAR digits[MAX_ULONGLONG_STR];
int i;
if (n <= ULONG_MAX)
{
// If this is a 32-bit integer, it's faster to use the
// 32-bit routine.
return UlStrPrintUlong(psz, (ULONG)n, chTerminator);
}
ASSERT(psz != NULL);
i = 0;
digits[i++] = chTerminator;
// Build the string in reverse
do
{
digits[i++] = (CHAR) (n % 10) + '0';
n /= 10;
} while (n != 0);
while (--i >= 0)
*psz++ = digits[i];
// Back up to the nul terminator, if present
if (chTerminator == '\0')
{
--psz;
ASSERT(*psz == '\0');
}
return psz;
} // UlStrPrintUlonglong
/***************************************************************************++
Routine Description:
Stores a string in a character buffer, followed by a
terminator character. Returns a pointer to the next position
in the output buffer, to make appending strings easy; i.e.,
you can use the result of UlStrPrintStr as the argument to the
next call to UlStrPrintStr. Note: the string is >not<
zero-terminated unless you passed in '\0' as chTerminator
Arguments:
pszOutput - output buffer; assumed to be large enough to hold the number.
pszInput - input string
chTerminator - character to append after the input string
Return Value:
pointer to end of string
History:
GeorgeRe 19-Sep-2000
--***************************************************************************/
__inline
PCHAR
UlStrPrintStr(
OUT PCHAR pszOutput,
IN const CHAR* pszInput,
IN CHAR chTerminator)
{
ASSERT(pszOutput != NULL);
ASSERT(pszInput != NULL);
// copy the input string
while (*pszInput != '\0')
*pszOutput++ = *pszInput++;
*pszOutput = chTerminator;
// Move past the terminator character unless it's a nul
if (chTerminator != '\0')
++pszOutput;
return pszOutput;
} // UlStrPrintStr
/***************************************************************************++
Routine Description:
Converts an V4 Ip address to string in the provided buffer.
Arguments:
psz - Pointer to the buffer
RawAddress - IP address structure from TDI / UL_CONNECTION
chTerminator - The terminator char will be appended to the end
Return:
The number of bytes copied to destination buffer.
--***************************************************************************/
__inline
PCHAR
UlStrPrintIP(
OUT PCHAR psz,
IN const VOID* pTdiAddress,
IN USHORT TdiAddressType,
IN CHAR chTerminator
)
{
if (TdiAddressType == TDI_ADDRESS_TYPE_IP)
{
PTDI_ADDRESS_IP pIPv4Address = ((PTDI_ADDRESS_IP) pTdiAddress);
struct in_addr IPv4Addr
= * (struct in_addr UNALIGNED*) &pIPv4Address->in_addr;
psz = RtlIpv4AddressToStringA(&IPv4Addr, psz);
}
else if (TdiAddressType == TDI_ADDRESS_TYPE_IP6)
{
PTDI_ADDRESS_IP6 pIPv6Address = ((PTDI_ADDRESS_IP6) pTdiAddress);
struct in6_addr IPv6Addr
= * (struct in6_addr UNALIGNED*) &pIPv6Address->sin6_addr[0];
psz = RtlIpv6AddressToStringA(&IPv6Addr, psz);
// Write Scope ID
*psz++ = '%';
psz = UlStrPrintUlong(psz, pIPv6Address->sin6_scope_id, '\0');
}
else
{
ASSERT(! "Unexpected TdiAddressType");
*psz++ = '?';
}
*psz = chTerminator;
// Move past the terminator character unless it's a nul
if (chTerminator != '\0')
++psz;
return psz;
} // UlStrPrintIP
/***************************************************************************++
Routine Description:
Converts an V4 or V6 Ip address to string in the provided buffer.
Provided seperator is inserted between Ip and Port and also
appended after the port.
String is * NOT * going to be null terminated.
Arguments:
psz - Pointer to the buffer
RawAddress - IP address structure from TDI / UL_CONNECTION
chSeperator - the seperator character
Return:
The char pointer pointing after the last written seperator.
--***************************************************************************/
__inline
PCHAR
UlStrPrintIPAndPort(
OUT PCHAR psz,
IN const VOID* pTdiAddress,
IN USHORT TdiAddressType,
IN CHAR chSeperator
)
{
if (TdiAddressType == TDI_ADDRESS_TYPE_IP)
{
PTDI_ADDRESS_IP pIPv4Address = ((PTDI_ADDRESS_IP) pTdiAddress);
struct in_addr IPv4Addr
= * (struct in_addr UNALIGNED*) &pIPv4Address->in_addr;
USHORT IpPortNum = SWAP_SHORT(pIPv4Address->sin_port);
psz = RtlIpv4AddressToStringA(&IPv4Addr, psz);
*psz++ = chSeperator;
psz = UlStrPrintUlong(psz, IpPortNum, '\0');
}
else if (TdiAddressType == TDI_ADDRESS_TYPE_IP6)
{
PTDI_ADDRESS_IP6 pIPv6Address = ((PTDI_ADDRESS_IP6) pTdiAddress);
struct in6_addr IPv6Addr
= * (struct in6_addr UNALIGNED*) &pIPv6Address->sin6_addr[0];
USHORT IpPortNum = SWAP_SHORT(pIPv6Address->sin6_port);
psz = RtlIpv6AddressToStringA(&IPv6Addr, psz);
// Write Scope ID
*psz++ = '%';
psz = UlStrPrintUlong(psz, pIPv6Address->sin6_scope_id, '\0');
*psz++ = chSeperator;
psz = UlStrPrintUlong(psz, IpPortNum, '\0');
}
else
{
ASSERT(! "Unexpected TdiAddressType");
*psz++ = '?';
}
*psz++ = chSeperator;
return psz;
} // UlStrPrintIPAndPort
__inline
VOID
CopyTdiAddrToSockAddr(
IN USHORT TdiAddressType,
IN const VOID* pTdiAddress,
OUT struct sockaddr* pSockAddress
)
{
if (TdiAddressType == TDI_ADDRESS_TYPE_IP)
{
const PTDI_ADDRESS_IP pIPv4Address
= (const PTDI_ADDRESS_IP) pTdiAddress;
struct sockaddr_in *pSockAddrIPv4
= (struct sockaddr_in*) pSockAddress;
pSockAddrIPv4->sin_family = TdiAddressType;
pSockAddrIPv4->sin_port = pIPv4Address->sin_port;
pSockAddrIPv4->sin_addr.s_addr
= (UNALIGNED ULONG) pIPv4Address->in_addr;
RtlCopyMemory(
&pSockAddrIPv4->sin_zero[0],
&pIPv4Address->sin_zero[0],
8 * sizeof(UCHAR)
);
}
else if (TdiAddressType == TDI_ADDRESS_TYPE_IP6)
{
const PTDI_ADDRESS_IP6 pIPv6Address
= (const PTDI_ADDRESS_IP6) pTdiAddress;
struct sockaddr_in6 *pSockAddrIPv6
= (struct sockaddr_in6*) pSockAddress;
pSockAddrIPv6->sin6_family = TdiAddressType;
pSockAddrIPv6->sin6_port = pIPv6Address->sin6_port;
pSockAddrIPv6->sin6_flowinfo
= (UNALIGNED ULONG) pIPv6Address->sin6_flowinfo;
RtlCopyMemory(
&pSockAddrIPv6->sin6_addr,
&pIPv6Address->sin6_addr[0],
8 * sizeof(USHORT)
);
pSockAddrIPv6->sin6_scope_id
= (UNALIGNED ULONG) pIPv6Address->sin6_scope_id;
}
else
{
ASSERT(! "Unexpected TdiAddressType");
}
} // CopyTdiAddrToSockAddr
__inline
PCHAR
UlStrPrintProtocolStatus(
OUT PCHAR psz,
IN USHORT HttpStatusCode,
IN CHAR chTerminator
)
{
ASSERT(HttpStatusCode <= UL_MAX_HTTP_STATUS_CODE);
//
// Build ASCII representation of 3-digit status code
// in reverse order: units, tens, hundreds
//
psz[2] = '0' + (CHAR)(HttpStatusCode % 10);
HttpStatusCode /= 10;
psz[1] = '0' + (CHAR)(HttpStatusCode % 10);
HttpStatusCode /= 10;
psz[0] = '0' + (CHAR)(HttpStatusCode % 10);
psz[3] = chTerminator;
return psz + 4;
} // UlStrPrintProtocolStatus
__inline
VOID
UlProbeForRead(
IN const VOID* Address,
IN SIZE_T Length,
IN ULONG Alignment,
IN KPROCESSOR_MODE RequestorMode
)
{
ASSERT((Alignment == 1) || (Alignment == 2) ||
(Alignment == 4) || (Alignment == 8) ||
(Alignment == 16));
UlTraceVerbose(IOCTL,
("http!UlProbeForRead: "
"%Id bytes @ %p, Align = %lu, Mode = '%c'.\n",
Length, Address, Alignment,
(RequestorMode != KernelMode) ? 'U' : 'K'
));
if (RequestorMode != KernelMode)
{
// ASSERT(Length == 0 || (LONG_PTR) Address > 0);
// ProbeForRead will throw an exception if we probe kernel-mode data
ProbeForRead(Address, Length, Alignment);
}
else if (Length != 0)
{
// Check alignment
if ( ( ((ULONG_PTR) Address) & (Alignment - 1)) != 0 )
ExRaiseDatatypeMisalignment();
}
} // UlProbeForRead
__inline
VOID
UlProbeForWrite(
IN PVOID Address,
IN SIZE_T Length,
IN ULONG Alignment,
IN KPROCESSOR_MODE RequestorMode
)
{
ASSERT((Alignment == 1) || (Alignment == 2) ||
(Alignment == 4) || (Alignment == 8) ||
(Alignment == 16));
UlTraceVerbose(IOCTL,
("http!UlProbeForWrite: "
"%Id bytes @ %p, Align = %lu, Mode = '%c'.\n",
Length, Address, Alignment,
(RequestorMode != KernelMode) ? 'U' : 'K'
));
if (RequestorMode != KernelMode)
{
// ASSERT(Length == 0 || (LONG_PTR) Address > 0);
// ProbeForWrite will throw an exception if we probe kernel-mode data
ProbeForWrite(Address, Length, Alignment);
}
else if (Length != 0)
{
// Check alignment
if ( ( ((ULONG_PTR) Address) & (Alignment - 1)) != 0 )
ExRaiseDatatypeMisalignment();
}
} // UlProbeForWrite
/***************************************************************************++
Routine Description:
Probes an ANSI string and validates its length and accessibility.
This MUST be called from within an exception handler, as it
will throw exceptions if the data is invalid.
Arguments:
pStr - Pointer to the ANSI string to be validated.
ByteLength - Length in bytes of pStr, excluding the trailing '\0'.
RequestorMode - UserMode or KernelMode
--***************************************************************************/
__inline
VOID
UlProbeAnsiString(
IN PCSTR pStr,
IN USHORT ByteLength,
IN KPROCESSOR_MODE RequestorMode
)
{
UlTraceVerbose(IOCTL,
("http!UlProbeAnsiString: "
"%hu bytes @ %p, "
"Mode='%c'.\n",
ByteLength, pStr,
((RequestorMode != KernelMode) ? 'U' : 'K')
));
// String cannot be empty or NULL.
if (0 == ByteLength || NULL == pStr)
{
ExRaiseStatus(STATUS_INVALID_PARAMETER);
}
UlProbeForRead(
pStr,
(SIZE_T) ByteLength,
sizeof(CHAR),
RequestorMode
);
} // UlProbeAnsiString
/***************************************************************************++
Routine Description:
Probes a WCHAR string and validates its length and accessibility.
This MUST be called from within an exception handler, as it
will throw exceptions if the data is invalid.
Arguments:
pStr - Pointer to the WCHAR string to be validated.
ByteLength - Length in bytes of pStr, excluding the trailing L'\0'.
RequestorMode - UserMode or KernelMode
--***************************************************************************/
__inline
VOID
UlProbeWideString(
IN PCWSTR pStr,
IN USHORT ByteLength,
IN KPROCESSOR_MODE RequestorMode
)
{
UlTraceVerbose(IOCTL,
("http!UlProbeWideString: "
"%hu bytes (%hu) WCHARs @ %p,"
"Mode = '%c'.\n",
ByteLength,
ByteLength / sizeof(WCHAR),
pStr,
((RequestorMode != KernelMode) ? 'U' : 'K')
));
// String cannot be empty or NULL.
// ByteLength must be even.
// Data must be WCHAR-aligned.
if (0 == ByteLength || NULL == pStr
|| (ByteLength & (sizeof(WCHAR) - 1)) != 0
|| (((ULONG_PTR) pStr) & (sizeof(WCHAR) - 1)) != 0)
{
ExRaiseStatus(STATUS_INVALID_PARAMETER);
}
UlProbeForRead(
pStr,
(SIZE_T) ByteLength,
sizeof(WCHAR),
RequestorMode
);
} // UlProbeWideString
/***************************************************************************++
Routine Description:
Probes a UNICODE_STRING and validates its members. And captures down
to a kernel buffer.
If this function returns success, caller should clean up the allocated
Unicode buffer by calling UlFreeCapturedUnicodeString(), once it's done
with it.
Arguments:
pSrc - Pointer to the UNICODE_STRING to be validated.
The UNICODE_STRING struct should live in kernel mode
(local stack copy), but the Buffer should be in user
mode address space, unless RequestorMode == KernelMode.
pDst - Pointer to the UNICODE_STRING to hold the captured user
buffer. Caller must have initialized this before passing
in.
AllocationLimit - User string will be refused if it is exceeding this limit.
Expressed in WCHARs. If zero, no size check is done.
RequestorMode - UserMode or KernelMode.
--***************************************************************************/
__inline
NTSTATUS
UlProbeAndCaptureUnicodeString(
IN PCUNICODE_STRING pSrc,
IN KPROCESSOR_MODE RequestorMode,
OUT PUNICODE_STRING pDst,
IN const USHORT AllocationLimit // In WCHARS, optional
)
{
NTSTATUS Status = STATUS_SUCCESS;
PWSTR pKernelBuffer = NULL;
ASSERT(NULL != pSrc);
ASSERT(NULL != pDst);
ASSERT(pSrc != pDst);
ASSERT(AllocationLimit <= UNICODE_STRING_MAX_WCHAR_LEN);
// Ensure that pDst is properly initialized
RtlInitEmptyUnicodeString(pDst, NULL, 0);
UlTraceVerbose(IOCTL,
("http!UlProbeAndCaptureUnicodeString: struct @ %p, "
"Length = %hu bytes (%hu) WCHARs, "
"MaxLen = %hu bytes,"
"Mode = '%c'.\n",
pSrc,
pSrc->Length,
pSrc->Length / sizeof(WCHAR),
pSrc->MaximumLength,
(RequestorMode != KernelMode) ? 'U' : 'K'
));
// Do not allocate/copy more than the limit being enforced.
// if limit is non-zero, otherwise it is not being enforced
if (0 != AllocationLimit &&
(AllocationLimit * sizeof(WCHAR)) < pSrc->Length)
{
return STATUS_INVALID_PARAMETER;
}
if ((pSrc->MaximumLength < pSrc->Length) || (pSrc->Length == 0))
{
return STATUS_INVALID_PARAMETER;
}
__try
{
// Probe the user's buffer first.
UlProbeWideString(
pSrc->Buffer,
pSrc->Length,
RequestorMode
);
// Allocate a kernel buffer and capture the user's buffer.
// The ULONG cast prevents USHORT arithmetic overflow.
pKernelBuffer = (PWSTR) UL_ALLOCATE_ARRAY(
PagedPool,
WCHAR,
((ULONG) pSrc->Length + sizeof(WCHAR))
/ sizeof(WCHAR),
UL_UNICODE_STRING_POOL_TAG
);
if (pKernelBuffer == NULL)
{
Status = STATUS_NO_MEMORY;
__leave;
}
// Copy and null-terminate the unicode string.
RtlCopyMemory(pKernelBuffer, pSrc->Buffer, pSrc->Length);
pKernelBuffer[pSrc->Length/sizeof(WCHAR)] = UNICODE_NULL;
pDst->Buffer = pKernelBuffer;
pDst->Length = pSrc->Length;
pDst->MaximumLength = pDst->Length + sizeof(WCHAR);
}
__except( UL_EXCEPTION_FILTER() )
{
Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
}
if (!NT_SUCCESS(Status))
{
if (pKernelBuffer != NULL)
{
UL_FREE_POOL(pKernelBuffer, UL_UNICODE_STRING_POOL_TAG );
}
}
return Status;
} // UlProbeAndCaptureUnicodeString
// Cleans up a UNICODE_STRING initialized by UlProbeAndCaptureUnicodeString()
__inline
VOID
UlFreeCapturedUnicodeString(
IN PUNICODE_STRING pCapturedUnicodeString
)
{
ASSERT(pCapturedUnicodeString);
if (pCapturedUnicodeString->Buffer != NULL)
{
UL_FREE_POOL(
pCapturedUnicodeString->Buffer,
UL_UNICODE_STRING_POOL_TAG
);
}
RtlInitEmptyUnicodeString(pCapturedUnicodeString, NULL, 0);
}
//
// Small macro to test the sanity of an unicode string
// also tests whether it is null terminated or not.
//
#define IS_WELL_FORMED_UNICODE_STRING(pUStr) \
((pUStr) && \
(pUStr)->Buffer && \
(pUStr)->Length && \
(!((pUStr)->Length & 1)) && \
(pUStr)->Length < (pUStr)->MaximumLength && \
(!((pUStr)->MaximumLength & 1)) && \
(pUStr)->Buffer[ \
(pUStr)->Length/sizeof(WCHAR)] \
== UNICODE_NULL \
)
//
// 64-bit interlocked routines
//
#ifdef _WIN64
#define UlInterlockedIncrement64 InterlockedIncrement64
#define UlInterlockedDecrement64 InterlockedDecrement64
#define UlInterlockedAdd64 InterlockedAdd64
#define UlInterlockedExchange64 InterlockedExchange64
#else // !_WIN64
__inline
LONGLONG
UlInterlockedIncrement64 (
IN OUT PLONGLONG Addend
)
{
LONGLONG localAddend;
LONGLONG addendPlusOne;
LONGLONG originalAddend;
do {
localAddend = *((volatile LONGLONG *) Addend);
addendPlusOne = localAddend + 1;
originalAddend = InterlockedCompareExchange64( Addend,
addendPlusOne,
localAddend );
PAUSE_PROCESSOR;
} while (originalAddend != localAddend);
return addendPlusOne;
} // UlInterlockedIncrement64
__inline
LONGLONG
UlInterlockedDecrement64 (
IN OUT PLONGLONG Addend
)
{
LONGLONG localAddend;
LONGLONG addendMinusOne;
LONGLONG originalAddend;
do {
localAddend = *((volatile LONGLONG *) Addend);
addendMinusOne = localAddend - 1;
originalAddend = InterlockedCompareExchange64( Addend,
addendMinusOne,
localAddend );
PAUSE_PROCESSOR;
} while (originalAddend != localAddend);
return addendMinusOne;
} // UlInterlockedDecrement64
__inline
LONGLONG
UlInterlockedAdd64 (
IN OUT PLONGLONG Addend,
IN LONGLONG Value
)
{
LONGLONG localAddend;
LONGLONG addendPlusValue;
LONGLONG originalAddend;
do {
localAddend = *((volatile LONGLONG *) Addend);
addendPlusValue = localAddend + Value;
originalAddend = InterlockedCompareExchange64( Addend,
addendPlusValue,
localAddend );
PAUSE_PROCESSOR;
} while (originalAddend != localAddend);
return originalAddend;
} // UlInterlockedAdd64
__inline
LONGLONG
UlInterlockedExchange64 (
IN OUT PLONGLONG Addend,
IN LONGLONG newValue
)
{
LONGLONG localAddend;
LONGLONG originalAddend;
do {
localAddend = *((volatile LONGLONG *) Addend);
originalAddend = InterlockedCompareExchange64( Addend,
newValue,
localAddend );
PAUSE_PROCESSOR;
} while (originalAddend != localAddend);
return originalAddend;
} // UlInterlockedExchange64
#endif // !_WIN64
//
// Barrier support for read-mostly operations
// Note that the AMD64 and IA32 barrier relies on program ordering
// and does not generate a hardware barrier
//
#if defined(_M_IA64)
#define UL_READMOSTLY_READ_BARRIER() __mf()
#define UL_READMOSTLY_WRITE_BARRIER() __mf()
#define UL_READMOSTLY_MEMORY_BARRIER() __mf()
#elif defined(_AMD64_) || defined(_X86_)
extern VOID _ReadWriteBarrier();
extern VOID _WriteBarrier();
#pragma intrinsic(_ReadWriteBarrier)
#pragma intrinsic(_WriteBarrier)
#define UL_READMOSTLY_READ_BARRIER() _ReadWriteBarrier()
#define UL_READMOSTLY_WRITE_BARRIER() _WriteBarrier()
#define UL_READMOSTLY_MEMORY_BARRIER() _ReadWriteBarrier()
#else
#error Cannot generate memory barriers for this architecture
#endif
__inline
PVOID
UlpFixup(
IN PUCHAR pUserPtr,
IN PUCHAR pKernelPtr,
IN PUCHAR pOffsetPtr,
IN ULONG BufferLength
)
{
ASSERT( pOffsetPtr >= pKernelPtr );
ASSERT( DIFF(pOffsetPtr - pKernelPtr) <= BufferLength );
UNREFERENCED_PARAMETER(BufferLength);
return pUserPtr + DIFF(pOffsetPtr - pKernelPtr);
} // UlpFixup
#define FIXUP_PTR( Type, pUserPtr, pKernelPtr, pOffsetPtr, BufferLength ) \
(Type)UlpFixup( \
(PUCHAR)(pUserPtr), \
(PUCHAR)(pKernelPtr), \
(PUCHAR)(pOffsetPtr), \
(BufferLength) \
)
//
// Time utility to calculate the TimeZone Bias Daylight/standart
// and returns one of the following values.
// It's taken from base\client\timedate.c.
// Once this two functions are exposed in the kernel we can get rid of
// this two functions.
//
#define UL_TIME_ZONE_ID_INVALID 0xFFFFFFFF
#define UL_TIME_ZONE_ID_UNKNOWN 0
#define UL_TIME_ZONE_ID_STANDARD 1
#define UL_TIME_ZONE_ID_DAYLIGHT 2
BOOLEAN
UlpCutoverTimeToSystemTime(
PTIME_FIELDS CutoverTime,
PLARGE_INTEGER SystemTime,
PLARGE_INTEGER CurrentSystemTime
);
ULONG
UlCalcTimeZoneIdAndBias(
IN RTL_TIME_ZONE_INFORMATION *ptzi,
OUT PLONG pBias
);
BOOLEAN
UlIsLowNPPCondition( VOID );
//
// Converts from NtStatus to Win32Status
//
#define HttpNtStatusToWin32Status( Status ) \
( ( (Status) == STATUS_SUCCESS ) \
? NO_ERROR \
: RtlNtStatusToDosErrorNoTeb( Status ) )
ULONG
HttpUnicodeToUTF8Count(
IN LPCWSTR pwszIn,
IN ULONG dwInLen,
IN BOOLEAN bEncode
);
NTSTATUS
HttpUnicodeToUTF8Encode(
IN LPCWSTR pwszIn,
IN ULONG dwInLen,
OUT PUCHAR pszOut,
IN ULONG dwOutLen,
OUT ULONG *pdwOutLen,
IN BOOLEAN bEncode
);
PSTR
UlUlongToHexString(
ULONG n,
PSTR pBuffer
);
PCHAR
UxStriStr(
const char *str1,
const char *str2,
ULONG length
);
PCHAR
UxStrStr(
const char *str1,
const char *str2,
ULONG length
);
/**************************************************************************++
Routine Description:
This routine tries to convert a SECURITY_STATUS to an NTSTATUS. It calls
MapSecurityError to perform the conversion. If the conversion fails,
it returns STATUS_UNSUCCESSFUL.
This routine always returns a valid NTSTATUS.
Arguments:
SecStatus - SECURITY_STATUS to be converted into NTSTATUS.
Return Value:
NTSTATUS.
--**************************************************************************/
__forceinline
NTSTATUS
SecStatusToNtStatus(
SECURITY_STATUS SecStatus
)
{
NTSTATUS Status;
//
// Try to convert SECURITY_STATUS to NTSTATUS. If a corresponding
// NTSTATUS is not found, then return STATUS_UNSUCCESSFUL.
//
Status = MapSecurityError(SecStatus);
//
// The following is temporarily disabled because, the tests will fail.
// Enable this when MapSecurityError is fixed.
//
#if 0
if (!NT_SUCCESS(Status) && Status == (NTSTATUS)SecStatus)
{
Status = STATUS_UNSUCCESSFUL;
}
#endif
return Status;
}
/**************************************************************************++
Routine Description:
This routine returns the number of bytes required to encode n byte
binary data in base64.
Arguments:
BinaryLength - Length of binary data (in bytes)
pBase64Length - Pointer to length of Base64 data (in bytes)
Return Value:
NTSTATUS.
--**************************************************************************/
__forceinline
NTSTATUS
BinaryToBase64Length(
IN ULONG BinaryLength,
OUT PULONG pBase64Length
)
{
NTSTATUS Status;
//
// Every 6 bits in binary will be encoded by 8 bits in base64.
// Hence the output is roughly 33.33% larger than input.
// First round up (BinaryLength / 3). Now. each 3 bytes
// in binary data will yield 4 bytes of base64 encoded data.
//
// N.B. The order of arithmetic operation is important.
// Actual formula of conversion is:
// Base64Length = ceil(BinaryLength/3) * 4.
//
*pBase64Length = ((BinaryLength + 2) / 3) * 4;
Status = STATUS_SUCCESS;
// Was there an arithmetic overflow in the above computation?
if (*pBase64Length < BinaryLength)
{
Status = STATUS_INTEGER_OVERFLOW;
}
return Status;
}
/**************************************************************************++
Routine Description:
This routine returns the number of bytes required to decode base64
encoded data of length n back to binary format.
Arguments:
Base64Length - Length of base64 data (in bytes).
pBinaryLength - Length of binary data (in bytes).
Return Value:
NTSTATUS.
--**************************************************************************/
__forceinline
NTSTATUS
Base64ToBinaryLength(
IN ULONG Base64Length,
OUT PULONG pBinaryLength
)
{
NTSTATUS Status;
*pBinaryLength = (Base64Length / 4) * 3;
Status = STATUS_SUCCESS;
// Base64Length must be a multiple of 4.
if (Base64Length % 4 != 0)
{
Status = STATUS_INVALID_PARAMETER;
}
return Status;
}
/**************************************************************************++
Safer version of UlInitUnicodeString, using the private function until
the Rtl one is exposed.
--**************************************************************************/
__inline
NTSTATUS
UlInitUnicodeStringEx(
OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString OPTIONAL
)
{
if (SourceString != NULL)
{
SIZE_T Length = wcslen(SourceString);
//
// We are actually limited to 32765 characters since we want
// to store a meaningful MaximumLength also.
//
if (Length > (UNICODE_STRING_MAX_CHARS - 1))
{
return STATUS_NAME_TOO_LONG;
}
Length *= sizeof(WCHAR);
DestinationString->Length = (USHORT) Length;
DestinationString->MaximumLength = (USHORT) (Length + sizeof(WCHAR));
DestinationString->Buffer = (PWSTR) SourceString;
}
else
{
DestinationString->Length = 0;
DestinationString->MaximumLength = 0;
DestinationString->Buffer = NULL;
}
return STATUS_SUCCESS;
}
NTSTATUS
BinaryToBase64(
IN PUCHAR pBinaryData,
IN ULONG BinaryDataLen,
IN PUCHAR pBase64Data,
IN ULONG Base64DataLen,
OUT PULONG BytesWritten
);
NTSTATUS
Base64ToBinary(
IN PUCHAR pBase64Data,
IN ULONG Base64DataLen,
IN PUCHAR pBinaryData,
IN ULONG BinaryDataLen,
OUT PULONG BytesWritten
);
//
// A simple exclusive spinlock at passive level that doesn't raise IRQLs.
//
#define UL_EX_LOCK_FREE 0
#define UL_EX_LOCK_LOCKED 1
typedef LONG UL_EXCLUSIVE_LOCK, *PUL_EXCLUSIVE_LOCK;
__inline
VOID
UlInitializeExclusiveLock(
PUL_EXCLUSIVE_LOCK pExLock
)
{
*pExLock = UL_EX_LOCK_FREE;
}
__inline
VOID
UlAcquireExclusiveLock(
PUL_EXCLUSIVE_LOCK pExLock
)
{
while (TRUE)
{
if (UL_EX_LOCK_FREE == *((volatile LONG *) pExLock))
{
if (UL_EX_LOCK_FREE == InterlockedCompareExchange(
pExLock,
UL_EX_LOCK_LOCKED,
UL_EX_LOCK_FREE
))
{
break;
}
}
PAUSE_PROCESSOR;
}
}
__inline
VOID
UlReleaseExclusiveLock(
PUL_EXCLUSIVE_LOCK pExLock
)
{
ASSERT( UL_EX_LOCK_LOCKED == *pExLock );
InterlockedExchange( pExLock, UL_EX_LOCK_FREE );
}
#endif // _MISC_H_