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