windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/ul/api/internal.c

710 lines
18 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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