/*++ Copyright (c) 1998-2001 Microsoft Corporation Module Name: internal.c Abstract: User-mode interface to HTTP.SYS. Author: Keith Moore (keithmo) 15-Dec-1998 Revision History: --*/ #include "precomp.h" // // Private definitions // typedef struct _CACHED_EVENT { SINGLE_LIST_ENTRY ListEntry; HANDLE EventHandle; } CACHED_EVENT, *PCACHED_EVENT; // // Private globals // SLIST_HEADER CachedEventList; // // Private macros. // #define EA_BUFFER_LENGTH \ ( sizeof(FILE_FULL_EA_INFORMATION) + \ HTTP_OPEN_PACKET_NAME_LENGTH + \ sizeof(HTTP_OPEN_PACKET) ) // // Private prototypes. // NTSTATUS HttpApiAcquireCachedEvent( OUT CACHED_EVENT ** ppCachedEvent ); VOID HttpApiReleaseCachedEvent( IN CACHED_EVENT * pCachedEvent ); // // Public functions. // /***************************************************************************++ Routine Description: Synchronous wrapper around NtDeviceIoControlFile(). Arguments: FileHandle - Supplies a handle to the file on which the service is being performed. IoControlCode - Subfunction code to determine exactly what operation is being performed. pInputBuffer - Optionally supplies an input buffer to be passed to the device driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. InputBufferLength - Length of the pInputBuffer in bytes. pOutputBuffer - Optionally supplies an output buffer to receive information from the device driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. OutputBufferLength - Length of the pOutputBuffer in bytes. pBytesTransferred - Optionally receives the number of bytes transferred. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS HttpApiSynchronousDeviceControl( IN HANDLE FileHandle, IN ULONG IoControlCode, IN PVOID pInputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID pOutputBuffer OPTIONAL, IN ULONG OutputBufferLength, OUT PULONG pBytesTransferred OPTIONAL ) { NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; CACHED_EVENT * pCachedEvent; LARGE_INTEGER timeout; // // Try to snag an event object. // status = HttpApiAcquireCachedEvent( &pCachedEvent ); if (NT_SUCCESS(status)) { ASSERT( pCachedEvent != NULL ); // // Make the call. // status = NtDeviceIoControlFile( FileHandle, // FileHandle pCachedEvent->EventHandle, // Event NULL, // ApcRoutine NULL, // ApcContext &ioStatusBlock, // IoStatusBlock IoControlCode, // IoControlCode pInputBuffer, // InputBuffer InputBufferLength, // InputBufferLength pOutputBuffer, // OutputBuffer OutputBufferLength // OutputBufferLength ); if (status == STATUS_PENDING) { // // Wait for it to complete. // timeout.LowPart = 0xFFFFFFFF; timeout.HighPart = 0x7FFFFFFF; status = NtWaitForSingleObject( pCachedEvent->EventHandle, FALSE, &timeout ); ASSERT( status == STATUS_SUCCESS ); status = ioStatusBlock.Status; } // // If the call didn't fail and the caller wants the number // of bytes transferred, grab the value from the I/O status // block & return it. // if (!NT_ERROR(status) && pBytesTransferred != NULL) { *pBytesTransferred = (ULONG)ioStatusBlock.Information; } // // Release the cached event object we acquired above. // HttpApiReleaseCachedEvent( pCachedEvent ); } return status; } // HttpApiSynchronousDeviceControl /***************************************************************************++ Routine Description: Overlapped wrapper around NtDeviceIoControlFile(). Arguments: FileHandle - Supplies a handle to the file on which the service is being performed. pOverlapped - Supplies an OVERLAPPED structure. IoControlCode - Subfunction code to determine exactly what operation is being performed. pInputBuffer - Optionally supplies an input buffer to be passed to the device driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. InputBufferLength - Length of the pInputBuffer in bytes. pOutputBuffer - Optionally supplies an output buffer to receive information from the device driver. Whether or not the buffer is actually optional is dependent on the IoControlCode. OutputBufferLength - Length of the pOutputBuffer in bytes. pBytesTransferred - Optionally receives the number of bytes transferred. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS HttpApiOverlappedDeviceControl( IN HANDLE FileHandle, IN OUT LPOVERLAPPED pOverlapped, IN ULONG IoControlCode, IN PVOID pInputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID pOutputBuffer OPTIONAL, IN ULONG OutputBufferLength, OUT PULONG pBytesTransferred OPTIONAL ) { NTSTATUS status; // // Overlapped I/O gets a little more interesting. We'll strive to be // compatible with NT's KERNEL32 implementation. See DeviceIoControl() // in \\rastaman\ntwin\src\base\client\filehops.c for the gory details. // OVERLAPPED_TO_IO_STATUS(pOverlapped)->Status = STATUS_PENDING; status = NtDeviceIoControlFile( FileHandle, // FileHandle pOverlapped->hEvent, // Event NULL, // ApcRoutine (ULONG_PTR)pOverlapped->hEvent & 1 // ApcContext ? NULL : pOverlapped, OVERLAPPED_TO_IO_STATUS(pOverlapped), // IoStatusBlock IoControlCode, // IoControlCode pInputBuffer, // InputBuffer InputBufferLength, // InputBufferLength pOutputBuffer, // OutputBuffer OutputBufferLength // OutputBufferLength ); // // If the call didn't fail or pend and the caller wants the number of // bytes transferred, grab the value from the I/O status block & // return it. // if (status == STATUS_SUCCESS) { if (pBytesTransferred) { *pBytesTransferred = (ULONG)OVERLAPPED_TO_IO_STATUS(pOverlapped)->Information; } status = STATUS_PENDING; } return status; } // HttpApiOverlappedDeviceControl /***************************************************************************++ Routine Description: Initializes the event object cache. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG HttpApiInitializeEventCache( VOID ) { RtlInitializeSListHead( &CachedEventList ); return NO_ERROR; } // HttpApiInitializeEventCache /***************************************************************************++ Routine Description: Terminates the event object cache. Return Value: ULONG - Completion status. --***************************************************************************/ ULONG HttpApiTerminateEventCache( VOID ) { CACHED_EVENT * pCachedEvent; PSINGLE_LIST_ENTRY Entry; for ( ;; ) { Entry = RtlInterlockedPopEntrySList( &CachedEventList ); if (Entry == NULL) { break; } pCachedEvent = CONTAINING_RECORD( Entry, CACHED_EVENT, ListEntry ); NtClose( pCachedEvent->EventHandle ); FREE_MEM( pCachedEvent ); } return NO_ERROR; } // HttpApiTerminateEventCache /***************************************************************************++ Routine Description: This routine attempts to start UL.SYS. Return Value: BOOLEAN - TRUE if successful, FALSE otherwise. --***************************************************************************/ BOOLEAN HttpApiTryToStartDriver( VOID ) { BOOLEAN result; SC_HANDLE scHandle; SC_HANDLE svcHandle; result = FALSE; // until proven otherwise... // // Open the service controller. // scHandle = OpenSCManagerW( NULL, // lpMachineName NULL, // lpDatabaseName SC_MANAGER_ALL_ACCESS // dwDesiredAccess ); if (scHandle != NULL) { // // Try to open the UL service. // svcHandle = OpenServiceW( scHandle, // hSCManager HTTP_SERVICE_NAME, // lpServiceName SERVICE_ALL_ACCESS // dwDesiredAccess ); if (svcHandle != NULL) { // // Try to start it. // if (StartService( svcHandle, 0, NULL)) { result = TRUE; } CloseServiceHandle( svcHandle ); } CloseServiceHandle( scHandle ); } return result; } // HttpApiTryToStartDriver // // Private functions. // /***************************************************************************++ Routine Description: Helper routine for opening a UL.SYS handle. Arguments: pHandle - Receives a handle if successful. DesiredAccess - Supplies the types of access requested to the file. HandleType - one of Filter, ControlChannel, or AppPool pObjectName - Optionally supplies the name of the application pool to create/open. Options - Supplies zero or more HTTP_OPTION_* flags. CreateDisposition - Supplies the creation disposition for the new object. pSecurityAttributes - Optionally supplies security attributes for the newly created application pool. Ignored if opening a control channel. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS HttpApiOpenDriverHelper( OUT PHANDLE pHandle, IN ACCESS_MASK DesiredAccess, IN HTTPAPI_HANDLE_TYPE HandleType, IN PCWSTR pObjectName OPTIONAL, IN ULONG Options, IN ULONG CreateDisposition, IN PSECURITY_ATTRIBUTES pSecurityAttributes OPTIONAL ) { NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING deviceName; IO_STATUS_BLOCK ioStatusBlock; ULONG shareAccess; ULONG createOptions; PFILE_FULL_EA_INFORMATION pEaBuffer; PHTTP_OPEN_PACKET pOpenPacket; WCHAR deviceNameBuffer[MAX_PATH]; UCHAR rawEaBuffer[EA_BUFFER_LENGTH]; // // Validate the parameters. // if ((pHandle == NULL) || (Options & ~HTTP_OPTION_VALID)) { return STATUS_INVALID_PARAMETER; } if ((HandleType != HttpApiControlChannelHandleType) && (HandleType != HttpApiFilterChannelHandleType) && (HandleType != HttpApiAppPoolHandleType)) { return STATUS_INVALID_PARAMETER; } // // Build the open packet. // pEaBuffer = (PFILE_FULL_EA_INFORMATION)rawEaBuffer; pEaBuffer->NextEntryOffset = 0; pEaBuffer->Flags = 0; pEaBuffer->EaNameLength = HTTP_OPEN_PACKET_NAME_LENGTH; pEaBuffer->EaValueLength = sizeof(*pOpenPacket); RtlCopyMemory( pEaBuffer->EaName, HTTP_OPEN_PACKET_NAME, HTTP_OPEN_PACKET_NAME_LENGTH + 1 ); pOpenPacket = (PHTTP_OPEN_PACKET)( pEaBuffer->EaName + pEaBuffer->EaNameLength + 1 ); pOpenPacket->MajorVersion = HTTP_INTERFACE_VERSION_MAJOR; pOpenPacket->MinorVersion = HTTP_INTERFACE_VERSION_MINOR; // // Build the device name. // if (HandleType == HttpApiControlChannelHandleType) { // // It's a control channel, so just use the appropriate device name. // wcscpy( deviceNameBuffer, HTTP_CONTROL_DEVICE_NAME ); } else { if (HandleType == HttpApiFilterChannelHandleType) { // // It's a fitler channel, so start with the appropriate // device name. // wcscpy( deviceNameBuffer, HTTP_FILTER_DEVICE_NAME ); } else { ASSERT(HandleType == HttpApiAppPoolHandleType); // // It's an app pool, so start with the appropriate device name. // wcscpy( deviceNameBuffer, HTTP_APP_POOL_DEVICE_NAME ); // // Set WRITE_OWNER in DesiredAccess if AppPool is a controller. // if ((Options & HTTP_OPTION_CONTROLLER)) { DesiredAccess |= WRITE_OWNER; } } if (pObjectName != NULL ) { // // It's a named object, so append a slash and the name, // but first check to ensure we don't overrun our buffer. // if ((wcslen(deviceNameBuffer) + wcslen(pObjectName) + 2) > DIMENSION(deviceNameBuffer)) { status = STATUS_INVALID_PARAMETER; goto complete; } wcscat( deviceNameBuffer, L"\\" ); wcscat( deviceNameBuffer, pObjectName ); } } // // Determine the share access and create options based on the // Flags parameter. // shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; createOptions = 0; if ((Options & HTTP_OPTION_OVERLAPPED) == 0) { createOptions |= FILE_SYNCHRONOUS_IO_NONALERT; } // // Build the object attributes. // RtlInitUnicodeString( &deviceName, deviceNameBuffer ); InitializeObjectAttributes( &objectAttributes, // ObjectAttributes &deviceName, // ObjectName OBJ_CASE_INSENSITIVE, // Attributes NULL, // RootDirectory NULL, // SecurityDescriptor ); if (pSecurityAttributes != NULL) { objectAttributes.SecurityDescriptor = pSecurityAttributes->lpSecurityDescriptor; if (pSecurityAttributes->bInheritHandle) { objectAttributes.Attributes |= OBJ_INHERIT; } } // // Open the UL device. // status = NtCreateFile( pHandle, // FileHandle DesiredAccess, // DesiredAccess &objectAttributes, // ObjectAttributes &ioStatusBlock, // IoStatusBlock NULL, // AllocationSize 0, // FileAttributes shareAccess, // ShareAccess CreateDisposition, // CreateDisposition createOptions, // CreateOptions pEaBuffer, // EaBuffer EA_BUFFER_LENGTH // EaLength ); complete: if (!NT_SUCCESS(status)) { *pHandle = NULL; } return status; } // HttpApiOpenDriverHelper /***************************************************************************++ Routine Description: Acquires a short-term event from the global event cache. This event object may only be used for pseudo-synchronous I/O. Arguments: ppCachedEvent - Receives pointer to cached event structure Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS HttpApiAcquireCachedEvent( OUT CACHED_EVENT ** ppCachedEvent ) { PSINGLE_LIST_ENTRY Entry; NTSTATUS status; CACHED_EVENT * pCachedEvent; Entry = RtlInterlockedPopEntrySList( &CachedEventList ); if (Entry != NULL) { pCachedEvent = CONTAINING_RECORD( Entry, CACHED_EVENT, ListEntry ); } else { pCachedEvent = ALLOC_MEM( sizeof( CACHED_EVENT ) ); if (pCachedEvent == NULL) { return STATUS_NO_MEMORY; } status = NtCreateEvent( &(pCachedEvent->EventHandle), // EventHandle EVENT_ALL_ACCESS, // DesiredAccess NULL, // ObjectAttributes SynchronizationEvent, // EventType FALSE // InitialState ); if (!NT_SUCCESS( status )) { FREE_MEM( pCachedEvent ); return status; } } *ppCachedEvent = pCachedEvent; return STATUS_SUCCESS; } /***************************************************************************++ Routine Description: Releases a cached event acquired via HttpApiAcquireCachedEvent(). Arguments: Event - Supplies the cached event to release --***************************************************************************/ VOID HttpApiReleaseCachedEvent( IN CACHED_EVENT * pCachedEvent ) { RtlInterlockedPushEntrySList( &CachedEventList, &pCachedEvent->ListEntry ); } // HttpApiReleaseCachedEvent