Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

782 lines
23 KiB

/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
Internal.c
Abstract:
User-mode interface to HTTP.SYS: Internal helper functions.
Author:
Keith Moore (keithmo) 15-Dec-1998
Revision History:
Rajesh Sundaram (rajeshsu) 10-Oct-2000 : Added HTTP client code
--*/
#include "precomp.h"
//
// Private macros.
//
#ifndef MAX
#define MAX(_a, _b) ((_a) > (_b)? (_a): (_b))
#endif
#define MAX_HTTP_DEVICE_NAME \
(MAX(MAX(sizeof(HTTP_SERVER_DEVICE_NAME)/sizeof(WCHAR), sizeof(HTTP_CONTROL_DEVICE_NAME)/sizeof(WCHAR)), \
MAX(sizeof(HTTP_APP_POOL_DEVICE_NAME)/sizeof(WCHAR), sizeof(HTTP_FILTER_DEVICE_NAME)/sizeof(WCHAR))))
//
// Private prototypes.
//
NTSTATUS
HttpApiAcquireCachedEvent(
OUT HANDLE * phEvent
);
//
// 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:
ULONG - Completion status.
--***************************************************************************/
ULONG
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;
LARGE_INTEGER timeout;
HANDLE hEvent;
//
// Try to snag an event object.
//
status = HttpApiAcquireCachedEvent( &hEvent );
if (NT_SUCCESS(status))
{
ASSERT( hEvent != NULL );
//
// Make the call.
//
status = NtDeviceIoControlFile(
FileHandle, // FileHandle
hEvent, // 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( hEvent,
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;
}
//
// Note: We do not have to release the cached event. The event is associated
// with this thread using TLS. There is nothing to cleanup now.
// The event will be cleaned up when the thread goes away.
//
}
return HttpApiNtStatusToWin32Status( 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:
ULONG - Completion status.
--***************************************************************************/
ULONG
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;
ULONG result;
//
// Overlapped I/O gets a little more interesting. We'll strive to be
// compatible with NT's KERNEL32 implementation. See DeviceIoControl()
// in \sdnt\base\win32\client\filehops.c for the gory details.
//
ASSERT(pOverlapped);
SET_STATUS_OVERLAPPED_TO_IO_STATUS(pOverlapped, 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
);
//
// Set LastError using the original status returned so RtlGetLastNtStatus
// RtlGetLastWin32Error will get the correct status.
//
result = HttpApiNtStatusToWin32Status( status );
//
// Convert all informational and warning status to ERROR_IO_PENDING so
// user can always expect the completion routine gets called.
//
if (NT_INFORMATION(status) || NT_WARNING(status))
{
result = ERROR_IO_PENDING;
}
//
// 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 (result == NO_ERROR && pBytesTransferred)
{
//
// We need a __try __except to mimic DeviceIoControl().
//
__try
{
*pBytesTransferred =
(ULONG)OVERLAPPED_TO_IO_STATUS(pOverlapped)->Information;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
*pBytesTransferred = 0;
}
}
return result;
} // HttpApiOverlappedDeviceControl
/***************************************************************************++
Routine Description:
This routine checks to see if a service has been started. If the service
is in START_PENDING, it waits for it to start completely.
Return Value:
BOOLEAN - TRUE if successful, FALSE otherwise.
--***************************************************************************/
BOOLEAN
QueryAndWaitForServiceStart(
IN SC_HANDLE svcHandle
)
{
SERVICE_STATUS Status;
for(;;)
{
if(!QueryServiceStatus(svcHandle, &Status))
{
return FALSE;
}
switch(Status.dwCurrentState)
{
case SERVICE_RUNNING:
return TRUE;
break;
case SERVICE_START_PENDING:
// Yield to another thread on current processor. If
// no threads are ready to run on the current
// processor, we'll have to sleep to avoid consuming
// too much CPU in what would look almost like a busy
// wait
if(!SwitchToThread())
{
Sleep(50);
}
break;
default:
return FALSE;
break;
}
}
}
/***************************************************************************++
Routine Description:
This routine attempts to start HTTP.SYS.
Return Value:
BOOLEAN - TRUE if successful, FALSE otherwise.
--***************************************************************************/
BOOLEAN
HttpApiTryToStartDriver(
IN PWSTR ServiceName
)
{
BOOLEAN result;
SC_HANDLE scHandle;
SC_HANDLE svcHandle;
result = FALSE; // until proven otherwise...
//
// NOTE:
//
// If an auto-start service calls into HTTP from their Service Entry
// point AND if they have not laid out a dependency on the HTTP service,
// we will deadlock. This is because ServiceStart() will not return until
// all auto-start services are started.
//
// We "could" check for this by checking the status on the named event
// called SC_AUTOSTART_EVENTNAME. If this event is signalled, we have
// completed autostart. However, this event cannot be opened by non-admin
// processes. Therefore, we'll just not even bother checking for this.
//
//
// Open the service controller.
//
scHandle = OpenSCManagerW(
NULL, // lpMachineName
NULL, // lpDatabaseName
SC_MANAGER_CONNECT // dwDesiredAccess
);
if (scHandle != NULL)
{
//
// Try to open the HTTP service.
//
svcHandle = OpenServiceW(
scHandle, // hSCManager
ServiceName, // lpServiceName
SERVICE_START | SERVICE_QUERY_STATUS // dwDesiredAccess
);
if (svcHandle != NULL)
{
//
// First, see if the service is already started. We can't call
// ServiceStart() directly, because of the SCM deadlock mentioned
// above.
//
if(QueryAndWaitForServiceStart(svcHandle))
{
// If the service is already running, we don't have to do
// anything else.
result = TRUE;
}
else
{
//
// Service is not running. So, we try to start it, and wait
// the start to complete.
//
if (StartService( svcHandle, 0, NULL))
{
if(QueryAndWaitForServiceStart(svcHandle))
{
result = TRUE;
}
}
else
{
if(ERROR_SERVICE_ALREADY_RUNNING == GetLastError())
{
// some other thread has already started this service,
// let's treat this as success.
result = TRUE;
}
}
}
CloseServiceHandle( svcHandle );
}
CloseServiceHandle( scHandle );
}
return result;
} // HttpTryToStartDriver
//
// Private functions.
//
/***************************************************************************++
Routine Description:
Helper routine for opening a HTTP.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 PWCHAR Uri,
IN USHORT UriLength,
IN PWCHAR Proxy,
IN USHORT ProxyLength,
IN PTRANSPORT_ADDRESS pTransportAddress,
IN USHORT TransportAddressLength,
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;
WCHAR deviceNameBuffer[MAX_HTTP_DEVICE_NAME + MAX_PATH + 2];
PHTTP_OPEN_PACKET pOpenVersion;
ULONG EaBufferLength;
//
// Validate the parameters.
//
if ((pHandle == NULL) ||
(Options & ~HTTP_OPTION_VALID))
{
return STATUS_INVALID_PARAMETER;
}
if ((HandleType != HttpApiControlChannelHandleType) &&
(HandleType != HttpApiFilterChannelHandleType) &&
(HandleType != HttpApiAppPoolHandleType) &&
(HandleType != HttpApiServerHandleType))
{
return STATUS_INVALID_PARAMETER;
}
//
// Build the open packet.
//
EaBufferLength =
sizeof(HTTP_OPEN_PACKET) +
HTTP_OPEN_PACKET_NAME_LENGTH +
sizeof(FILE_FULL_EA_INFORMATION);
pEaBuffer = RtlAllocateHeap(RtlProcessHeap(),
0,
EaBufferLength
);
if(!pEaBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Build the first EA which will contain the version info.
//
pEaBuffer->Flags = 0;
pEaBuffer->EaNameLength = HTTP_OPEN_PACKET_NAME_LENGTH;
pEaBuffer->EaValueLength = sizeof(*pOpenVersion);
pEaBuffer->NextEntryOffset = 0;
RtlCopyMemory(
pEaBuffer->EaName,
HTTP_OPEN_PACKET_NAME,
HTTP_OPEN_PACKET_NAME_LENGTH + 1);
pOpenVersion = (PHTTP_OPEN_PACKET)
(pEaBuffer->EaName +pEaBuffer->EaNameLength + 1);
pOpenVersion->MajorVersion = HTTP_INTERFACE_VERSION_MAJOR;
pOpenVersion->MinorVersion = HTTP_INTERFACE_VERSION_MINOR;
pOpenVersion->ServerNameLength = UriLength;
pOpenVersion->pServerName = Uri;
pOpenVersion->ProxyNameLength = ProxyLength;
pOpenVersion->pProxyName = Proxy;
pOpenVersion->pTransportAddress = pTransportAddress;
pOpenVersion->TransportAddressLength = TransportAddressLength;
//
// 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 if(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;
}
}
else
{
ASSERT(HandleType == HttpApiServerHandleType);
wcscpy( deviceNameBuffer, HTTP_SERVER_DEVICE_NAME );
}
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;
//
// Build the object attributes.
//
status = RtlInitUnicodeStringEx( &deviceName, deviceNameBuffer );
if ( !NT_SUCCESS(status) )
{
goto complete;
}
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
EaBufferLength // EaLength
);
complete:
if (!NT_SUCCESS(status))
{
*pHandle = NULL;
}
RtlFreeHeap(RtlProcessHeap(),
0,
pEaBuffer);
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.
We will cache the event and associate it with the thread using TLS.
Therefore acquiring the event simply means checking whether we already
have an associated event with TLS. If not, we'll create an event and
associate it.
Arguments:
phEvent - Receives handle to event
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
HttpApiAcquireCachedEvent(
HANDLE * phEvent
)
{
NTSTATUS status;
HANDLE hEvent = NULL;
//
// See if an event was already associated with TLS
//
hEvent = TlsGetValue( g_TlsIndex );
if (hEvent == NULL)
{
//
// No event associated. Create one now
//
status = NtCreateEvent(
&hEvent, // EventHandle
EVENT_ALL_ACCESS, // DesiredAccess
NULL, // ObjectAttributes
SynchronizationEvent, // EventType
FALSE // InitialState
);
if (!NT_SUCCESS( status ))
{
return status;
}
//
// Associate so subsequent requests on this thread don't have to
// create the event
//
if (!TlsSetValue( g_TlsIndex, hEvent ))
{
//
// If we couldn't set the TLS, then something really bad
// happened. Bail with error
//
NtClose( hEvent );
return STATUS_INSUFFICIENT_RESOURCES;
}
}
*phEvent = hEvent;
return STATUS_SUCCESS;
}