mirror of https://github.com/tongzx/nt5src
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.
3713 lines
88 KiB
3713 lines
88 KiB
/*++
|
|
|
|
Copyright (c) 1998-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
misc.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the miscellaneous UL routines.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 10-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
ULONG HttpChars[256];
|
|
USHORT FastPopChars[256];
|
|
USHORT DummyPopChars[256];
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
NTSTATUS
|
|
UlpRestartDeviceControl(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, UlOpenRegistry )
|
|
#pragma alloc_text( PAGE, UlReadLongParameter )
|
|
#pragma alloc_text( PAGE, UlReadLongLongParameter )
|
|
#pragma alloc_text( PAGE, UlReadGenericParameter )
|
|
#pragma alloc_text( PAGE, UlIssueDeviceControl )
|
|
#endif // ALLOC_PRAGMA
|
|
#if 0
|
|
NOT PAGEABLE -- UlBuildDeviceControlIrp
|
|
NOT PAGEABLE -- UlULongLongToAscii
|
|
NOT PAGEABLE -- UlpRestartDeviceControl
|
|
NOT PAGEABLE -- UlAllocateReceiveBufferPool
|
|
NOT PAGEABLE -- UlFreeReceiveBufferPool
|
|
NOT PAGEABLE -- UlAllocateIrpContextPool
|
|
NOT PAGEABLE -- UlFreeIrpContextPool
|
|
NOT PAGEABLE -- UlAllocateRequestBufferPool
|
|
NOT PAGEABLE -- UlFreeRequestBufferPool
|
|
NOT PAGEABLE -- UlAllocateInternalRequestPool
|
|
NOT PAGEABLE -- UlFreeInternalRequestPool
|
|
NOT PAGEABLE -- UlAllocateChunkTrackerPool
|
|
NOT PAGEABLE -- UlFreeChunkTrackerPool
|
|
NOT PAGEABLE -- UlAllocateFullTrackerPool
|
|
NOT PAGEABLE -- UlFreeFullTrackerPool
|
|
NOT PAGEABLE -- UlAllocateResponseBufferPool
|
|
NOT PAGEABLE -- UlFreeResponseBufferPool
|
|
NOT PAGEABLE -- UlAllocateLogBufferPool
|
|
NOT PAGEABLE -- UlFreeLogBufferPool
|
|
NOT PAGEABLE -- UlInvokeCompletionRoutine
|
|
NOT PAGEABLE -- UlUlInterlockedIncrement64
|
|
NOT PAGEABLE -- UlUlInterlockedDecrement64
|
|
NOT PAGEABLE -- UlUlInterlockedAdd64
|
|
NOT PAGEABLE -- UlUlInterlockedExchange64
|
|
|
|
NOT PAGEABLE -- TwoDigitsToUnicode
|
|
NOT PAGEABLE -- TimeFieldsToHttpDate
|
|
NOT PAGEABLE -- AsciiToShort
|
|
NOT PAGEABLE -- TwoAsciisToShort
|
|
NOT PAGEABLE -- NumericToAsciiMonth
|
|
NOT PAGEABLE -- StringTimeToSystemTime
|
|
#endif
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Opens a handle to the UL's Parameters registry key.
|
|
|
|
Arguments:
|
|
|
|
BaseName - Supplies the name of the parent registry key containing
|
|
the Parameters key.
|
|
|
|
ParametersHandle - Returns a handle to the Parameters key.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlOpenRegistry(
|
|
IN PUNICODE_STRING BaseName,
|
|
OUT PHANDLE ParametersHandle
|
|
)
|
|
{
|
|
HANDLE configHandle;
|
|
NTSTATUS status;
|
|
PWSTR parametersString = REGISTRY_PARAMETERS;
|
|
UNICODE_STRING parametersKeyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG disposition;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Open the registry for the initial string.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes, // ObjectAttributes
|
|
BaseName, // ObjectName
|
|
OBJ_CASE_INSENSITIVE | // Attributes
|
|
UL_KERNEL_HANDLE,
|
|
NULL, // RootDirectory
|
|
NULL // SecurityDescriptor
|
|
);
|
|
|
|
UlAttachToSystemProcess();
|
|
|
|
status = ZwOpenKey( &configHandle, KEY_READ, &objectAttributes );
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
UlDetachFromSystemProcess();
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Now open the parameters key.
|
|
//
|
|
|
|
RtlInitUnicodeString( ¶metersKeyName, parametersString );
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes, // ObjectAttributes
|
|
¶metersKeyName, // ObjectName
|
|
OBJ_CASE_INSENSITIVE, // Attributes
|
|
configHandle, // RootDirectory
|
|
NULL // SecurityDescriptor
|
|
);
|
|
|
|
status = ZwOpenKey( ParametersHandle, KEY_READ, &objectAttributes );
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ZwClose( configHandle );
|
|
UlDetachFromSystemProcess();
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// All keys successfully opened or created.
|
|
//
|
|
|
|
ZwClose( configHandle );
|
|
UlDetachFromSystemProcess();
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlOpenRegistry
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Reads a single (LONG/ULONG) value from the registry.
|
|
|
|
Arguments:
|
|
|
|
ParametersHandle - Supplies an open registry handle.
|
|
|
|
ValueName - Supplies the name of the value to read.
|
|
|
|
DefaultValue - Supplies the default value.
|
|
|
|
Return Value:
|
|
|
|
LONG - The value read from the registry or the default if the
|
|
registry data was unavailable or incorrect.
|
|
|
|
--***************************************************************************/
|
|
LONG
|
|
UlReadLongParameter(
|
|
IN HANDLE ParametersHandle,
|
|
IN PWCHAR ValueName,
|
|
IN LONG DefaultValue
|
|
)
|
|
{
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION information;
|
|
UNICODE_STRING valueKeyName;
|
|
ULONG informationLength;
|
|
LONG returnValue;
|
|
NTSTATUS status;
|
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(LONG)];
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Build the value name, read it from the registry.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&valueKeyName,
|
|
ValueName
|
|
);
|
|
|
|
information = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|
|
|
status = ZwQueryValueKey(
|
|
ParametersHandle,
|
|
&valueKeyName,
|
|
KeyValuePartialInformation,
|
|
(PVOID)information,
|
|
sizeof(buffer),
|
|
&informationLength
|
|
);
|
|
|
|
//
|
|
// If the read succeeded, the type is DWORD and the length is
|
|
// sane, use it. Otherwise, use the default.
|
|
//
|
|
|
|
if (status == STATUS_SUCCESS &&
|
|
information->Type == REG_DWORD &&
|
|
information->DataLength == sizeof(returnValue))
|
|
{
|
|
RtlMoveMemory( &returnValue, information->Data, sizeof(returnValue) );
|
|
} else {
|
|
returnValue = DefaultValue;
|
|
}
|
|
|
|
return returnValue;
|
|
|
|
} // UlReadLongParameter
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Reads a single (LONGLONG/ULONGLONG) value from the registry.
|
|
|
|
Arguments:
|
|
|
|
ParametersHandle - Supplies an open registry handle.
|
|
|
|
ValueName - Supplies the name of the value to read.
|
|
|
|
DefaultValue - Supplies the default value.
|
|
|
|
Return Value:
|
|
|
|
LONGLONG - The value read from the registry or the default if the
|
|
registry data was unavailable or incorrect.
|
|
|
|
--***************************************************************************/
|
|
LONGLONG
|
|
UlReadLongLongParameter(
|
|
IN HANDLE ParametersHandle,
|
|
IN PWCHAR ValueName,
|
|
IN LONGLONG DefaultValue
|
|
)
|
|
{
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION information;
|
|
UNICODE_STRING valueKeyName;
|
|
ULONG informationLength;
|
|
LONGLONG returnValue;
|
|
NTSTATUS status;
|
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(LONGLONG)];
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Build the value name, read it from the registry.
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&valueKeyName,
|
|
ValueName
|
|
);
|
|
|
|
information = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|
|
|
status = ZwQueryValueKey(
|
|
ParametersHandle,
|
|
&valueKeyName,
|
|
KeyValuePartialInformation,
|
|
(PVOID)information,
|
|
sizeof(buffer),
|
|
&informationLength
|
|
);
|
|
|
|
//
|
|
// If the read succeeded, the type is DWORD and the length is
|
|
// sane, use it. Otherwise, use the default.
|
|
//
|
|
|
|
if (status == STATUS_SUCCESS &&
|
|
information->Type == REG_QWORD &&
|
|
information->DataLength == sizeof(returnValue))
|
|
{
|
|
RtlMoveMemory( &returnValue, information->Data, sizeof(returnValue) );
|
|
} else {
|
|
returnValue = DefaultValue;
|
|
}
|
|
|
|
return returnValue;
|
|
|
|
} // UlReadLongLongParameter
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Reads a single free-form value from the registry.
|
|
|
|
Arguments:
|
|
|
|
ParametersHandle - Supplies an open registry handle.
|
|
|
|
ValueName - Supplies the name of the value to read.
|
|
|
|
Value - Receives the value read from the registry.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlReadGenericParameter(
|
|
IN HANDLE ParametersHandle,
|
|
IN PWCHAR ValueName,
|
|
OUT PKEY_VALUE_PARTIAL_INFORMATION * Value
|
|
)
|
|
{
|
|
|
|
KEY_VALUE_PARTIAL_INFORMATION partialInfo;
|
|
UNICODE_STRING valueKeyName;
|
|
ULONG informationLength;
|
|
NTSTATUS status;
|
|
PKEY_VALUE_PARTIAL_INFORMATION newValue;
|
|
ULONG dataLength;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Build the value name, then perform an initial read. The read
|
|
// should fail with buffer overflow, but that's OK. We just want
|
|
// to get the length of the data.
|
|
//
|
|
|
|
RtlInitUnicodeString( &valueKeyName, ValueName );
|
|
|
|
status = ZwQueryValueKey(
|
|
ParametersHandle,
|
|
&valueKeyName,
|
|
KeyValuePartialInformation,
|
|
(PVOID)&partialInfo,
|
|
sizeof(partialInfo),
|
|
&informationLength
|
|
);
|
|
|
|
if (NT_ERROR(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Determine the data length. Ensure that strings and multi-sz get
|
|
// properly terminated.
|
|
//
|
|
|
|
dataLength = partialInfo.DataLength - 1;
|
|
|
|
if (partialInfo.Type == REG_SZ || partialInfo.Type == REG_EXPAND_SZ)
|
|
{
|
|
dataLength += 1;
|
|
}
|
|
|
|
if (partialInfo.Type == REG_MULTI_SZ)
|
|
{
|
|
dataLength += 2;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffer.
|
|
//
|
|
|
|
newValue = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
PagedPool,
|
|
KEY_VALUE_PARTIAL_INFORMATION,
|
|
dataLength,
|
|
UL_REGISTRY_DATA_POOL_TAG
|
|
);
|
|
|
|
if (newValue == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// update the actually allocated length for later use
|
|
//
|
|
|
|
dataLength += sizeof(KEY_VALUE_PARTIAL_INFORMATION);
|
|
|
|
RtlZeroMemory( newValue, dataLength );
|
|
|
|
//
|
|
// Perform the actual read.
|
|
//
|
|
|
|
status = ZwQueryValueKey(
|
|
ParametersHandle,
|
|
&valueKeyName,
|
|
KeyValuePartialInformation,
|
|
(PVOID)(newValue),
|
|
dataLength,
|
|
&informationLength
|
|
);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*Value = newValue;
|
|
}
|
|
else
|
|
{
|
|
UL_FREE_POOL( newValue, UL_REGISTRY_DATA_POOL_TAG );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlReadGenericParameter
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Builds a properly formatted device control IRP.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the IRP to format.
|
|
|
|
IoControlCode - Supplies the device IO control code.
|
|
|
|
InputBuffer - Supplies the input buffer.
|
|
|
|
InputBufferLength - Supplies the length of InputBuffer.
|
|
|
|
OutputBuffer - Supplies the output buffer.
|
|
|
|
OutputBufferLength - Supplies the length of OutputBuffer.
|
|
|
|
MdlAddress - Supplies a MDL to attach to the IRP. This is assumed to
|
|
be a non-paged MDL.
|
|
|
|
FileObject - Supplies the file object for the target driver.
|
|
|
|
DeviceObject - Supplies the correct device object for the target
|
|
driver.
|
|
|
|
IoStatusBlock - Receives the final completion status of the request.
|
|
|
|
CompletionRoutine - Supplies a pointer to a completion routine to
|
|
call after the request completes. This will only be called if
|
|
this routine returns STATUS_PENDING.
|
|
|
|
CompletionContext - Supplies an uninterpreted context value passed
|
|
to the completion routine.
|
|
|
|
TargetThread - Optionally supplies a target thread for the IRP. If
|
|
this value is NULL, then the current thread is used.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlBuildDeviceControlIrp(
|
|
IN OUT PIRP Irp,
|
|
IN ULONG IoControlCode,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength,
|
|
IN PMDL MdlAddress,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
|
|
IN PVOID CompletionContext,
|
|
IN PETHREAD TargetThread OPTIONAL
|
|
)
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( Irp != NULL );
|
|
ASSERT( FileObject != NULL );
|
|
ASSERT( DeviceObject != NULL );
|
|
|
|
//
|
|
// Fill in the service independent parameters in the IRP.
|
|
//
|
|
|
|
Irp->Flags = 0;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->PendingReturned = FALSE;
|
|
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = NULL;
|
|
|
|
Irp->AssociatedIrp.SystemBuffer = InputBuffer ? InputBuffer : OutputBuffer;
|
|
Irp->UserBuffer = OutputBuffer;
|
|
Irp->MdlAddress = MdlAddress;
|
|
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
|
|
Irp->Tail.Overlay.Thread = TargetThread ? TargetThread : PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
|
|
//
|
|
// Put the file object pointer in the stack location.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( Irp );
|
|
irpSp->FileObject = FileObject;
|
|
irpSp->DeviceObject = DeviceObject;
|
|
|
|
//
|
|
// Fill in the service dependent parameters in the IRP stack.
|
|
//
|
|
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
|
|
|
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = 0;
|
|
|
|
//
|
|
// Set the completion routine appropriately.
|
|
//
|
|
|
|
if (CompletionRoutine == NULL)
|
|
{
|
|
IoSetCompletionRoutine(
|
|
Irp,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
IoSetCompletionRoutine(
|
|
Irp,
|
|
CompletionRoutine,
|
|
CompletionContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
} // UlBuildDeviceControlIrp
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts the given ULONGLLONG to an ASCII representation and stores it
|
|
in the given string.
|
|
|
|
Arguments:
|
|
|
|
String - Receives the ASCII representation of the ULONGLONG.
|
|
|
|
Value - Supplies the ULONGLONG to convert.
|
|
|
|
Return Value:
|
|
|
|
PSTR - Pointer to the next character in String *after* the converted
|
|
ULONGLONG.
|
|
|
|
--***************************************************************************/
|
|
PSTR
|
|
UlULongLongToAscii(
|
|
IN PSTR String,
|
|
IN ULONGLONG Value
|
|
)
|
|
{
|
|
PSTR p1;
|
|
PSTR p2;
|
|
CHAR ch;
|
|
ULONG digit;
|
|
|
|
//
|
|
// Special case 0 to make the rest of the routine simpler.
|
|
//
|
|
|
|
if (Value == 0)
|
|
{
|
|
*String++ = '0';
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Convert the ULONG. Note that this will result in the string
|
|
// being backwards in memory.
|
|
//
|
|
|
|
p1 = String;
|
|
p2 = String;
|
|
|
|
while (Value != 0)
|
|
{
|
|
digit = (ULONG)( Value % 10 );
|
|
Value = Value / 10;
|
|
*p1++ = '0' + (CHAR)digit;
|
|
}
|
|
|
|
//
|
|
// Reverse the string.
|
|
//
|
|
|
|
String = p1;
|
|
p1--;
|
|
|
|
while (p1 > p2)
|
|
{
|
|
ch = *p1;
|
|
*p1 = *p2;
|
|
*p2 = ch;
|
|
|
|
p2++;
|
|
p1--;
|
|
}
|
|
}
|
|
|
|
*String = '\0';
|
|
return String;
|
|
|
|
} // UlULongLongToAscii
|
|
|
|
NTSTATUS
|
|
_RtlIntegerToUnicode(
|
|
IN ULONG Value,
|
|
IN ULONG Base OPTIONAL,
|
|
IN LONG BufferLength,
|
|
OUT PWSTR String
|
|
)
|
|
{
|
|
PWSTR p1;
|
|
PWSTR p2;
|
|
WCHAR ch;
|
|
ULONG digit;
|
|
|
|
//
|
|
// Special case 0 to make the rest of the routine simpler.
|
|
//
|
|
|
|
if (Value == 0)
|
|
{
|
|
*String++ = L'0';
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Convert the ULONG. Note that this will result in the string
|
|
// being backwards in memory.
|
|
//
|
|
|
|
p1 = String;
|
|
p2 = String;
|
|
|
|
while (Value != 0)
|
|
{
|
|
digit = (ULONG)( Value % 10 );
|
|
Value = Value / 10;
|
|
*p1++ = L'0' + (WCHAR)digit;
|
|
}
|
|
|
|
//
|
|
// Reverse the string.
|
|
//
|
|
|
|
String = p1;
|
|
p1--;
|
|
|
|
while (p1 > p2)
|
|
{
|
|
ch = *p1;
|
|
*p1 = *p2;
|
|
*p2 = ch;
|
|
|
|
p2++;
|
|
p1--;
|
|
}
|
|
}
|
|
|
|
*String = L'\0';
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // _RtlIntegerToUnicode
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts an ansi string to an integer. fails if any non-digit characters
|
|
appears in the string. fails on negative numbers, and assumes no preceding
|
|
spaces.
|
|
|
|
Arguments:
|
|
|
|
PUCHAR pString the string to convert
|
|
ULONG Base the base, must be 10 or 16
|
|
PULONG pValue the return value of the converted integer
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlAnsiToULongLong(
|
|
PUCHAR pString,
|
|
ULONG Base,
|
|
PULONGLONG pValue
|
|
)
|
|
{
|
|
ULONGLONG Value;
|
|
ULONGLONG NewValue;
|
|
|
|
if (Base != 10 && Base != 16)
|
|
RETURN(STATUS_INVALID_PARAMETER);
|
|
|
|
//
|
|
// No preceding space, we already skipped it
|
|
//
|
|
|
|
ASSERT(IS_HTTP_LWS(pString[0]) == FALSE);
|
|
|
|
Value = 0;
|
|
|
|
while (pString[0] != ANSI_NULL)
|
|
{
|
|
if (
|
|
(Base == 10 && IS_HTTP_DIGIT(pString[0]) == FALSE) ||
|
|
(Base == 16 && IS_HTTP_HEX(pString[0]) == FALSE)
|
|
)
|
|
{
|
|
//
|
|
// Not valid , bad!
|
|
//
|
|
|
|
RETURN(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (Base == 16)
|
|
{
|
|
if (IS_HTTP_ALPHA(pString[0]))
|
|
{
|
|
NewValue = 16 * Value + (UPCASE_CHAR(pString[0]) - 'A' + 10);
|
|
}
|
|
else
|
|
{
|
|
NewValue = 16 * Value + (pString[0] - '0');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewValue = 10 * Value + (pString[0] - '0');
|
|
}
|
|
|
|
if (NewValue < Value)
|
|
{
|
|
//
|
|
// Very bad... we overflew
|
|
//
|
|
|
|
RETURN(STATUS_SECTION_TOO_BIG);
|
|
}
|
|
|
|
Value = NewValue;
|
|
|
|
pString += 1;
|
|
}
|
|
|
|
*pValue = Value;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlAnsiToULongLong
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts a unicode string to an integer. fails if any non-digit characters
|
|
appear in the string. fails on negative numbers, and assumes no preceding
|
|
spaces.
|
|
|
|
Arguments:
|
|
|
|
PWCHAR pString the string to convert
|
|
ULONG Base the base, must be 10 or 16
|
|
PULONG pValue the return value of the converted integer
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlUnicodeToULongLong(
|
|
PWCHAR pString,
|
|
ULONG Base,
|
|
PULONGLONG pValue
|
|
)
|
|
{
|
|
ULONGLONG Value;
|
|
ULONGLONG NewValue;
|
|
|
|
if (Base != 10 && Base != 16)
|
|
RETURN(STATUS_INVALID_PARAMETER);
|
|
|
|
//
|
|
// No preceding space, we already skipped it
|
|
//
|
|
|
|
ASSERT(pString[0] < 128 && IS_HTTP_LWS(pString[0]) == FALSE);
|
|
|
|
Value = 0;
|
|
|
|
while (pString[0] != UNICODE_NULL)
|
|
{
|
|
if ((Base == 10 &&
|
|
(pString[0] >= 128 || IS_HTTP_DIGIT(pString[0]) == FALSE)) ||
|
|
(Base == 16 &&
|
|
(pString[0] >= 128 || IS_HTTP_HEX(pString[0]) == FALSE)))
|
|
{
|
|
//
|
|
// Not valid , bad!
|
|
//
|
|
|
|
RETURN(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (Base == 16)
|
|
{
|
|
if (IS_HTTP_ALPHA(pString[0]))
|
|
{
|
|
NewValue = 16 * Value + (pString[0] - L'A' + 10);
|
|
}
|
|
else
|
|
{
|
|
NewValue = 16 * Value + (pString[0] - L'0');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewValue = 10 * Value + (pString[0] - L'0');
|
|
}
|
|
|
|
if (NewValue < Value)
|
|
{
|
|
//
|
|
// Very bad... we overflew
|
|
//
|
|
|
|
RETURN(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
Value = NewValue;
|
|
|
|
pString += 1;
|
|
}
|
|
|
|
*pValue = Value;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlUnicodeToULongLong
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Synchronously issues a device control request to the TDI provider.
|
|
|
|
Arguments:
|
|
|
|
pTdiObject - Supplies a pointer to the TDI object.
|
|
|
|
pIrpParameters - Supplies a pointer to the IRP parameters.
|
|
|
|
IrpParametersLength - Supplies the length of pIrpParameters.
|
|
|
|
pMdlBuffer - Optionally supplies a pointer to a buffer to be mapped
|
|
into a MDL and placed in the MdlAddress field of the IRP.
|
|
|
|
MdlBufferLength - Optionally supplies the length of pMdlBuffer.
|
|
|
|
MinorFunction - Supplies the minor function code of the request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlIssueDeviceControl(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN PVOID pIrpParameters,
|
|
IN ULONG IrpParametersLength,
|
|
IN PVOID pMdlBuffer OPTIONAL,
|
|
IN ULONG MdlBufferLength OPTIONAL,
|
|
IN UCHAR MinorFunction
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PIRP pIrp;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
UL_STATUS_BLOCK ulStatus;
|
|
PMDL pMdl;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the event that will signal I/O completion.
|
|
//
|
|
|
|
UlInitializeStatusBlock( &ulStatus );
|
|
|
|
//
|
|
// Set the file object event to the non-signaled state.
|
|
//
|
|
|
|
KeResetEvent( &pTdiObject->pFileObject->Event );
|
|
|
|
//
|
|
// Allocate an IRP for the request.
|
|
//
|
|
|
|
pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize, // StackSize
|
|
FALSE // ChargeQuota
|
|
);
|
|
|
|
if (pIrp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Establish the service independent parameters.
|
|
//
|
|
|
|
pIrp->Flags = IRP_SYNCHRONOUS_API;
|
|
pIrp->RequestorMode = KernelMode;
|
|
pIrp->PendingReturned = FALSE;
|
|
|
|
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
|
|
|
|
//
|
|
// If we have a MDL buffer, allocate a new MDL and map the
|
|
// buffer into it.
|
|
//
|
|
|
|
if (pMdlBuffer != NULL)
|
|
{
|
|
pMdl = UlAllocateMdl(
|
|
pMdlBuffer, // VirtualAddress
|
|
MdlBufferLength, // Length
|
|
FALSE, // SecondaryBuffer
|
|
FALSE, // ChargeQuota
|
|
pIrp // Irp
|
|
);
|
|
|
|
if (pMdl == NULL)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool( pMdl );
|
|
}
|
|
else
|
|
{
|
|
pIrp->MdlAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP stack location.
|
|
//
|
|
|
|
pIrpSp = IoGetNextIrpStackLocation( pIrp );
|
|
|
|
pIrpSp->FileObject = pTdiObject->pFileObject;
|
|
pIrpSp->DeviceObject = pTdiObject->pDeviceObject;
|
|
|
|
ASSERT( IrpParametersLength <= sizeof(pIrpSp->Parameters) );
|
|
RtlCopyMemory(
|
|
&pIrpSp->Parameters,
|
|
pIrpParameters,
|
|
IrpParametersLength
|
|
);
|
|
|
|
pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
pIrpSp->MinorFunction = MinorFunction;
|
|
|
|
//
|
|
// Reference the file object.
|
|
//
|
|
|
|
ObReferenceObject( pTdiObject->pFileObject );
|
|
|
|
//
|
|
// Establish a completion routine to free the MDL and dereference
|
|
// the FILE_OBJECT.
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
pIrp, // Irp
|
|
&UlpRestartDeviceControl, // CompletionRoutine
|
|
&ulStatus, // Context
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE // InvokeOnCancel
|
|
);
|
|
|
|
//
|
|
// Issue the request.
|
|
//
|
|
|
|
status = UlCallDriver( pTdiObject->pDeviceObject, pIrp );
|
|
|
|
//
|
|
// If necessary, wait for the request to complete and snag the
|
|
// final completion status.
|
|
//
|
|
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
UlWaitForStatusBlockEvent( &ulStatus );
|
|
status = ulStatus.IoStatus.Status;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlIssueDeviceControl
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_RECEIVE_BUFFER structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be sizeof(UL_RECEIVE_BUFFER), but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_RCV_BUFFER_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateReceiveBufferPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_RECEIVE_BUFFER pBuffer;
|
|
SIZE_T irpLength;
|
|
SIZE_T mdlLength;
|
|
SIZE_T ExtraLength;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == sizeof(UL_RECEIVE_BUFFER) );
|
|
ASSERT( Tag == UL_RCV_BUFFER_POOL_TAG );
|
|
|
|
//
|
|
// Calculate the required length of the buffer & allocate it.
|
|
//
|
|
|
|
irpLength = IoSizeOfIrp( g_UlIrpStackSize );
|
|
irpLength = ALIGN_UP( irpLength, PVOID );
|
|
|
|
mdlLength = MmSizeOfMdl( (PVOID)(PAGE_SIZE - 1), g_UlReceiveBufferSize );
|
|
mdlLength = ALIGN_UP( mdlLength, PVOID );
|
|
|
|
ExtraLength = irpLength + (mdlLength*2) + g_UlReceiveBufferSize;
|
|
|
|
ASSERT( ( ExtraLength & (sizeof(PVOID) - 1) ) == 0 );
|
|
|
|
pBuffer = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
NonPagedPool,
|
|
UL_RECEIVE_BUFFER,
|
|
ExtraLength,
|
|
UL_RCV_BUFFER_POOL_TAG
|
|
);
|
|
|
|
if (pBuffer != NULL)
|
|
{
|
|
PUCHAR pRawBuffer = (PUCHAR)(pBuffer);
|
|
|
|
//
|
|
// Initialize the IRP, MDL, and data pointers within the buffer.
|
|
//
|
|
// CODEWORK: the signature should be set in invalid here, but
|
|
// there's no wrapper around the PplAllocate/Free functions
|
|
// for this structure, so set it to the valid sig for now.
|
|
//
|
|
|
|
pBuffer->Signature = UL_RECEIVE_BUFFER_SIGNATURE;
|
|
pRawBuffer += ALIGN_UP( sizeof(UL_RECEIVE_BUFFER), PVOID );
|
|
pBuffer->pIrp = (PIRP)pRawBuffer;
|
|
pRawBuffer += irpLength;
|
|
pBuffer->pMdl = (PMDL)pRawBuffer;
|
|
pRawBuffer += mdlLength;
|
|
pBuffer->pPartialMdl = (PMDL)pRawBuffer;
|
|
pRawBuffer += mdlLength;
|
|
pBuffer->pDataArea = (PVOID)pRawBuffer;
|
|
pBuffer->UnreadDataLength = 0;
|
|
|
|
//
|
|
// Initialize the IRP.
|
|
//
|
|
|
|
IoInitializeIrp(
|
|
pBuffer->pIrp, // Irp
|
|
(USHORT)irpLength, // PacketSize
|
|
g_UlIrpStackSize // StackSize
|
|
);
|
|
|
|
//
|
|
// Initialize the primary MDL.
|
|
//
|
|
|
|
MmInitializeMdl(
|
|
pBuffer->pMdl, // MemoryDescriptorList
|
|
pBuffer->pDataArea, // BaseVa
|
|
g_UlReceiveBufferSize // Length
|
|
);
|
|
|
|
MmBuildMdlForNonPagedPool( pBuffer->pMdl );
|
|
}
|
|
|
|
return (PVOID)pBuffer;
|
|
|
|
} // UlAllocateReceiveBufferPool
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_RECEIVE_BUFFER structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeReceiveBufferPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_RECEIVE_BUFFER pReceiveBuffer;
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pReceiveBuffer = (PUL_RECEIVE_BUFFER)pBuffer;
|
|
|
|
//
|
|
// Kill the signature, then free it.
|
|
//
|
|
|
|
pReceiveBuffer->Signature = UL_RECEIVE_BUFFER_SIGNATURE_X;
|
|
UL_FREE_POOL( pReceiveBuffer, UL_RCV_BUFFER_POOL_TAG );
|
|
|
|
} // UlFreeReceiveBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_IRP_CONTEXT structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be sizeof(UL_IRP_CONTEXT), but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_IRP_CONTEXT_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateIrpContextPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == sizeof(UL_IRP_CONTEXT) );
|
|
ASSERT( Tag == UL_IRP_CONTEXT_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the IRP context.
|
|
//
|
|
|
|
pIrpContext = UL_ALLOCATE_STRUCT(
|
|
NonPagedPool,
|
|
UL_IRP_CONTEXT,
|
|
UL_IRP_CONTEXT_POOL_TAG
|
|
);
|
|
|
|
if (pIrpContext != NULL)
|
|
{
|
|
//
|
|
// Initialize it.
|
|
//
|
|
|
|
//
|
|
// CODEWORK: It's bogus for us to set the valid signature
|
|
// here. It should only be valid once the object is really
|
|
// in use.
|
|
//
|
|
pIrpContext->Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
|
|
#if DBG
|
|
pIrpContext->pCompletionRoutine = &UlDbgInvalidCompletionRoutine;
|
|
#endif
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
}
|
|
|
|
return (PVOID)pIrpContext;
|
|
|
|
} // UlAllocateIrpContextPool
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_IRP_CONTEXT structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeIrpContextPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT)pBuffer;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
//
|
|
// Kill the signature, then free it.
|
|
//
|
|
|
|
pIrpContext->Signature = UL_IRP_CONTEXT_SIGNATURE_X;
|
|
UL_FREE_POOL( pIrpContext, UL_IRP_CONTEXT_POOL_TAG );
|
|
|
|
} // UlFreeIrpContextPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_REQUEST_BUFFER structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be DEFAULT_MAX_REQUEST_BUFFER_SIZE but is basically
|
|
ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_REQUEST_BUFFER_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateRequestBufferPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_REQUEST_BUFFER pRequestBuffer;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == DEFAULT_MAX_REQUEST_BUFFER_SIZE );
|
|
ASSERT( Tag == UL_REQUEST_BUFFER_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the request buffer.
|
|
//
|
|
|
|
pRequestBuffer = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
NonPagedPool,
|
|
UL_REQUEST_BUFFER,
|
|
DEFAULT_MAX_REQUEST_BUFFER_SIZE,
|
|
UL_REQUEST_BUFFER_POOL_TAG
|
|
);
|
|
|
|
if (pRequestBuffer != NULL)
|
|
{
|
|
//
|
|
// Initialize it.
|
|
//
|
|
|
|
pRequestBuffer->Signature = MAKE_FREE_TAG(UL_REQUEST_BUFFER_POOL_TAG);
|
|
}
|
|
|
|
return (PVOID)pRequestBuffer;
|
|
|
|
} // UlAllocateRequestBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_REQUEST_BUFFER structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeRequestBufferPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_REQUEST_BUFFER pRequestBuffer;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pRequestBuffer = (PUL_REQUEST_BUFFER)pBuffer;
|
|
|
|
//
|
|
// Kill the signature, then free it.
|
|
//
|
|
|
|
UL_FREE_POOL_WITH_SIG(pRequestBuffer, UL_REQUEST_BUFFER_POOL_TAG);
|
|
|
|
} // UlFreeRequestBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_INTERNAL_REQUEST structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be sizeof(UL_INTERNAL_REQUEST) but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_INTERNAL_REQUEST_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateInternalRequestPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_INTERNAL_REQUEST pRequest;
|
|
PUL_FULL_TRACKER pTracker;
|
|
ULONG SpaceLength;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == sizeof(UL_INTERNAL_REQUEST) );
|
|
ASSERT( Tag == UL_INTERNAL_REQUEST_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the request buffer plus the default cooked URL buffer and
|
|
// the full tracker plus the auxiliary buffer.
|
|
//
|
|
|
|
SpaceLength =
|
|
g_UlFullTrackerSize +
|
|
(g_UlMaxInternalUrlLength/sizeof(WCHAR) + 1) * sizeof(WCHAR);
|
|
|
|
pRequest = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
NonPagedPool,
|
|
UL_INTERNAL_REQUEST,
|
|
SpaceLength,
|
|
UL_INTERNAL_REQUEST_POOL_TAG
|
|
);
|
|
|
|
if (pRequest != NULL)
|
|
{
|
|
//
|
|
// Initialize it.
|
|
//
|
|
|
|
pRequest->Signature = MAKE_FREE_TAG(UL_INTERNAL_REQUEST_POOL_TAG);
|
|
|
|
pRequest->pTracker =
|
|
(PUL_FULL_TRACKER)((PCHAR)pRequest +
|
|
ALIGN_UP(sizeof(UL_INTERNAL_REQUEST), PVOID));
|
|
|
|
pRequest->pUrlBuffer =
|
|
(PWSTR)((PCHAR)pRequest->pTracker + g_UlFullTrackerSize);
|
|
|
|
//
|
|
// Initialize the fast/cache tracker.
|
|
//
|
|
|
|
pTracker = pRequest->pTracker;
|
|
|
|
pTracker->Signature = UL_FULL_TRACKER_POOL_TAG;
|
|
pTracker->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
pTracker->IsFromLookaside = FALSE;
|
|
pTracker->IsFromRequest = TRUE;
|
|
pTracker->AuxilaryBufferLength =
|
|
g_UlMaxFixedHeaderSize +
|
|
g_UlMaxVariableHeaderSize +
|
|
g_UlMaxCopyThreshold;
|
|
|
|
UlInitializeFullTrackerPool( pTracker, DEFAULT_MAX_IRP_STACK_SIZE );
|
|
}
|
|
|
|
return (PVOID)pRequest;
|
|
|
|
} // UlAllocateInternalRequestPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_INTERNAL_REQUEST structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeInternalRequestPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_INTERNAL_REQUEST pRequest;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pRequest = (PUL_INTERNAL_REQUEST)pBuffer;
|
|
|
|
//
|
|
// Free the resource.
|
|
//
|
|
|
|
ASSERT( pRequest->Signature == MAKE_FREE_TAG(UL_INTERNAL_REQUEST_POOL_TAG));
|
|
|
|
//
|
|
// Kill the signature, then free it.
|
|
//
|
|
|
|
UL_FREE_POOL_WITH_SIG( pRequest, UL_INTERNAL_REQUEST_POOL_TAG );
|
|
|
|
} // UlFreeInternalRequestPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_CHUNK_TRACKER structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be g_UlChunkTrackerSize but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_CHUNK_TRACKER_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateChunkTrackerPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_CHUNK_TRACKER pTracker;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == g_UlChunkTrackerSize );
|
|
ASSERT( Tag == UL_CHUNK_TRACKER_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the tracker buffer.
|
|
//
|
|
|
|
pTracker = (PUL_CHUNK_TRACKER)UL_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
g_UlChunkTrackerSize,
|
|
UL_CHUNK_TRACKER_POOL_TAG
|
|
);
|
|
|
|
if (pTracker != NULL)
|
|
{
|
|
pTracker->Signature = MAKE_FREE_TAG(UL_CHUNK_TRACKER_POOL_TAG);
|
|
pTracker->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
pTracker->IsFromLookaside = TRUE;
|
|
|
|
//
|
|
// Set up the IRP.
|
|
//
|
|
|
|
pTracker->pReadIrp =
|
|
(PIRP)((PCHAR)pTracker +
|
|
ALIGN_UP(sizeof(UL_CHUNK_TRACKER), PVOID));
|
|
|
|
IoInitializeIrp(
|
|
pTracker->pReadIrp,
|
|
IoSizeOfIrp(DEFAULT_MAX_IRP_STACK_SIZE),
|
|
DEFAULT_MAX_IRP_STACK_SIZE
|
|
);
|
|
|
|
pTracker->pSendIrp =
|
|
(PIRP)((PCHAR)pTracker->pReadIrp +
|
|
ALIGN_UP(IoSizeOfIrp(DEFAULT_MAX_IRP_STACK_SIZE), PVOID));
|
|
|
|
IoInitializeIrp(
|
|
pTracker->pSendIrp,
|
|
IoSizeOfIrp(DEFAULT_MAX_IRP_STACK_SIZE),
|
|
DEFAULT_MAX_IRP_STACK_SIZE
|
|
);
|
|
|
|
//
|
|
// Set up the variable header pointer.
|
|
//
|
|
|
|
pTracker->pVariableHeader =
|
|
(PUCHAR)((PCHAR)pTracker->pSendIrp +
|
|
ALIGN_UP(IoSizeOfIrp(DEFAULT_MAX_IRP_STACK_SIZE), PVOID));
|
|
}
|
|
|
|
return pTracker;
|
|
|
|
} // UlAllocateChunkTrackerPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_CHUNK_TRACKER structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeChunkTrackerPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_CHUNK_TRACKER pTracker = (PUL_CHUNK_TRACKER)pBuffer;
|
|
|
|
UL_FREE_POOL_WITH_SIG( pTracker, UL_CHUNK_TRACKER_POOL_TAG );
|
|
|
|
} // UlFreeChunkTrackerPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_FULL_TRACKER structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be g_UlFullTrackerSize but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_FULL_TRACKER_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateFullTrackerPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_FULL_TRACKER pTracker;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == g_UlFullTrackerSize );
|
|
ASSERT( Tag == UL_FULL_TRACKER_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the tracker buffer.
|
|
//
|
|
|
|
pTracker = (PUL_FULL_TRACKER)UL_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
g_UlFullTrackerSize,
|
|
UL_FULL_TRACKER_POOL_TAG
|
|
);
|
|
|
|
if (pTracker != NULL)
|
|
{
|
|
pTracker->Signature = MAKE_FREE_TAG(UL_FULL_TRACKER_POOL_TAG);
|
|
pTracker->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
|
|
pTracker->IsFromLookaside = TRUE;
|
|
pTracker->IsFromRequest = FALSE;
|
|
pTracker->AuxilaryBufferLength =
|
|
g_UlMaxFixedHeaderSize +
|
|
g_UlMaxVariableHeaderSize +
|
|
g_UlMaxCopyThreshold;
|
|
|
|
UlInitializeFullTrackerPool( pTracker, DEFAULT_MAX_IRP_STACK_SIZE );
|
|
}
|
|
|
|
return pTracker;
|
|
|
|
} // UlAllocateFullTrackerPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_FULL_TRACKER structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeFullTrackerPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_FULL_TRACKER pTracker = (PUL_FULL_TRACKER)pBuffer;
|
|
|
|
UL_FREE_POOL_WITH_SIG( pTracker, UL_FULL_TRACKER_POOL_TAG );
|
|
|
|
} // UlFreeFullTrackerPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_INTERNAL_RESPONSE structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be NonPagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be g_UlResponseBufferSize but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_INTERNAL_RESPONSE_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateResponseBufferPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == g_UlResponseBufferSize );
|
|
ASSERT( Tag == UL_INTERNAL_RESPONSE_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the default internal response buffer.
|
|
//
|
|
|
|
return UL_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
g_UlResponseBufferSize,
|
|
UL_INTERNAL_RESPONSE_POOL_TAG
|
|
);
|
|
|
|
} // UlAllocateResponseBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_INTERNAL_RESPONSE structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeResponseBufferPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_INTERNAL_RESPONSE pResponseBuffer;
|
|
|
|
pResponseBuffer = (PUL_INTERNAL_RESPONSE)pBuffer;
|
|
|
|
UL_FREE_POOL_WITH_SIG( pResponseBuffer, UL_INTERNAL_RESPONSE_POOL_TAG );
|
|
|
|
} // UlFreeResponseBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates the pool necessary for a new UL_FILE_LOG_BUFFER structure and
|
|
initializes the structure.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the type of pool to allocate. This must always
|
|
be PagedPool.
|
|
|
|
ByteLength - Supplies the byte length for the allocation request.
|
|
This should be sizeof(UL_LOG_FILE_BUFFER) but is basically ignored.
|
|
|
|
Tag - Supplies the tag to use for the pool. This should be
|
|
UL_LOG_FILE_BUFFER_POOL_TAG, but is basically ignored.
|
|
|
|
Note: These parameters are required so that this function has a
|
|
signature identical to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the newly allocated block if successful, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlAllocateLogBufferPool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_LOG_FILE_BUFFER pLogBuffer;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == sizeof(UL_LOG_FILE_BUFFER) );
|
|
ASSERT( Tag == UL_LOG_FILE_BUFFER_POOL_TAG );
|
|
|
|
//
|
|
// Allocate the default log buffer.
|
|
//
|
|
|
|
pLogBuffer = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
PagedPool,
|
|
UL_LOG_FILE_BUFFER,
|
|
g_UlLogBufferSize,
|
|
UL_LOG_FILE_BUFFER_POOL_TAG
|
|
);
|
|
|
|
if ( pLogBuffer != NULL )
|
|
{
|
|
pLogBuffer->Signature = MAKE_FREE_TAG(UL_LOG_FILE_BUFFER_POOL_TAG);
|
|
pLogBuffer->BufferUsed = 0;
|
|
pLogBuffer->Buffer = (PUCHAR) (pLogBuffer + 1);
|
|
}
|
|
|
|
return pLogBuffer;
|
|
|
|
} // UlAllocateLogBufferPool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees the pool allocated for a UL_LOG_FILE_BUFFER structure.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - Supplies the buffer to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeLogBufferPool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_LOG_FILE_BUFFER pLogBuffer;
|
|
|
|
pLogBuffer = (PUL_LOG_FILE_BUFFER) pBuffer;
|
|
|
|
UL_FREE_POOL_WITH_SIG( pLogBuffer, UL_LOG_FILE_BUFFER_POOL_TAG );
|
|
|
|
} // UlFreeLogBufferPool
|
|
|
|
|
|
//
|
|
// Private routines.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for device control IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request. In
|
|
this case, it's a pointer to a UL_STATUS_BLOCK structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpRestartDeviceControl(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_STATUS_BLOCK pStatus;
|
|
|
|
//
|
|
// If we attached an MDL to the IRP, then free it here and reset
|
|
// the MDL pointer to NULL. IO can't handle a nonpaged MDL in an
|
|
// IRP, so we do it here.
|
|
//
|
|
|
|
if (pIrp->MdlAddress != NULL)
|
|
{
|
|
UlFreeMdl( pIrp->MdlAddress );
|
|
pIrp->MdlAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// Complete the request.
|
|
//
|
|
|
|
pStatus = (PUL_STATUS_BLOCK)pContext;
|
|
|
|
UlSignalStatusBlock(
|
|
pStatus,
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information
|
|
);
|
|
|
|
//
|
|
// Tell IO to continue processing this IRP.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlpRestartDeviceControl
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to initialize the utilitu code.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
NTSTATUS
|
|
InitializeHttpUtil(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG i;
|
|
UCHAR c;
|
|
|
|
// Initialize the HttpChars array appropriately.
|
|
|
|
for (i = 0; i < 128; i++)
|
|
{
|
|
HttpChars[i] = HTTP_CHAR;
|
|
}
|
|
|
|
for (i = 'A'; i <= 'Z'; i++)
|
|
{
|
|
HttpChars[i] |= HTTP_UPCASE;
|
|
}
|
|
|
|
for (i = 'a'; i <= 'z'; i++)
|
|
{
|
|
HttpChars[i] |= HTTP_LOCASE;
|
|
}
|
|
|
|
for (i = '0'; i <= '9'; i++)
|
|
{
|
|
HttpChars[i] |= (HTTP_DIGIT | HTTP_HEX);
|
|
}
|
|
|
|
|
|
for (i = 0; i <= 31; i++)
|
|
{
|
|
HttpChars[i] |= HTTP_CTL;
|
|
}
|
|
|
|
HttpChars[127] |= HTTP_CTL;
|
|
|
|
HttpChars[SP] |= HTTP_LWS;
|
|
HttpChars[HT] |= HTTP_LWS;
|
|
|
|
|
|
for (i = 'A'; i <= 'F'; i++)
|
|
{
|
|
HttpChars[i] |= HTTP_HEX;
|
|
}
|
|
|
|
for (i = 'a'; i <= 'f'; i++)
|
|
{
|
|
HttpChars[i] |= HTTP_HEX;
|
|
}
|
|
|
|
HttpChars['('] |= HTTP_SEPERATOR;
|
|
HttpChars[')'] |= HTTP_SEPERATOR;
|
|
HttpChars['<'] |= HTTP_SEPERATOR;
|
|
HttpChars['>'] |= HTTP_SEPERATOR;
|
|
HttpChars['@'] |= HTTP_SEPERATOR;
|
|
HttpChars[','] |= HTTP_SEPERATOR;
|
|
HttpChars[';'] |= HTTP_SEPERATOR;
|
|
HttpChars[':'] |= HTTP_SEPERATOR;
|
|
HttpChars['\\'] |= HTTP_SEPERATOR;
|
|
HttpChars['"'] |= HTTP_SEPERATOR;
|
|
HttpChars['/'] |= HTTP_SEPERATOR;
|
|
HttpChars['['] |= HTTP_SEPERATOR;
|
|
HttpChars[']'] |= HTTP_SEPERATOR;
|
|
HttpChars['?'] |= HTTP_SEPERATOR;
|
|
HttpChars['='] |= HTTP_SEPERATOR;
|
|
HttpChars['{'] |= HTTP_SEPERATOR;
|
|
HttpChars['}'] |= HTTP_SEPERATOR;
|
|
HttpChars[SP] |= HTTP_SEPERATOR;
|
|
HttpChars[HT] |= HTTP_SEPERATOR;
|
|
|
|
|
|
//
|
|
// URL "reserved" characters (rfc2396)
|
|
//
|
|
|
|
HttpChars[';'] |= URL_LEGAL;
|
|
HttpChars['/'] |= URL_LEGAL;
|
|
HttpChars['\\'] |= URL_LEGAL;
|
|
HttpChars['?'] |= URL_LEGAL;
|
|
HttpChars[':'] |= URL_LEGAL;
|
|
HttpChars['@'] |= URL_LEGAL;
|
|
HttpChars['&'] |= URL_LEGAL;
|
|
HttpChars['='] |= URL_LEGAL;
|
|
HttpChars['+'] |= URL_LEGAL;
|
|
HttpChars['$'] |= URL_LEGAL;
|
|
HttpChars[','] |= URL_LEGAL;
|
|
|
|
|
|
//
|
|
// URL escape character
|
|
//
|
|
|
|
HttpChars['%'] |= URL_LEGAL;
|
|
|
|
//
|
|
// URL "mark" characters (rfc2396)
|
|
//
|
|
|
|
HttpChars['-'] |= URL_LEGAL;
|
|
HttpChars['_'] |= URL_LEGAL;
|
|
HttpChars['.'] |= URL_LEGAL;
|
|
HttpChars['!'] |= URL_LEGAL;
|
|
HttpChars['~'] |= URL_LEGAL;
|
|
HttpChars['*'] |= URL_LEGAL;
|
|
HttpChars['\''] |= URL_LEGAL;
|
|
HttpChars['('] |= URL_LEGAL;
|
|
HttpChars[')'] |= URL_LEGAL;
|
|
|
|
|
|
//
|
|
// RFC2396 describes these characters as `unwise' "because gateways and
|
|
// other transport agents are known to sometimes modify such characters,
|
|
// or they are used as delimiters". However, for compatibility with IIS
|
|
// 5.0 and DAV, we must allow these unwise characters in URLs.
|
|
//
|
|
|
|
HttpChars['{'] |= URL_LEGAL;
|
|
HttpChars['}'] |= URL_LEGAL;
|
|
HttpChars['|'] |= URL_LEGAL;
|
|
HttpChars['^'] |= URL_LEGAL;
|
|
HttpChars['['] |= URL_LEGAL;
|
|
HttpChars[']'] |= URL_LEGAL;
|
|
HttpChars['`'] |= URL_LEGAL;
|
|
|
|
//
|
|
// These US-ASCII characters are "excluded"; i.e. not URL_LEGAL (see RFC):
|
|
// '<' | '>' | '#' | '%' | '"' (0x22) | ' ' (0x20)
|
|
// In addition, control characters (0x00-0x1F and 0x7F) and
|
|
// non US-ASCII characters (0x80-0xFF) are not URL_LEGAL.
|
|
//
|
|
|
|
for (i = 0; i < 128; i++)
|
|
{
|
|
if (!IS_HTTP_SEPERATOR(i) && !IS_HTTP_CTL(i))
|
|
{
|
|
HttpChars[i] |= HTTP_TOKEN;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Fast path for PopChar
|
|
//
|
|
|
|
RtlZeroMemory(FastPopChars, 256 * sizeof(USHORT));
|
|
RtlZeroMemory(DummyPopChars, 256 * sizeof(USHORT));
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
c = (UCHAR)i;
|
|
|
|
if (IS_URL_TOKEN(c) && c != '%' && (c & 0x80) != 0x80)
|
|
{
|
|
FastPopChars[i] = (USHORT)c;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Turn backslashes into forward slashes
|
|
//
|
|
|
|
FastPopChars['\\'] = L'/';
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates brand new UL_NONPAGED_RESOURCE structures from the
|
|
lookaside lists
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
the resource. NULL = out of memory.
|
|
|
|
--***************************************************************************/
|
|
PUL_NONPAGED_RESOURCE
|
|
UlResourceNew(
|
|
ULONG OwnerTag
|
|
)
|
|
{
|
|
PUL_NONPAGED_RESOURCE pResource;
|
|
|
|
pResource = (PUL_NONPAGED_RESOURCE)(
|
|
PplAllocate(
|
|
g_pUlNonpagedData->ResourceLookaside
|
|
)
|
|
);
|
|
|
|
if (pResource != NULL)
|
|
{
|
|
pResource->Signature = UL_NONPAGED_RESOURCE_SIGNATURE;
|
|
#if DBG
|
|
pResource->Resource.OwnerTag = OwnerTag;
|
|
#endif
|
|
pResource->RefCount = 1;
|
|
|
|
UlTrace(
|
|
REFCOUNT,
|
|
("ul!UlResourceNew res=%p tag=0x%08x refcount=%d\n",
|
|
pResource, OwnerTag,
|
|
pResource->RefCount)
|
|
);
|
|
|
|
}
|
|
|
|
return pResource;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Reference a resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - the resource
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlReferenceResource(
|
|
PUL_NONPAGED_RESOURCE pResource
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IS_VALID_UL_NONPAGED_RESOURCE(pResource));
|
|
|
|
refCount = InterlockedIncrement( &pResource->RefCount );
|
|
|
|
UlTrace(
|
|
REFCOUNT,
|
|
("ul!UlReferenceResource res=%p refcount=%d\n",
|
|
pResource,
|
|
refCount)
|
|
);
|
|
|
|
} // UlReferenceResource
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Dereference a resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - the resource
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDereferenceResource(
|
|
PUL_NONPAGED_RESOURCE pResource
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IS_VALID_UL_NONPAGED_RESOURCE(pResource));
|
|
|
|
refCount = InterlockedDecrement( &pResource->RefCount );
|
|
|
|
UlTrace(
|
|
REFCOUNT,
|
|
("ul!UlDereferenceResource res=%p refcount=%d\n",
|
|
pResource,
|
|
refCount)
|
|
);
|
|
|
|
if (refCount == 0)
|
|
{
|
|
//
|
|
// free it
|
|
//
|
|
|
|
PplFree(
|
|
g_pUlNonpagedData->ResourceLookaside,
|
|
pResource
|
|
);
|
|
}
|
|
|
|
} // UlDereferenceResource
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
support function for lookasides to allocate the memory for
|
|
UL_NONPAGED_RESOURCEs
|
|
|
|
Arguments:
|
|
|
|
PoolType - the pool type
|
|
|
|
ByteLength - how much to alloc
|
|
|
|
Tag - the tag to use
|
|
|
|
Return Value:
|
|
|
|
nt status code
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlResourceAllocatePool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T ByteLength,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PUL_NONPAGED_RESOURCE pResource;
|
|
|
|
ASSERT( PoolType == NonPagedPool );
|
|
ASSERT( ByteLength == sizeof(UL_NONPAGED_RESOURCE) );
|
|
ASSERT( Tag == UL_NONPAGED_RESOURCE_POOL_TAG );
|
|
|
|
|
|
pResource = UL_ALLOCATE_STRUCT(
|
|
NonPagedPool,
|
|
UL_NONPAGED_RESOURCE,
|
|
UL_NONPAGED_RESOURCE_POOL_TAG
|
|
);
|
|
|
|
if (pResource != NULL)
|
|
{
|
|
pResource->Signature = UL_NONPAGED_RESOURCE_SIGNATURE;
|
|
pResource->RefCount = 1;
|
|
|
|
Status = UlInitializeResource(
|
|
&pResource->Resource,
|
|
"UL_NONPAGED_RESOURCE[%p].Resource",
|
|
pResource,
|
|
UL_NONPAGED_RESOURCE_POOL_TAG
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) == FALSE)
|
|
{
|
|
UL_FREE_POOL_WITH_SIG(pResource, UL_NONPAGED_RESOURCE_POOL_TAG);
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
return (PVOID)(pResource);
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
support function for lookasides to free the memory for
|
|
UL_NONPAGED_RESOURCE's
|
|
|
|
Arguments:
|
|
|
|
pBuffer - the UL_NONPAGED_RESOURCE buffer to free
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlResourceFreePool(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
PUL_NONPAGED_RESOURCE pResource;
|
|
NTSTATUS Status;
|
|
|
|
pResource = (PUL_NONPAGED_RESOURCE)(pBuffer);
|
|
|
|
ASSERT(pResource != NULL);
|
|
ASSERT(pResource->Signature == UL_NONPAGED_RESOURCE_SIGNATURE);
|
|
|
|
pResource->Signature = UL_NONPAGED_RESOURCE_SIGNATURE_X;
|
|
|
|
|
|
Status = UlDeleteResource(&pResource->Resource);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
UL_FREE_POOL_WITH_SIG(
|
|
pResource,
|
|
UL_NONPAGED_RESOURCE_POOL_TAG
|
|
);
|
|
|
|
} // UlResourceFreePool
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Invokes the completion routine (if specified) and determines the
|
|
appropriate return code. This routine ensures that, if the completion
|
|
routine is invoked, the caller always returns STATUS_PENDING.
|
|
|
|
Arguments:
|
|
|
|
Status - Supplies the completion status.
|
|
|
|
Information - Optionally supplies additional information about
|
|
the completed operation, such as the number of bytes
|
|
transferred.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status. Will always be STATUS_PENDING if the
|
|
completion routine is invoked.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlInvokeCompletionRoutine(
|
|
IN NTSTATUS Status,
|
|
IN ULONG_PTR Information,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
if (pCompletionRoutine != NULL)
|
|
{
|
|
(pCompletionRoutine)(
|
|
pCompletionContext,
|
|
Status,
|
|
Information
|
|
);
|
|
|
|
Status = STATUS_PENDING;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // UlInvokeCompletionRoutine
|
|
|
|
|
|
//
|
|
// constants used by the date formatter
|
|
//
|
|
|
|
const PWSTR pDays[] =
|
|
{
|
|
L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat"
|
|
};
|
|
|
|
const PWSTR pMonths[] =
|
|
{
|
|
L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul",
|
|
L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"
|
|
};
|
|
|
|
__inline
|
|
VOID
|
|
TwoDigitsToUnicode(
|
|
PWSTR pBuffer,
|
|
ULONG Number
|
|
)
|
|
{
|
|
pBuffer[0] = L'0' + (WCHAR)(Number / 10);
|
|
pBuffer[1] = L'0' + (WCHAR)(Number % 10);
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts the given system time to string representation containing
|
|
GMT Formatted String.
|
|
|
|
Arguments:
|
|
|
|
pTime - System time that needs to be converted.
|
|
|
|
pBuffer - pointer to string which will contain the GMT time on
|
|
successful return.
|
|
|
|
BufferLength - size of pszBuff in bytes
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
History:
|
|
|
|
MuraliK 3-Jan-1995
|
|
paulmcd 4-Mar-1999 copied to ul
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
TimeFieldsToHttpDate(
|
|
IN PTIME_FIELDS pTime,
|
|
OUT PWSTR pBuffer,
|
|
IN ULONG BufferLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(pBuffer != NULL);
|
|
|
|
if (BufferLength < (HTTP_DATE_COUNT + 1)*sizeof(WCHAR))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// 0 1 2
|
|
// 01234567890123456789012345678
|
|
// Formats a string like: "Thu, 14 Jul 1994 15:26:05 GMT"
|
|
//
|
|
|
|
//
|
|
// write the constants
|
|
//
|
|
|
|
pBuffer[3] = L',';
|
|
pBuffer[4] = pBuffer[7] = pBuffer[11] = L' ';
|
|
pBuffer[19] = pBuffer[22] = L':';
|
|
|
|
//
|
|
// now the variants
|
|
//
|
|
|
|
//
|
|
// 0-based Weekday
|
|
//
|
|
|
|
RtlCopyMemory(&(pBuffer[0]), pDays[pTime->Weekday], 3*sizeof(WCHAR));
|
|
|
|
TwoDigitsToUnicode(&(pBuffer[5]), pTime->Day);
|
|
|
|
//
|
|
// 1-based Month
|
|
//
|
|
|
|
RtlCopyMemory(&(pBuffer[8]), pMonths[pTime->Month - 1], 3*sizeof(WCHAR)); // 1-based
|
|
|
|
Status = _RtlIntegerToUnicode(pTime->Year, 10, 5, &(pBuffer[12]));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
pBuffer[16] = L' ';
|
|
|
|
TwoDigitsToUnicode(&(pBuffer[17]), pTime->Hour);
|
|
TwoDigitsToUnicode(&(pBuffer[20]), pTime->Minute);
|
|
TwoDigitsToUnicode(&(pBuffer[23]), pTime->Second);
|
|
|
|
RtlCopyMemory(&(pBuffer[25]), L" GMT", sizeof(L" GMT"));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // TimeFieldsToHttpDate
|
|
|
|
|
|
__inline
|
|
SHORT
|
|
FASTCALL
|
|
AsciiToShort(
|
|
PCHAR pString
|
|
)
|
|
{
|
|
return (SHORT)atoi(pString);
|
|
}
|
|
|
|
|
|
__inline
|
|
SHORT
|
|
FASTCALL
|
|
TwoAsciisToShort(
|
|
PCHAR pString
|
|
)
|
|
{
|
|
SHORT Value;
|
|
SHORT Number;
|
|
|
|
Number = pString[1] - '0';
|
|
|
|
if (Number <= 9)
|
|
{
|
|
Value = Number;
|
|
Number = pString[0] - '0';
|
|
|
|
if (Number <= 9)
|
|
{
|
|
Value += Number * 10;
|
|
return Value;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
DateTime function ported from user mode W3SVC
|
|
--***************************************************************************/
|
|
|
|
/************************************************************
|
|
* Data
|
|
************************************************************/
|
|
|
|
static const PSTR s_rgchMonths[] = {
|
|
"Jan", "Feb", "Mar", "Apr",
|
|
"May", "Jun", "Jul", "Aug",
|
|
"Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
// Custom hash table for NumericToAsciiMonth() for mapping "Apr" to 4
|
|
static const CHAR MonthIndexTable[64] = {
|
|
-1,'A', 2, 12, -1, -1, -1, 8, // A to G
|
|
-1, -1, -1, -1, 7, -1,'N', -1, // F to O
|
|
9, -1,'R', -1, 10, -1, 11, -1, // P to W
|
|
-1, 5, -1, -1, -1, -1, -1, -1, // X to Z
|
|
-1,'A', 2, 12, -1, -1, -1, 8, // a to g
|
|
-1, -1, -1, -1, 7, -1,'N', -1, // f to o
|
|
9, -1,'R', -1, 10, -1, 11, -1, // p to w
|
|
-1, 5, -1, -1, -1, -1, -1, -1 // x to z
|
|
};
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
/***************************************************************************++
|
|
|
|
Converts three letters of a month to numeric month
|
|
|
|
Arguments:
|
|
s String to convert
|
|
|
|
Returns:
|
|
numeric equivalent, 0 on failure.
|
|
|
|
--***************************************************************************/
|
|
__inline
|
|
SHORT
|
|
FASTCALL
|
|
NumericToAsciiMonth(
|
|
PCHAR s
|
|
)
|
|
{
|
|
UCHAR monthIndex;
|
|
UCHAR c;
|
|
PSTR monthString;
|
|
|
|
//
|
|
// use the third character as the index
|
|
//
|
|
|
|
c = (s[2] - 0x40) & 0x3F;
|
|
|
|
monthIndex = MonthIndexTable[c];
|
|
|
|
if ( monthIndex < 13 ) {
|
|
goto verify;
|
|
}
|
|
|
|
//
|
|
// ok, we need to look at the second character
|
|
//
|
|
|
|
if ( monthIndex == 'N' ) {
|
|
|
|
//
|
|
// we got an N which we need to resolve further
|
|
//
|
|
|
|
//
|
|
// if s[1] is 'u' then Jun, if 'a' then Jan
|
|
//
|
|
|
|
if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
|
|
monthIndex = 1;
|
|
} else {
|
|
monthIndex = 6;
|
|
}
|
|
|
|
} else if ( monthIndex == 'R' ) {
|
|
|
|
//
|
|
// if s[1] is 'a' then March, if 'p' then April
|
|
//
|
|
|
|
if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
|
|
monthIndex = 3;
|
|
} else {
|
|
monthIndex = 4;
|
|
}
|
|
} else {
|
|
goto error_exit;
|
|
}
|
|
|
|
verify:
|
|
|
|
monthString = (PSTR) s_rgchMonths[monthIndex-1];
|
|
|
|
if ( (s[0] == monthString[0]) &&
|
|
(s[1] == monthString[1]) &&
|
|
(s[2] == monthString[2]) ) {
|
|
|
|
return(monthIndex);
|
|
|
|
} else if ( (toupper(s[0]) == monthString[0]) &&
|
|
(tolower(s[1]) == monthString[1]) &&
|
|
(tolower(s[2]) == monthString[2]) ) {
|
|
|
|
return monthIndex;
|
|
}
|
|
|
|
error_exit:
|
|
return(0);
|
|
|
|
} // NumericToAsciiMonth
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Converts a string representation of a GMT time (three different
|
|
varieties) to an NT representation of a file time.
|
|
|
|
We handle the following variations:
|
|
|
|
Sun, 06 Nov 1994 08:49:37 GMT (RFC 822 updated by RFC 1123)
|
|
Sunday, 06-Nov-94 08:49:37 GMT (RFC 850)
|
|
Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format
|
|
|
|
Arguments:
|
|
pszTime String representation of time field
|
|
pliTime large integer containing the time in NT format.
|
|
|
|
Returns:
|
|
TRUE on success and FALSE on failure.
|
|
|
|
History:
|
|
|
|
Johnl 24-Jan-1995 Modified from WWW library
|
|
ericsten 30-Nov-2000 Ported from user-mode W3SVC
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
StringTimeToSystemTime(
|
|
IN const PSTR pszTime,
|
|
OUT LARGE_INTEGER * pliTime
|
|
)
|
|
{
|
|
|
|
PCHAR s;
|
|
TIME_FIELDS st;
|
|
|
|
if (pszTime == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
st.Milliseconds = 0;
|
|
|
|
if ((s = strchr(pszTime, ','))) {
|
|
|
|
ULONG len;
|
|
|
|
//
|
|
// Thursday, 10-Jun-93 01:29:59 GMT
|
|
// or: Thu, 10 Jan 1993 01:29:59 GMT */
|
|
//
|
|
|
|
s++;
|
|
|
|
while (*s && *s==' ') s++;
|
|
len = strlen(s);
|
|
|
|
if (len < 18) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *(s+2) == '-' ) { /* First format */
|
|
|
|
st.Day = AsciiToShort(s);
|
|
st.Month = NumericToAsciiMonth(s+3);
|
|
st.Year = AsciiToShort(s+7);
|
|
st.Hour = AsciiToShort(s+10);
|
|
st.Minute = AsciiToShort(s+13);
|
|
st.Second = AsciiToShort(s+16);
|
|
|
|
} else { /* Second format */
|
|
|
|
if (len < 20) {
|
|
return FALSE;
|
|
}
|
|
|
|
st.Day = TwoAsciisToShort(s);
|
|
st.Month = NumericToAsciiMonth(s+3);
|
|
st.Year = TwoAsciisToShort(s+7) * 100 + TwoAsciisToShort(s+9);
|
|
st.Hour = TwoAsciisToShort(s+12);
|
|
st.Minute = TwoAsciisToShort(s+15);
|
|
st.Second = TwoAsciisToShort(s+18);
|
|
|
|
}
|
|
} else { /* Try the other format: Wed Jun 9 01:29:59 1993 GMT */
|
|
|
|
s = (PCHAR) pszTime;
|
|
while (*s && *s==' ') s++;
|
|
|
|
if ((int)strlen(s) < 24) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (isdigit(*(s+8))) {
|
|
st.Day = AsciiToShort(s+8);
|
|
} else {
|
|
if ( ' ' != *(s+8) ) {
|
|
return FALSE;
|
|
}
|
|
st.Day = AsciiToShort(s+9);
|
|
}
|
|
st.Month = NumericToAsciiMonth(s+4);
|
|
st.Year = AsciiToShort(s+20);
|
|
st.Hour = AsciiToShort(s+11);
|
|
st.Minute = AsciiToShort(s+14);
|
|
st.Second = AsciiToShort(s+17);
|
|
}
|
|
|
|
//
|
|
// Adjust for dates with only two digits
|
|
//
|
|
|
|
if ( st.Year < 1000 ) {
|
|
if ( st.Year < 50 ) {
|
|
st.Year += 2000;
|
|
} else {
|
|
st.Year += 1900;
|
|
}
|
|
}
|
|
|
|
if ( !RtlTimeFieldsToTime( &st, pliTime )) {
|
|
return FALSE;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/***************************************************************************++
|
|
End of DateTime function ported from user mode W3SVC
|
|
--***************************************************************************/
|
|
|
|
|
|
//
|
|
// Some Unicode to Utf8 conversion utilities taken and modified frm
|
|
// base\win32\winnls\utf.c. Use this until they expose the same functionality
|
|
// in kernel.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Maps a Unicode character string to its UTF-8 string counterpart
|
|
|
|
Conversion continues until the source is finished or an error happens in
|
|
either case it returns the number of UTF-8 characters written.
|
|
|
|
If the supllied buffer is not big enough it returns 0.
|
|
|
|
--***************************************************************************/
|
|
|
|
ULONG
|
|
HttpUnicodeToUTF8(
|
|
IN PCWSTR lpSrcStr,
|
|
IN LONG cchSrc,
|
|
OUT LPSTR lpDestStr,
|
|
IN LONG cchDest
|
|
)
|
|
{
|
|
LPCWSTR lpWC = lpSrcStr;
|
|
LONG cchU8 = 0; // # of UTF8 chars generated
|
|
DWORD dwSurrogateChar;
|
|
WCHAR wchHighSurrogate = 0;
|
|
BOOLEAN bHandled;
|
|
|
|
while ((cchSrc--) && ((cchDest == 0) || (cchU8 < cchDest)))
|
|
{
|
|
bHandled = FALSE;
|
|
|
|
//
|
|
// Check if high surrogate is available
|
|
//
|
|
if ((*lpWC >= HIGH_SURROGATE_START) && (*lpWC <= HIGH_SURROGATE_END))
|
|
{
|
|
if (cchDest)
|
|
{
|
|
// Another high surrogate, then treat the 1st as normal
|
|
// Unicode character.
|
|
if (wchHighSurrogate)
|
|
{
|
|
if ((cchU8 + 2) < cchDest)
|
|
{
|
|
lpDestStr[cchU8++] = UTF8_1ST_OF_3 | HIGHER_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | MIDDLE_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | LOWER_6_BIT(wchHighSurrogate);
|
|
}
|
|
else
|
|
{
|
|
// not enough buffer
|
|
cchSrc++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cchU8 += 3;
|
|
}
|
|
wchHighSurrogate = *lpWC;
|
|
bHandled = TRUE;
|
|
}
|
|
|
|
if (!bHandled && wchHighSurrogate)
|
|
{
|
|
if ((*lpWC >= LOW_SURROGATE_START) && (*lpWC <= LOW_SURROGATE_END))
|
|
{
|
|
// wheee, valid surrogate pairs
|
|
|
|
if (cchDest)
|
|
{
|
|
if ((cchU8 + 3) < cchDest)
|
|
{
|
|
dwSurrogateChar = (((wchHighSurrogate-0xD800) << 10) + (*lpWC - 0xDC00) + 0x10000);
|
|
|
|
lpDestStr[cchU8++] = (UTF8_1ST_OF_4 | (unsigned char)(dwSurrogateChar >> 18)); // 3 bits from 1st byte
|
|
lpDestStr[cchU8++] = (UTF8_TRAIL | (unsigned char)((dwSurrogateChar >> 12) & 0x3f)); // 6 bits from 2nd byte
|
|
lpDestStr[cchU8++] = (UTF8_TRAIL | (unsigned char)((dwSurrogateChar >> 6) & 0x3f)); // 6 bits from 3rd byte
|
|
lpDestStr[cchU8++] = (UTF8_TRAIL | (unsigned char)(0x3f &dwSurrogateChar)); // 6 bits from 4th byte
|
|
}
|
|
else
|
|
{
|
|
// not enough buffer
|
|
cchSrc++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we already counted 3 previously (in high surrogate)
|
|
cchU8 += 1;
|
|
}
|
|
|
|
bHandled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Bad Surrogate pair : ERROR
|
|
// Just process wchHighSurrogate , and the code below will
|
|
// process the current code point
|
|
if (cchDest)
|
|
{
|
|
if ((cchU8 + 2) < cchDest)
|
|
{
|
|
lpDestStr[cchU8++] = UTF8_1ST_OF_3 | HIGHER_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | MIDDLE_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | LOWER_6_BIT(wchHighSurrogate);
|
|
}
|
|
else
|
|
{
|
|
// not enough buffer
|
|
cchSrc++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
wchHighSurrogate = 0;
|
|
}
|
|
|
|
if (!bHandled)
|
|
{
|
|
if (*lpWC <= ASCII)
|
|
{
|
|
//
|
|
// Found ASCII.
|
|
//
|
|
if (cchDest)
|
|
{
|
|
lpDestStr[cchU8] = (char)*lpWC;
|
|
}
|
|
cchU8++;
|
|
}
|
|
else if (*lpWC <= UTF8_2_MAX)
|
|
{
|
|
//
|
|
// Found 2 byte sequence if < 0x07ff (11 bits).
|
|
//
|
|
if (cchDest)
|
|
{
|
|
if ((cchU8 + 1) < cchDest)
|
|
{
|
|
//
|
|
// Use upper 5 bits in first byte.
|
|
// Use lower 6 bits in second byte.
|
|
//
|
|
lpDestStr[cchU8++] = UTF8_1ST_OF_2 | (*lpWC >> 6);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | LOWER_6_BIT(*lpWC);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Error - buffer too small.
|
|
//
|
|
cchSrc++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cchU8 += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Found 3 byte sequence.
|
|
//
|
|
if (cchDest)
|
|
{
|
|
if ((cchU8 + 2) < cchDest)
|
|
{
|
|
//
|
|
// Use upper 4 bits in first byte.
|
|
// Use middle 6 bits in second byte.
|
|
// Use lower 6 bits in third byte.
|
|
//
|
|
lpDestStr[cchU8++] = UTF8_1ST_OF_3 | HIGHER_6_BIT(*lpWC);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | MIDDLE_6_BIT(*lpWC);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | LOWER_6_BIT(*lpWC);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Error - buffer too small.
|
|
//
|
|
cchSrc++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cchU8 += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
lpWC++;
|
|
}
|
|
|
|
//
|
|
// If the last character was a high surrogate, then handle it as a normal
|
|
// unicode character.
|
|
//
|
|
if ((cchSrc < 0) && (wchHighSurrogate != 0))
|
|
{
|
|
if (cchDest)
|
|
{
|
|
if ((cchU8 + 2) < cchDest)
|
|
{
|
|
lpDestStr[cchU8++] = UTF8_1ST_OF_3 | HIGHER_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | MIDDLE_6_BIT(wchHighSurrogate);
|
|
lpDestStr[cchU8++] = UTF8_TRAIL | LOWER_6_BIT(wchHighSurrogate);
|
|
}
|
|
else
|
|
{
|
|
cchSrc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the destination buffer was large enough.
|
|
//
|
|
if (cchDest && (cchSrc >= 0))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Return the number of UTF-8 characters written.
|
|
//
|
|
return cchU8;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Search input list of ETags for one that matches our local ETag.
|
|
|
|
Arguments:
|
|
pLocalETag - The local ETag we're using.
|
|
pETagList - The ETag list we've received from the client.
|
|
bWeakCompare - Whether using Weak Comparison is ok
|
|
|
|
Returns:
|
|
|
|
TRUE if we found a matching ETag, FALSE otherwise.
|
|
|
|
Author:
|
|
Anil Ruia (AnilR) 3-Apr-2000
|
|
|
|
History:
|
|
Eric Stenson (EricSten) 6-Dec-2000 ported from user-mode
|
|
|
|
|
|
--*/
|
|
BOOLEAN FindInETagList(
|
|
IN PUCHAR pLocalETag,
|
|
IN PUCHAR pETagList,
|
|
IN BOOLEAN fWeakCompare
|
|
)
|
|
{
|
|
ULONG QuoteCount;
|
|
PUCHAR pFileETag;
|
|
BOOLEAN Matched;
|
|
|
|
// We'll loop through the ETag string, looking for ETag to
|
|
// compare, as long as we have an ETag to look at.
|
|
|
|
do
|
|
{
|
|
while (isspace(*pETagList))
|
|
{
|
|
pETagList++;
|
|
}
|
|
|
|
if (!*pETagList)
|
|
{
|
|
// Ran out of ETag.
|
|
return FALSE;
|
|
}
|
|
|
|
// If this ETag is *, it's a match.
|
|
if (*pETagList == '*')
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// See if this ETag is weak.
|
|
if (pETagList[0] == 'W' && pETagList[1] == '/')
|
|
{
|
|
// This is a weak validator. If we're not doing the weak
|
|
// comparison, fail.
|
|
|
|
if (!fWeakCompare)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Skip over the 'W/', and any intervening whitespace.
|
|
pETagList += 2;
|
|
|
|
while (isspace(*pETagList))
|
|
{
|
|
pETagList++;
|
|
}
|
|
|
|
if (!*pETagList)
|
|
{
|
|
// Ran out of ETag.
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (*pETagList != '"')
|
|
{
|
|
// This isn't a quoted string, so fail.
|
|
return FALSE;
|
|
}
|
|
|
|
// OK, right now we should be at the start of a quoted string that
|
|
// we can compare against our current ETag.
|
|
|
|
QuoteCount = 0;
|
|
|
|
Matched = TRUE;
|
|
pFileETag = pLocalETag;
|
|
|
|
// Do the actual compare. We do this by scanning the current ETag,
|
|
// which is a quoted string. We look for two quotation marks, the
|
|
// the delimiters if the quoted string. If after we find two quotes
|
|
// in the ETag everything has matched, then we've matched this ETag.
|
|
// Otherwise we'll try the next one.
|
|
|
|
do
|
|
{
|
|
CHAR Temp;
|
|
|
|
Temp = *pETagList;
|
|
|
|
if (Temp == '"')
|
|
{
|
|
QuoteCount++;
|
|
}
|
|
|
|
if (*pFileETag != Temp)
|
|
{
|
|
Matched = FALSE;
|
|
}
|
|
|
|
if (!Temp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pETagList++;
|
|
|
|
if (*pFileETag == '\0')
|
|
{
|
|
break;
|
|
}
|
|
|
|
pFileETag++;
|
|
|
|
|
|
}
|
|
while (QuoteCount != 2);
|
|
|
|
if (Matched)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Otherwise, at this point we need to look at the next ETag.
|
|
|
|
while (QuoteCount != 2)
|
|
{
|
|
if (*pETagList == '"')
|
|
{
|
|
QuoteCount++;
|
|
}
|
|
else
|
|
{
|
|
if (*pETagList == '\0')
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pETagList++;
|
|
}
|
|
|
|
while (isspace(*pETagList))
|
|
{
|
|
pETagList++;
|
|
}
|
|
|
|
if (*pETagList == ',')
|
|
{
|
|
pETagList++;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
while ( *pETagList );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Build a NULL terminated UNICODE string from the IP and Port address
|
|
|
|
Arguments:
|
|
IpAddressStringW - String buffer to place the UNICODE string
|
|
(caller allocated)
|
|
IpAddress - 32-bit version of the IP address
|
|
IpPortNum - 16-bit version of the TCP Port
|
|
|
|
Returns:
|
|
|
|
Count of bytes written into IpAddressStringW.
|
|
|
|
Author:
|
|
Eric Stenson (EricSten) 29-Jan-2001
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
HostAddressAndPortToStringW(
|
|
IN OUT PWCHAR IpAddressStringW,
|
|
IN ULONG IpAddress,
|
|
IN USHORT IpPortNum
|
|
)
|
|
{
|
|
PWCHAR pszW = IpAddressStringW;
|
|
|
|
pszW = UlStrPrintUlongW(pszW, (IpAddress >> 24) & 0xFF, 0, '.');
|
|
pszW = UlStrPrintUlongW(pszW, (IpAddress >> 16) & 0xFF, 0, '.');
|
|
pszW = UlStrPrintUlongW(pszW, (IpAddress >> 8) & 0xFF, 0, '.');
|
|
pszW = UlStrPrintUlongW(pszW, (IpAddress >> 0) & 0xFF, 0, ':');
|
|
pszW = UlStrPrintUlongW(pszW, IpPortNum, 0, '\0');
|
|
|
|
return DIFF(pszW - IpAddressStringW) * sizeof(WCHAR);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculates current bias (daylight time aware) and time zone ID.
|
|
|
|
Captured from base\client\datetime.c
|
|
|
|
Until this two functions are exposed in the kernel we have to
|
|
keep them here.
|
|
|
|
Arguments:
|
|
|
|
IN CONST TIME_ZONE_INFORMATION *ptzi - time zone for which to calculate bias
|
|
OUT KSYSTEM_TIME *pBias - current bias
|
|
|
|
Return Value:
|
|
|
|
TIME_ZONE_ID_UNKNOWN - daylight saving time is not used in the
|
|
current time zone.
|
|
|
|
TIME_ZONE_ID_STANDARD - The system is operating in the range covered
|
|
by StandardDate.
|
|
|
|
TIME_ZONE_ID_DAYLIGHT - The system is operating in the range covered
|
|
by DaylightDate.
|
|
|
|
TIME_ZONE_ID_INVALID - The operation failed.
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
UlCalcTimeZoneIdAndBias(
|
|
IN RTL_TIME_ZONE_INFORMATION *ptzi,
|
|
OUT PLONG pBias
|
|
)
|
|
{
|
|
LARGE_INTEGER TimeZoneBias;
|
|
LARGE_INTEGER NewTimeZoneBias;
|
|
LARGE_INTEGER LocalCustomBias;
|
|
LARGE_INTEGER UtcStandardTime;
|
|
LARGE_INTEGER UtcDaylightTime;
|
|
LARGE_INTEGER StandardTime;
|
|
LARGE_INTEGER DaylightTime;
|
|
LARGE_INTEGER CurrentUniversalTime;
|
|
ULONG CurrentTimeZoneId = UL_TIME_ZONE_ID_INVALID;
|
|
|
|
NewTimeZoneBias.QuadPart = Int32x32To64(ptzi->Bias*60, 10000000);
|
|
|
|
//
|
|
// Now see if we have stored cutover times
|
|
//
|
|
|
|
if (ptzi->StandardStart.Month && ptzi->DaylightStart.Month)
|
|
{
|
|
KeQuerySystemTime(&CurrentUniversalTime);
|
|
|
|
//
|
|
// We have timezone cutover information. Compute the
|
|
// cutover dates and compute what our current bias
|
|
// is
|
|
//
|
|
|
|
if((!UlCutoverTimeToSystemTime(
|
|
&ptzi->StandardStart,
|
|
&StandardTime,
|
|
&CurrentUniversalTime)
|
|
) ||
|
|
(!UlCutoverTimeToSystemTime(
|
|
&ptzi->DaylightStart,
|
|
&DaylightTime,
|
|
&CurrentUniversalTime)
|
|
)
|
|
)
|
|
{
|
|
return UL_TIME_ZONE_ID_INVALID;
|
|
}
|
|
|
|
//
|
|
// Convert standard time and daylight time to utc
|
|
//
|
|
|
|
LocalCustomBias.QuadPart = Int32x32To64(ptzi->StandardBias*60, 10000000);
|
|
TimeZoneBias.QuadPart = NewTimeZoneBias.QuadPart + LocalCustomBias.QuadPart;
|
|
UtcDaylightTime.QuadPart = DaylightTime.QuadPart + TimeZoneBias.QuadPart;
|
|
|
|
LocalCustomBias.QuadPart = Int32x32To64(ptzi->DaylightBias*60, 10000000);
|
|
TimeZoneBias.QuadPart = NewTimeZoneBias.QuadPart + LocalCustomBias.QuadPart;
|
|
UtcStandardTime.QuadPart = StandardTime.QuadPart + TimeZoneBias.QuadPart;
|
|
|
|
//
|
|
// If daylight < standard, then time >= daylight and
|
|
// less than standard is daylight
|
|
//
|
|
|
|
if (UtcDaylightTime.QuadPart < UtcStandardTime.QuadPart)
|
|
{
|
|
//
|
|
// If today is >= DaylightTime and < StandardTime, then
|
|
// We are in daylight savings time
|
|
//
|
|
|
|
if ((CurrentUniversalTime.QuadPart >= UtcDaylightTime.QuadPart) &&
|
|
(CurrentUniversalTime.QuadPart < UtcStandardTime.QuadPart))
|
|
{
|
|
CurrentTimeZoneId = UL_TIME_ZONE_ID_DAYLIGHT;
|
|
}
|
|
else
|
|
{
|
|
CurrentTimeZoneId = UL_TIME_ZONE_ID_STANDARD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If today is >= StandardTime and < DaylightTime, then
|
|
// We are in standard time
|
|
//
|
|
|
|
if ((CurrentUniversalTime.QuadPart >= UtcStandardTime.QuadPart) &&
|
|
(CurrentUniversalTime.QuadPart < UtcDaylightTime.QuadPart))
|
|
{
|
|
CurrentTimeZoneId = UL_TIME_ZONE_ID_STANDARD;
|
|
|
|
}
|
|
else
|
|
{
|
|
CurrentTimeZoneId = UL_TIME_ZONE_ID_DAYLIGHT;
|
|
}
|
|
}
|
|
|
|
// Bias in minutes
|
|
|
|
*pBias = ptzi->Bias + (CurrentTimeZoneId == UL_TIME_ZONE_ID_DAYLIGHT ?
|
|
ptzi->DaylightBias : ptzi->StandardBias
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
*pBias = ptzi->Bias;
|
|
CurrentTimeZoneId = UL_TIME_ZONE_ID_UNKNOWN;
|
|
}
|
|
|
|
return CurrentTimeZoneId;
|
|
}
|
|
|
|
BOOLEAN
|
|
UlCutoverTimeToSystemTime(
|
|
PTIME_FIELDS CutoverTime,
|
|
PLARGE_INTEGER SystemTime,
|
|
PLARGE_INTEGER CurrentSystemTime
|
|
)
|
|
{
|
|
TIME_FIELDS CurrentTimeFields;
|
|
|
|
//
|
|
// Get the current system time
|
|
//
|
|
|
|
RtlTimeToTimeFields(CurrentSystemTime,&CurrentTimeFields);
|
|
|
|
//
|
|
// check for absolute time field. If the year is specified,
|
|
// the the time is an abosulte time
|
|
//
|
|
|
|
if ( CutoverTime->Year )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
TIME_FIELDS WorkingTimeField;
|
|
TIME_FIELDS ScratchTimeField;
|
|
LARGE_INTEGER ScratchTime;
|
|
CSHORT BestWeekdayDate;
|
|
CSHORT WorkingWeekdayNumber;
|
|
CSHORT TargetWeekdayNumber;
|
|
CSHORT TargetYear;
|
|
CSHORT TargetMonth;
|
|
CSHORT TargetWeekday; // range [0..6] == [Sunday..Saturday]
|
|
BOOLEAN MonthMatches;
|
|
//
|
|
// The time is an day in the month style time
|
|
//
|
|
// the convention is the Day is 1-5 specifying 1st, 2nd... Last
|
|
// day within the month. The day is WeekDay.
|
|
//
|
|
|
|
//
|
|
// Compute the target month and year
|
|
//
|
|
|
|
TargetWeekdayNumber = CutoverTime->Day;
|
|
if ( TargetWeekdayNumber > 5 || TargetWeekdayNumber == 0 ) {
|
|
return FALSE;
|
|
}
|
|
TargetWeekday = CutoverTime->Weekday;
|
|
TargetMonth = CutoverTime->Month;
|
|
MonthMatches = FALSE;
|
|
|
|
TargetYear = CurrentTimeFields.Year;
|
|
|
|
try_next_year:
|
|
|
|
BestWeekdayDate = 0;
|
|
|
|
WorkingTimeField.Year = TargetYear;
|
|
WorkingTimeField.Month = TargetMonth;
|
|
WorkingTimeField.Day = 1;
|
|
WorkingTimeField.Hour = CutoverTime->Hour;
|
|
WorkingTimeField.Minute = CutoverTime->Minute;
|
|
WorkingTimeField.Second = CutoverTime->Second;
|
|
WorkingTimeField.Milliseconds = CutoverTime->Milliseconds;
|
|
WorkingTimeField.Weekday = 0;
|
|
|
|
//
|
|
// Convert to time and then back to time fields so we can determine
|
|
// the weekday of day 1 on the month
|
|
//
|
|
|
|
if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
|
|
return FALSE;
|
|
}
|
|
RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
|
|
|
|
//
|
|
// Compute bias to target weekday
|
|
//
|
|
if ( ScratchTimeField.Weekday > TargetWeekday ) {
|
|
WorkingTimeField.Day += (7-(ScratchTimeField.Weekday - TargetWeekday));
|
|
}
|
|
else if ( ScratchTimeField.Weekday < TargetWeekday ) {
|
|
WorkingTimeField.Day += (TargetWeekday - ScratchTimeField.Weekday);
|
|
}
|
|
|
|
//
|
|
// We are now at the first weekday that matches our target weekday
|
|
//
|
|
|
|
BestWeekdayDate = WorkingTimeField.Day;
|
|
WorkingWeekdayNumber = 1;
|
|
|
|
//
|
|
// Keep going one week at a time until we either pass the
|
|
// target weekday, or we match exactly
|
|
//
|
|
|
|
while ( WorkingWeekdayNumber < TargetWeekdayNumber ) {
|
|
WorkingTimeField.Day += 7;
|
|
if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
|
|
break;
|
|
}
|
|
RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
|
|
WorkingWeekdayNumber++;
|
|
BestWeekdayDate = ScratchTimeField.Day;
|
|
}
|
|
WorkingTimeField.Day = BestWeekdayDate;
|
|
|
|
//
|
|
// If the months match, and the date is less than the current
|
|
// date, then be have to go to next year.
|
|
//
|
|
|
|
if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
|
|
return FALSE;
|
|
}
|
|
if ( MonthMatches ) {
|
|
if ( WorkingTimeField.Day < CurrentTimeFields.Day ) {
|
|
MonthMatches = FALSE;
|
|
TargetYear++;
|
|
goto try_next_year;
|
|
}
|
|
if ( WorkingTimeField.Day == CurrentTimeFields.Day ) {
|
|
|
|
if (ScratchTime.QuadPart < CurrentSystemTime->QuadPart) {
|
|
MonthMatches = FALSE;
|
|
TargetYear++;
|
|
goto try_next_year;
|
|
}
|
|
}
|
|
}
|
|
*SystemTime = ScratchTime;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|