|
|
/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
ioctl.c
Abstract:
This module implements various IOCTL handlers.
Author:
Keith Moore (keithmo) 10-Jun-1998
Revision History:
Paul McDaniel (paulmcd) 15-Mar-1999 Modified SendResponse George V. Reilly (GeorgeRe) May 2001 Hardened the IOCTLs
--*/
// Paranoia is the name of the game here. We don't trust anything we get from
// user mode. All data has to be probed inside of a try/except handler.
// Furthermore, we assume that malicious or incompetent users will
// asynchronously change the data at any time, so we attempt to capture as
// much as possible of it in stack variables. If we need to walk through a
// list more than once, we cannot assume that it's the same data the second
// time around. Failure to observe these rules could result in a bugcheck or
// in the usermode code gaining access to kernel data structures.
#include "precomp.h"
#include "ioctlp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, UlQueryControlChannelIoctl )
#pragma alloc_text( PAGE, UlSetControlChannelIoctl )
#pragma alloc_text( PAGE, UlCreateConfigGroupIoctl )
#pragma alloc_text( PAGE, UlDeleteConfigGroupIoctl )
#pragma alloc_text( PAGE, UlQueryConfigGroupIoctl )
#pragma alloc_text( PAGE, UlSetConfigGroupIoctl )
#pragma alloc_text( PAGE, UlAddUrlToConfigGroupIoctl )
#pragma alloc_text( PAGE, UlRemoveUrlFromConfigGroupIoctl )
#pragma alloc_text( PAGE, UlRemoveAllUrlsFromConfigGroupIoctl )
#pragma alloc_text( PAGE, UlQueryAppPoolInformationIoctl )
#pragma alloc_text( PAGE, UlSetAppPoolInformationIoctl )
#pragma alloc_text( PAGE, UlReceiveHttpRequestIoctl )
#pragma alloc_text( PAGE, UlReceiveEntityBodyIoctl )
#pragma alloc_text( PAGE, UlSendHttpResponseIoctl )
#pragma alloc_text( PAGE, UlSendEntityBodyIoctl )
#pragma alloc_text( PAGE, UlFlushResponseCacheIoctl )
#pragma alloc_text( PAGE, UlWaitForDemandStartIoctl )
#pragma alloc_text( PAGE, UlWaitForDisconnectIoctl )
#pragma alloc_text( PAGE, UlFilterAcceptIoctl )
#pragma alloc_text( PAGE, UlFilterCloseIoctl )
#pragma alloc_text( PAGE, UlFilterRawReadIoctl )
#pragma alloc_text( PAGE, UlFilterRawWriteIoctl )
#pragma alloc_text( PAGE, UlFilterAppReadIoctl )
#pragma alloc_text( PAGE, UlFilterAppWriteIoctl )
#pragma alloc_text( PAGE, UlReceiveClientCertIoctl )
#pragma alloc_text( PAGE, UlGetCountersIoctl )
#pragma alloc_text( PAGE, UlAddFragmentToCacheIoctl )
#pragma alloc_text( PAGE, UlReadFragmentFromCacheIoctl )
#pragma alloc_text( PAGE, UcSetServerContextInformationIoctl )
#pragma alloc_text( PAGE, UcQueryServerContextInformationIoctl )
#pragma alloc_text( PAGE, UcReceiveResponseIoctl )
#pragma alloc_text( PAGEUC, UcSendEntityBodyIoctl )
#pragma alloc_text( PAGEUC, UcSendRequestIoctl )
#pragma alloc_text( PAGEUC, UcCancelRequestIoctl )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlShutdownAppPoolIoctl NOT PAGEABLE -- UlpRestartSendHttpResponse NOT PAGEABLE -- UlShutdownFilterIoctl
#endif
//
// Public functions.
//
/***************************************************************************++
Routine Description:
This routine queries information associated with a control channel.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlQueryControlChannelIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_CONTROL_CHANNEL_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel; PVOID pMdlBuffer = NULL; ULONG Length = 0; ULONG OutputBufferLength;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, QUERY_CONTROL_CHANNEL);
PAGED_CODE();
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONTROL_CHANNEL_INFO, pInfo);
//
// Validate input buffer
// A NULL MdlAddress inidicates a request for buffer length
//
if ( NULL != pIrp->MdlAddress ) { GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer); }
// Also make sure that user buffer was aligned properly
switch (pInfo->InformationClass) { case HttpControlChannelStateInformation: HANDLE_BUFFER_LENGTH_REQUEST(pIrp, pIrpSp, HTTP_ENABLED_STATE);
VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, HTTP_ENABLED_STATE); break;
case HttpControlChannelBandwidthInformation: HANDLE_BUFFER_LENGTH_REQUEST(pIrp, pIrpSp, HTTP_BANDWIDTH_LIMIT);
VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, HTTP_BANDWIDTH_LIMIT); break;
case HttpControlChannelConnectionInformation: HANDLE_BUFFER_LENGTH_REQUEST(pIrp, pIrpSp, HTTP_CONNECTION_LIMIT);
VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, HTTP_CONNECTION_LIMIT); break;
default: Status = STATUS_INVALID_PARAMETER; goto end; break; }
OutputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = UlGetControlChannelInformation( pIrp->RequestorMode, pControlChannel, pInfo->InformationClass, pMdlBuffer, OutputBufferLength, &Length );
if (NT_SUCCESS(Status)) { pIrp->IoStatus.Information = (ULONG_PTR)Length; }
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlQueryControlChannelIoctl
/***************************************************************************++
Routine Description:
This routine sets information associated with a control channel.
Note: This is a METHOD_IN_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSetControlChannelIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_CONTROL_CHANNEL_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel; HTTP_CONTROL_CHANNEL_INFORMATION_CLASS Class; PVOID pMdlBuffer = NULL; ULONG OutputBufferLength; //
// sanity check.
//
ASSERT_IOCTL_METHOD(IN_DIRECT, SET_CONTROL_CHANNEL);
PAGED_CODE();
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONTROL_CHANNEL_INFO, pInfo);
VALIDATE_INFORMATION_CLASS( pInfo, Class, HTTP_CONTROL_CHANNEL_INFORMATION_CLASS, HttpControlChannelMaximumInformation);
//
// Validate input buffer
//
GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer);
switch ( Class ) { case HttpControlChannelStateInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_ENABLED_STATE); break; case HttpControlChannelBandwidthInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_BANDWIDTH_LIMIT); break;
case HttpControlChannelFilterInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONTROL_CHANNEL_FILTER); break; case HttpControlChannelTimeoutInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT); break; case HttpControlChannelUTF8Logging: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONTROL_CHANNEL_UTF8_LOGGING); break; case HttpControlChannelBinaryLogging: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONTROL_CHANNEL_BINARY_LOGGING); break;
case HttpControlChannelDemandStartThreshold: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONTROL_CHANNEL_DEMAND_START_THRESHOLD); break;
default: Status = STATUS_INVALID_PARAMETER; goto end; break; }
//
// call the function
//
OutputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = UlSetControlChannelInformation( pControlChannel, pInfo->InformationClass, pMdlBuffer, OutputBufferLength, pIrp->RequestorMode ); end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlSetControlChannelIoctl
/***************************************************************************++
Routine Description:
This routine creates a new configuration group.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlCreateConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_CONFIG_GROUP_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel; HTTP_CONFIG_GROUP_ID LocalConfigGroupId;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, CREATE_CONFIG_GROUP);
PAGED_CODE();
HTTP_SET_NULL_ID(&LocalConfigGroupId);
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_OUTPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_INFO, pInfo);
// it's pure output, wipe it to be sure
RtlZeroMemory(pInfo, sizeof(HTTP_CONFIG_GROUP_INFO));
// Call the internal worker func
//
Status = UlCreateConfigGroup( pControlChannel, &LocalConfigGroupId );
if (NT_SUCCESS(Status)) pInfo->ConfigGroupId = LocalConfigGroupId;
end: if (Status != STATUS_PENDING) { // how much output should we return?
pIrp->IoStatus.Information = sizeof(HTTP_CONFIG_GROUP_INFO); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlCreateConfigGroupIoctl
/***************************************************************************++
Routine Description:
This routine deletes an existing configuration group.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlDeleteConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_CONFIG_GROUP_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, DELETE_CONFIG_GROUP);
PAGED_CODE();
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_INFO, pInfo);
Status = UlDeleteConfigGroup(pInfo->ConfigGroupId);
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlDeleteConfigGroupIoctl
/***************************************************************************++
Routine Description:
This routine queries information associated with a configuration group.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlQueryConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_CONFIG_GROUP_INFO pInfo; PVOID pMdlBuffer = NULL; ULONG Length = 0L; ULONG OutputLength; PUL_CONTROL_CHANNEL pControlChannel; HTTP_CONFIG_GROUP_INFORMATION_CLASS Class;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, QUERY_CONFIG_GROUP);
PAGED_CODE();
//
// Going to access the url string from user mode memory
//
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_INFO, pInfo);
VALIDATE_INFORMATION_CLASS( pInfo, Class, HTTP_CONFIG_GROUP_INFORMATION_CLASS, HttpConfigGroupMaximumInformation);
//
// Validate input buffer
// A NULL MdlAddress inidicates a request for buffer length
//
if ( NULL != pIrp->MdlAddress ) { GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer); }
switch ( Class ) { case HttpConfigGroupBandwidthInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, HTTP_CONFIG_GROUP_MAX_BANDWIDTH); VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, HTTP_CONFIG_GROUP_MAX_BANDWIDTH); break;
case HttpConfigGroupConnectionInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, HTTP_CONFIG_GROUP_MAX_CONNECTIONS); VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, HTTP_CONFIG_GROUP_MAX_CONNECTIONS); break; case HttpConfigGroupStateInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, HTTP_CONFIG_GROUP_STATE);
VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, HTTP_CONFIG_GROUP_STATE); break; case HttpConfigGroupConnectionTimeoutInformation: HANDLE_BUFFER_LENGTH_REQUEST(pIrp, pIrpSp, ULONG);
VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, ULONG); break;
case HttpConfigGroupAppPoolInformation:
default: Status = STATUS_INVALID_PARAMETER; goto end; break; }
//
// call the function
//
OutputLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = UlQueryConfigGroupInformation( pInfo->ConfigGroupId, pInfo->InformationClass, pMdlBuffer, OutputLength, &Length );
pIrp->IoStatus.Information = (NT_SUCCESS(Status)) ?
(ULONG_PTR)Length : (ULONG_PTR)0;
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlQueryConfigGroupIoctl
/***************************************************************************++
Routine Description:
This routine sets information associated with a configuration group.
Note: This is a METHOD_IN_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSetConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_CONFIG_GROUP_INFO pInfo; PVOID pMdlBuffer; ULONG OutputLength; PUL_CONTROL_CHANNEL pControlChannel; HTTP_CONFIG_GROUP_INFORMATION_CLASS Class;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(IN_DIRECT, SET_CONFIG_GROUP);
PAGED_CODE();
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_INFO, pInfo);
VALIDATE_INFORMATION_CLASS( pInfo, Class, HTTP_CONFIG_GROUP_INFORMATION_CLASS, HttpConfigGroupMaximumInformation);
//
// Validate input buffer
//
GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer);
switch ( Class ) { case HttpConfigGroupLogInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_LOGGING); break;
case HttpConfigGroupAppPoolInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_APP_POOL); break;
case HttpConfigGroupBandwidthInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_MAX_BANDWIDTH); break;
case HttpConfigGroupConnectionInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_MAX_CONNECTIONS); break;
case HttpConfigGroupStateInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_STATE); break;
case HttpConfigGroupSiteInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_CONFIG_GROUP_SITE); break;
case HttpConfigGroupConnectionTimeoutInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, ULONG); break;
default: Status = STATUS_INVALID_PARAMETER; goto end; break;
}
//
// call the function
//
OutputLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
UlTrace(IOCTL, ("UlSetConfigGroupIoctl: CGroupId=%I64x, " "InfoClass=%d, pMdlBuffer=%p, length=%d\n", pInfo->ConfigGroupId, pInfo->InformationClass, pMdlBuffer, OutputLength ));
Status = UlSetConfigGroupInformation( pInfo->ConfigGroupId, pInfo->InformationClass, pMdlBuffer, OutputLength, pIrp->RequestorMode );
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlSetConfigGroupIoctl
/***************************************************************************++
Routine Description:
This routine adds a new URL prefix to a configuration group.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlAddUrlToConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_CONFIG_GROUP_URL_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel; UNICODE_STRING FullyQualifiedUrl; ACCESS_STATE AccessState; AUX_ACCESS_DATA AuxData; ACCESS_MASK AccessMask;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, ADD_URL_TO_CONFIG_GROUP);
PAGED_CODE();
RtlInitEmptyUnicodeString(&FullyQualifiedUrl, NULL, 0); VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_URL_INFO, pInfo);
__try { Status = UlProbeAndCaptureUnicodeString( &pInfo->FullyQualifiedUrl, pIrp->RequestorMode, &FullyQualifiedUrl, UNICODE_STRING_MAX_WCHAR_LEN ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); }
if (NT_SUCCESS(Status)) { //
// Validate type of operation being performed.
//
if (pInfo->UrlType != HttpUrlOperatorTypeRegistration && pInfo->UrlType != HttpUrlOperatorTypeReservation) { Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Set Access mask.
//
AccessMask = (pInfo->UrlType == HttpUrlOperatorTypeRegistration)? HTTP_ALLOW_REGISTER_URL : HTTP_ALLOW_DELEGATE_URL;
//
// Capture the thread's access state. Adding a reservation is
// delegation.
//
Status = SeCreateAccessState( &AccessState, &AuxData, AccessMask, &g_UrlAccessGenericMapping );
if (NT_SUCCESS(Status)) { Status = UlAddUrlToConfigGroup( pInfo, &FullyQualifiedUrl, &AccessState, AccessMask, pIrp->RequestorMode );
//
// Delete the access state created above.
//
SeDeleteAccessState(&AccessState); } }
end:
UlFreeCapturedUnicodeString(&FullyQualifiedUrl); COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
}
/***************************************************************************++
Routine Description:
This routine removes a URL prefix from a configuration group.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlRemoveUrlFromConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_CONFIG_GROUP_URL_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel; UNICODE_STRING FullyQualifiedUrl; ACCESS_STATE AccessState; AUX_ACCESS_DATA AuxData;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, REMOVE_URL_FROM_CONFIG_GROUP);
PAGED_CODE();
RtlInitEmptyUnicodeString(&FullyQualifiedUrl, NULL, 0); VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_CONFIG_GROUP_URL_INFO, pInfo);
__try { Status = UlProbeAndCaptureUnicodeString( &pInfo->FullyQualifiedUrl, pIrp->RequestorMode, &FullyQualifiedUrl, UNICODE_STRING_MAX_WCHAR_LEN ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); }
if(NT_SUCCESS(Status)) { //
// Validate type of operation being performed.
//
if (pInfo->UrlType != HttpUrlOperatorTypeRegistration && pInfo->UrlType != HttpUrlOperatorTypeReservation) { Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Capture the thread's access state. Removing a reservation is
// same as delegation.
//
Status = SeCreateAccessState( &AccessState, &AuxData, HTTP_ALLOW_DELEGATE_URL, &g_UrlAccessGenericMapping );
if (NT_SUCCESS(Status)) { //
// Further sanitization and stronger checks will happen in cgroup.
//
Status = UlRemoveUrlFromConfigGroup( pInfo, &FullyQualifiedUrl, &AccessState, HTTP_ALLOW_DELEGATE_URL, pIrp->RequestorMode );
//
// Delete the above captured state.
//
SeDeleteAccessState(&AccessState); } }
end:
UlFreeCapturedUnicodeString(&FullyQualifiedUrl); COMPLETE_REQUEST_AND_RETURN(pIrp, Status);
}
/***************************************************************************++
Routine Description:
This routine removes all URLs from a configuration group.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlRemoveAllUrlsFromConfigGroupIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_REMOVE_ALL_URLS_INFO pInfo; PUL_CONTROL_CHANNEL pControlChannel;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, REMOVE_ALL_URLS_FROM_CONFIG_GROUP);
PAGED_CODE();
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_REMOVE_ALL_URLS_INFO, pInfo);
//
// Call the internal worker function.
//
Status = UlRemoveAllUrlsFromConfigGroup( pInfo->ConfigGroupId );
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
}
/***************************************************************************++
Routine Description:
This routine queries information associated with an application pool.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlQueryAppPoolInformationIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_APP_POOL_INFO pInfo; PVOID pMdlBuffer = NULL; ULONG OutputBufferLength; ULONG Length = 0; PUL_APP_POOL_PROCESS pProcess; HTTP_APP_POOL_INFORMATION_CLASS Class;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, QUERY_APP_POOL_INFORMATION);
PAGED_CODE();
// pProcess is an aligned address because it is allocated
// by the I/O Manager.
VALIDATE_APP_POOL(pIrpSp, pProcess, FALSE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_APP_POOL_INFO, pInfo);
VALIDATE_INFORMATION_CLASS( pInfo, Class, HTTP_APP_POOL_INFORMATION_CLASS, HttpConfigGroupMaximumInformation);
// if no outbut buffer passed down in the Irp
// that means app is asking for the required
// field length
if ( NULL != pIrp->MdlAddress ) { GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer); }
// Verify input data in output buffer
switch ( Class ) { case HttpAppPoolQueueLengthInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, LONG); VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, LONG); break;
case HttpAppPoolStateInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, HTTP_APP_POOL_ENABLED_STATE); VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, HTTP_APP_POOL_ENABLED_STATE); break;
case HttpAppPoolLoadBalancerInformation: HANDLE_BUFFER_LENGTH_REQUEST( pIrp, pIrpSp, HTTP_LOAD_BALANCER_CAPABILITIES); VALIDATE_BUFFER_ALIGNMENT( pMdlBuffer, HTTP_LOAD_BALANCER_CAPABILITIES); break;
default: Status = STATUS_INVALID_PARAMETER; goto end; break; }
OutputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = UlQueryAppPoolInformation( pProcess, pInfo->InformationClass, pMdlBuffer, OutputBufferLength, &Length );
pIrp->IoStatus.Information = (NT_SUCCESS(Status)) ?
(ULONG_PTR)Length : (ULONG_PTR)0;
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlQueryAppPoolInformationIoctl
/***************************************************************************++
Routine Description:
This routine sets information associated with an application pool.
Note: This is a METHOD_IN_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSetAppPoolInformationIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_APP_POOL_INFO pInfo; PVOID pMdlBuffer = NULL; PUL_APP_POOL_PROCESS pProcess = NULL; ULONG OutputBufferLength; HTTP_APP_POOL_INFORMATION_CLASS Class;
//
// Sanity check
//
ASSERT_IOCTL_METHOD(IN_DIRECT, SET_APP_POOL_INFORMATION);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, FALSE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_APP_POOL_INFO, pInfo);
VALIDATE_INFORMATION_CLASS( pInfo, Class, HTTP_APP_POOL_INFORMATION_CLASS, HttpConfigGroupMaximumInformation); //
// Validate input buffer
//
GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer);
//
// Also make sure that user buffer was aligned properly
//
switch (pInfo->InformationClass) { case HttpAppPoolQueueLengthInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, LONG); break;
case HttpAppPoolStateInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_APP_POOL_ENABLED_STATE); break;
case HttpAppPoolLoadBalancerInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_LOAD_BALANCER_CAPABILITIES); break;
case HttpAppPoolControlChannelInformation: VALIDATE_OUTPUT_BUFFER_FROM_MDL( pIrpSp, pMdlBuffer, HTTP_APP_POOL_CONTROL_CHANNEL); break;
default: Status = STATUS_INVALID_PARAMETER; goto end; break; }
OutputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
Status = UlSetAppPoolInformation( pProcess, pInfo->InformationClass, pMdlBuffer, OutputBufferLength );
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlSetAppPoolInformationIoctl
/***************************************************************************++
Routine Description:
This routine stops request processing on the app pool and cancels outstanding I/O.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlShutdownAppPoolIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUL_APP_POOL_PROCESS pProcess;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, SHUTDOWN_APP_POOL);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, FALSE);
//
// make the call
//
UlTrace(IOCTL, ("UlShutdownAppPoolIoctl: pAppPoolProcess=%p, pIrp=%p\n", pProcess, pIrp ));
UlShutdownAppPoolProcess( pProcess );
Status = STATUS_SUCCESS;
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlShutdownAppPoolIoctl
/***************************************************************************++
Routine Description:
This routine receives an HTTP request.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlReceiveHttpRequestIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_RECEIVE_REQUEST_INFO pInfo; PUL_APP_POOL_PROCESS pProcess = NULL;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, RECEIVE_HTTP_REQUEST);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE); VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_RECEIVE_REQUEST_INFO, pInfo); VALIDATE_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, PVOID);
//
// First make sure the output buffer is at least
// minimum size. This is important as we require
// at least this much space later.
//
UlTrace(ROUTING, ( "UlReceiveHttpRequestIoctl(outbuf=%d, inbuf=%d)\n", pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, pIrpSp->Parameters.DeviceIoControl.InputBufferLength ));
if ((pIrpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(HTTP_REQUEST)) && (pIrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(HTTP_RECEIVE_REQUEST_INFO))) { if (pInfo->Flags & (~HTTP_RECEIVE_REQUEST_FLAG_VALID)) { Status = STATUS_INVALID_PARAMETER; goto end; }
Status = UlReceiveHttpRequest( pInfo->RequestId, pInfo->Flags, pProcess, pIrp ); } else { Status = STATUS_BUFFER_TOO_SMALL; }
UlTrace(ROUTING, ( "UlReceiveHttpRequestIoctl: BytesNeeded=%Iu, status=0x%x\n", pIrp->IoStatus.Information, Status ));
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlReceiveHttpRequestIoctl
/***************************************************************************++
Routine Description:
This routine receives entity body data from an HTTP request.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlReceiveEntityBodyIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_RECEIVE_REQUEST_INFO pInfo; PUL_APP_POOL_PROCESS pProcess; PUL_INTERNAL_REQUEST pRequest = NULL;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, RECEIVE_ENTITY_BODY);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE); VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_RECEIVE_REQUEST_INFO, pInfo);
//
// Validate output buffer for METHOD_OUT_DIRECT.
//
if (NULL == pIrp->MdlAddress) { Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Now get the request from the request id.
// This gets us a reference to the request.
//
pRequest = UlGetRequestFromId(pInfo->RequestId, pProcess);
if (!pRequest) { Status = STATUS_CONNECTION_INVALID; goto end; }
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
//
// OK, now call the function.
//
Status = UlReceiveEntityBody(pProcess, pRequest, pIrp);
end: if (pRequest != NULL) { UL_DEREFERENCE_INTERNAL_REQUEST(pRequest); pRequest = NULL; }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlReceiveEntityBodyIoctl
/***************************************************************************++
Routine Description:
This routine sends an HTTP response.
Note: This is a METHOD_NEITHER IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSendHttpResponseIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_SEND_HTTP_RESPONSE_INFO pSendInfo; HTTP_SEND_HTTP_RESPONSE_INFO LocalSendInfo; PUL_INTERNAL_RESPONSE pResponse = NULL; PUL_INTERNAL_RESPONSE pResponseCopy; PHTTP_RESPONSE pHttpResponse = NULL; PUL_INTERNAL_REQUEST pRequest = NULL; BOOLEAN ServedFromCache = FALSE; BOOLEAN CaptureCache; PUL_APP_POOL_PROCESS pAppPoolProcess = NULL; ULONG BufferLength = 0; BOOLEAN FastSend = FALSE; BOOLEAN CopySend = FALSE; BOOLEAN CloseConnection = FALSE; BOOLEAN LastResponse = FALSE; HTTP_REQUEST_ID RequestId = HTTP_NULL_ID; HTTP_DATA_CHUNK LocalEntityChunks[UL_LOCAL_CHUNKS]; PHTTP_DATA_CHUNK pLocalEntityChunks = NULL; PHTTP_DATA_CHUNK pEntityChunks; HTTP_LOG_FIELDS_DATA LocalLogData; USHORT StatusCode = 0; ULONGLONG SendBytes = 0; ULONGLONG ConnectionSendBytes = 0; ULONGLONG GlobalSendBytes = 0;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(NEITHER, SEND_HTTP_RESPONSE);
PAGED_CODE();
__try { //
// Ensure that this is really an app pool, not a control channel.
// And it's going until we are done with the sendresponse.
//
VALIDATE_APP_POOL(pIrpSp, pAppPoolProcess, TRUE); VALIDATE_SEND_INFO( pIrp, pIrpSp, pSendInfo, LocalSendInfo, pEntityChunks, pLocalEntityChunks, LocalEntityChunks );
VALIDATE_LOG_DATA(pIrp, LocalSendInfo, LocalLogData);
LastResponse = (BOOLEAN) (0 == (LocalSendInfo.Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA));
if (ETW_LOG_MIN() && LastResponse) { RequestId = LocalSendInfo.RequestId;
UlEtwTraceEvent( &UlTransGuid, ETW_TYPE_ULRECV_RESP, (PVOID) &RequestId, sizeof(HTTP_REQUEST_ID), NULL, 0 ); }
UlTrace(SEND_RESPONSE, ( "http!UlSendHttpResponseIoctl - Flags = %X\n", LocalSendInfo.Flags ));
//
// UlSendHttpResponse() *must* take a PHTTP_RESPONSE. This will
// protect us from those whackos that attempt to build their own
// raw response headers.
//
pHttpResponse = LocalSendInfo.pHttpResponse;
if (pHttpResponse == NULL) { Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Now get the request from the request id.
// This gives us a reference to the request.
//
pRequest = UlGetRequestFromId(LocalSendInfo.RequestId, pAppPoolProcess);
if (pRequest == NULL) { //
// Couldn't map the HTTP_REQUEST_ID.
//
Status = STATUS_CONNECTION_INVALID; goto end; }
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest)); ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
//
// OK, we have the connection. Now capture the incoming
// HTTP_RESPONSE structure and map it to our internal
// format.
//
if (LocalSendInfo.CachePolicy.Policy != HttpCachePolicyNocache) { CaptureCache = pRequest->CachePreconditions; } else { CaptureCache = FALSE; }
//
// Check if we need to perform a CopySend if this IRP is
// non-overlapped and this is not LastResponse.
//
if (g_UlEnableCopySend && !LastResponse && !pIrp->Overlay.AsynchronousParameters.UserApcRoutine && !pIrp->Overlay.AsynchronousParameters.UserApcContext) { CopySend = TRUE; }
//
// Take the fast path if this is a single memory chunk that needs no
// retransmission (<= 64k).
//
if (!CaptureCache && !pRequest->SendInProgress && !CopySend && LocalSendInfo.EntityChunkCount == 1 && pEntityChunks->DataChunkType == HttpDataChunkFromMemory && pEntityChunks->FromMemory.BufferLength <= g_UlMaxBytesPerSend) { BufferLength = pEntityChunks->FromMemory.BufferLength; FastSend = (BOOLEAN) (BufferLength > 0); }
if (!FastSend) { Status = UlCaptureHttpResponse( pAppPoolProcess, LocalSendInfo.pHttpResponse, pRequest, LocalSendInfo.EntityChunkCount, pEntityChunks, UlCaptureNothing, LocalSendInfo.Flags, CaptureCache, LocalSendInfo.pLogData, &StatusCode, &pResponse );
if (!NT_SUCCESS(Status)) { goto end; } }
//
// Apply ConnectionSendLimit and GlobalSendLimit. Only FromMemory
// chunks are taken into account plus the overhead. Do this before
// checking response flags because the check alters request's state.
//
if (FastSend) { SendBytes = BufferLength + g_UlFullTrackerSize; } else { ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse));
SendBytes = pResponse->FromMemoryLength + g_UlResponseBufferSize + g_UlChunkTrackerSize; }
Status = UlCheckSendLimit( pRequest->pHttpConn, SendBytes, &ConnectionSendBytes, &GlobalSendBytes );
if (!NT_SUCCESS(Status)) { goto end; }
//
// Check pRequest->SentResponse and pRequest->SentLast flags.
//
Status = UlCheckSendHttpResponseFlags( pRequest, LocalSendInfo.Flags );
if (!NT_SUCCESS(Status)) { goto end; }
//
// If this is for a zombie connection and not the last sendresponse
// then we will reject. Otherwise if the logging data is provided
// we will only do the logging and bail out.
//
Status = UlCheckForZombieConnection( pRequest, pRequest->pHttpConn, LocalSendInfo.Flags, LocalSendInfo.pLogData, pIrp->RequestorMode );
if (!NT_SUCCESS(Status)) { goto end; }
//
// Capture the user log data if we have a response captured.
//
if (pResponse && LocalSendInfo.pLogData && pRequest->SentLast == 1) { Status = UlCaptureUserLogData( LocalSendInfo.pLogData, pRequest, &pResponse->pLogData );
if (!NT_SUCCESS(Status)) { goto end; } } } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); goto end; }
ASSERT(NT_SUCCESS(Status));
if (FastSend) { Status = UlFastSendHttpResponse( LocalSendInfo.pHttpResponse, LocalSendInfo.pLogData, pEntityChunks, 1, BufferLength, NULL, LocalSendInfo.Flags, pRequest, pIrp, pIrp->RequestorMode, ConnectionSendBytes, GlobalSendBytes, NULL );
goto end; }
//
// At this point, we'll definitely be initiating the
// send. Go ahead and mark the IRP as pending, then
// guarantee that we'll only return pending from
// this point on.
//
IoMarkIrpPending( pIrp );
//
// Remember ConnectionSendBytes and GlobalSendBytes. These are needed
// to uncheck send limit when the IRP is completed.
//
ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse));
pResponse->ConnectionSendBytes = ConnectionSendBytes; pResponse->GlobalSendBytes = GlobalSendBytes;
//
// Set CopySend flag on the response.
//
pResponse->CopySend = CopySend;
//
// Save the captured response in the IRP so we can dereference it
// after the IRP completes. The ownership of pResponse is transferred
// to the IRP beyond this point so we zap pResponse to avoid
// double-derefrence in the cleanup.
//
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pResponse; pResponseCopy = pResponse; pResponse = NULL;
//
// Prepare the response (open files, etc).
//
Status = UlPrepareHttpResponse( pRequest->Version, pHttpResponse, pResponseCopy, UserMode );
if (NT_SUCCESS(Status)) { //
// Try capture to cache and send
//
if (CaptureCache) { Status = UlCacheAndSendResponse( pRequest, pResponseCopy, pAppPoolProcess, LocalSendInfo.CachePolicy, &UlpRestartSendHttpResponse, pIrp, &ServedFromCache );
if (NT_SUCCESS(Status) && !ServedFromCache) { //
// Send the non-cached response
//
Status = UlSendHttpResponse( pRequest, pResponseCopy, &UlpRestartSendHttpResponse, pIrp ); } } else { //
// Non-cacheable request/response, send response directly.
//
Status = UlSendHttpResponse( pRequest, pResponseCopy, &UlpRestartSendHttpResponse, pIrp ); } }
if (Status != STATUS_PENDING) { ASSERT(Status != STATUS_SUCCESS);
//
// UlSendHttpResponse either completed in-line
// (extremely unlikely) or failed (much more
// likely). Fake a completion to the completion
// routine so that the IRP will get completed
// properly, then map the return code to
// STATUS_PENDING, since we've already marked
// the IRP as such.
//
UlpRestartSendHttpResponse( pIrp, Status, 0 );
CloseConnection = TRUE;
Status = STATUS_PENDING; }
end:
//
// Free the local chunk array if we have allocated one.
//
if (pLocalEntityChunks) { UL_FREE_POOL(pLocalEntityChunks, UL_DATA_CHUNK_POOL_TAG); }
//
// Close the connection if we hit an error.
//
if (pRequest) { //
// STATUS_OBJECT_PATH_NOT_FOUND means no cache chunk is found for the
// response to send, in which case we should not close the connection
// but rather let the user retry.
//
if ((NT_ERROR(Status) && STATUS_OBJECT_PATH_NOT_FOUND != Status) || CloseConnection) { UlCloseConnection( pRequest->pHttpConn->pConnection, TRUE, NULL, NULL ); }
//
// Uncheck either ConnectionSendBytes or GlobalSendBytes while we
// still have a reference on the HttpConnection.
//
if (Status != STATUS_PENDING) { UlUncheckSendLimit( pRequest->pHttpConn, ConnectionSendBytes, GlobalSendBytes ); }
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest); }
if (pResponse) { ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse)); UL_DEREFERENCE_INTERNAL_RESPONSE(pResponse); }
//
// If the last response was an error case, log an error event here
//
if (ETW_LOG_MIN() && LastResponse && (NT_ERROR(Status) && Status != STATUS_OBJECT_PATH_NOT_FOUND)) { UlEtwTraceEvent( &UlTransGuid, ETW_TYPE_SEND_ERROR, (PVOID) &RequestId, sizeof(HTTP_REQUEST_ID), (PVOID) &StatusCode, sizeof(USHORT), NULL, 0 ); }
if (Status != STATUS_PENDING) { pIrp->IoStatus.Status = Status; UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
RETURN(Status);
} // UlSendHttpResponseIoctl
/***************************************************************************++
Routine Description:
This routine sends an HTTP entity body.
Note: This is a METHOD_NEITHER IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlSendEntityBodyIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_SEND_HTTP_RESPONSE_INFO pSendInfo; HTTP_SEND_HTTP_RESPONSE_INFO LocalSendInfo; PUL_INTERNAL_RESPONSE pResponse = NULL; PUL_INTERNAL_RESPONSE pResponseCopy; PUL_INTERNAL_REQUEST pRequest = NULL; PUL_APP_POOL_PROCESS pAppPoolProcess = NULL; ULONG BufferLength = 0; BOOLEAN FastSend = FALSE; BOOLEAN CopySend = FALSE; BOOLEAN CloseConnection = FALSE; BOOLEAN LastResponse = FALSE; HTTP_REQUEST_ID RequestId = HTTP_NULL_ID; HTTP_DATA_CHUNK LocalEntityChunks[UL_LOCAL_CHUNKS]; PHTTP_DATA_CHUNK pLocalEntityChunks = NULL; PHTTP_DATA_CHUNK pEntityChunks; HTTP_LOG_FIELDS_DATA LocalLogData; USHORT StatusCode = 0; ULONGLONG SendBytes = 0; ULONGLONG ConnectionSendBytes = 0; ULONGLONG GlobalSendBytes = 0;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(NEITHER, SEND_ENTITY_BODY);
PAGED_CODE();
__try { VALIDATE_APP_POOL(pIrpSp, pAppPoolProcess, TRUE); VALIDATE_SEND_INFO( pIrp, pIrpSp, pSendInfo, LocalSendInfo, pEntityChunks, pLocalEntityChunks, LocalEntityChunks );
VALIDATE_LOG_DATA(pIrp, LocalSendInfo, LocalLogData);
LastResponse = (BOOLEAN) (0 == (LocalSendInfo.Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA));
if (ETW_LOG_MIN() && LastResponse) { RequestId = LocalSendInfo.RequestId;
UlEtwTraceEvent( &UlTransGuid, ETW_TYPE_ULRECV_RESPBODY, (PVOID) &RequestId, sizeof(HTTP_REQUEST_ID), NULL, 0 ); }
UlTrace(SEND_RESPONSE, ( "http!UlSendEntityBodyIoctl - Flags = %X\n", LocalSendInfo.Flags ));
//
// Now get the request from the request id.
// This gives us a reference to the request.
//
pRequest = UlGetRequestFromId(LocalSendInfo.RequestId, pAppPoolProcess);
if (pRequest == NULL) { //
// Couldn't map the HTTP_REQUEST_ID.
//
Status = STATUS_CONNECTION_INVALID; goto end; }
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest)); ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
//
// Check if we need to perform a CopySend if this IRP is
// non-overlapped and this is not LastResponse.
//
if (g_UlEnableCopySend && !LastResponse && !pIrp->Overlay.AsynchronousParameters.UserApcRoutine && !pIrp->Overlay.AsynchronousParameters.UserApcContext) { CopySend = TRUE; }
//
// Take the fast path if this is a single memory chunk that needs no
// retransmission (<= 64k).
//
if (!pRequest->SendInProgress && !CopySend && LocalSendInfo.EntityChunkCount == 1 && pEntityChunks->DataChunkType == HttpDataChunkFromMemory && pEntityChunks->FromMemory.BufferLength <= g_UlMaxBytesPerSend) { BufferLength = pEntityChunks->FromMemory.BufferLength; FastSend = (BOOLEAN) (BufferLength > 0); }
//
// OK, we have the connection. Now capture the incoming
// HTTP_RESPONSE structure and map it to our internal
// format if this is not a FastSend.
//
if (!FastSend) { Status = UlCaptureHttpResponse( pAppPoolProcess, NULL, pRequest, LocalSendInfo.EntityChunkCount, pEntityChunks, UlCaptureNothing, LocalSendInfo.Flags, FALSE, LocalSendInfo.pLogData, &StatusCode, &pResponse );
if (!NT_SUCCESS(Status)) { goto end; } }
//
// Apply ConnectionSendLimit and GlobalSendLimit. Only FromMemory
// chunks are taken into account plus the overhead. Do this before
// checking response flags because the check alters request's state.
//
if (FastSend) { SendBytes = BufferLength + g_UlFullTrackerSize; } else { ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse));
SendBytes = pResponse->FromMemoryLength + g_UlResponseBufferSize + g_UlChunkTrackerSize; }
Status = UlCheckSendLimit( pRequest->pHttpConn, SendBytes, &ConnectionSendBytes, &GlobalSendBytes );
if (!NT_SUCCESS(Status)) { goto end; }
//
// Check pRequest->SentResponse and pRequest->SentLast flags.
//
Status = UlCheckSendEntityBodyFlags( pRequest, LocalSendInfo.Flags );
if (!NT_SUCCESS(Status)) { goto end; }
//
// If this is for a zombie connection and not the last sendresponse
// then we will reject. Otherwise if the logging data is provided
// we will only do the logging and bail out.
//
Status = UlCheckForZombieConnection( pRequest, pRequest->pHttpConn, LocalSendInfo.Flags, LocalSendInfo.pLogData, pIrp->RequestorMode );
if (!NT_SUCCESS(Status)) { goto end; }
//
// Capture the user log data if we have a response captured.
//
if (pResponse && LocalSendInfo.pLogData && pRequest->SentLast == 1) { Status = UlCaptureUserLogData( LocalSendInfo.pLogData, pRequest, &pResponse->pLogData );
if (!NT_SUCCESS(Status)) { goto end; } } } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); goto end; }
ASSERT(NT_SUCCESS(Status)); ASSERT(LocalSendInfo.pHttpResponse == NULL);
if (FastSend) { Status = UlFastSendHttpResponse( NULL, LocalSendInfo.pLogData, pEntityChunks, 1, BufferLength, NULL, LocalSendInfo.Flags, pRequest, pIrp, pIrp->RequestorMode, ConnectionSendBytes, GlobalSendBytes, NULL );
goto end; }
//
// At this point, we'll definitely be initiating the
// send. Go ahead and mark the IRP as pending, then
// guarantee that we'll only return pending from
// this point on.
//
IoMarkIrpPending( pIrp );
// Remember ConnectionSendBytes and GlobalSendBytes. These are needed
// to uncheck send limit when the IRP is completed.
//
ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse));
pResponse->ConnectionSendBytes = ConnectionSendBytes; pResponse->GlobalSendBytes = GlobalSendBytes;
//
// Set CopySend flag on the response.
//
pResponse->CopySend = CopySend;
//
// Save the captured response in the IRP so we can dereference it
// after the IRP completes. The ownership of pResponse is transferred
// to the IRP beyond this point so we zap pResponse to avoid
// double-derefrence in the cleanup.
//
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pResponse; pResponseCopy = pResponse; pResponse = NULL;
//
// Prepare the response (open files, etc).
//
Status = UlPrepareHttpResponse( pRequest->Version, NULL, pResponseCopy, UserMode );
if (NT_SUCCESS(Status)) { //
// Send the response
//
Status = UlSendHttpResponse( pRequest, pResponseCopy, &UlpRestartSendHttpResponse, pIrp ); }
if (Status != STATUS_PENDING) { ASSERT(Status != STATUS_SUCCESS);
//
// UlSendHttpResponse either completed in-line
// (extremely unlikely) or failed (much more
// likely). Fake a completion to the completion
// routine so that the IRP will get completed
// properly, then map the return code to
// STATUS_PENDING, since we've already marked
// the IRP as such.
//
UlpRestartSendHttpResponse( pIrp, Status, 0 );
CloseConnection = TRUE;
Status = STATUS_PENDING; }
end:
//
// Free the local chunk array if we have allocated one.
//
if (pLocalEntityChunks) { UL_FREE_POOL(pLocalEntityChunks, UL_DATA_CHUNK_POOL_TAG); }
//
// Close the connection if we hit an error.
//
if (pRequest) { //
// STATUS_OBJECT_PATH_NOT_FOUND means no cache chunk is found for the
// response to send, in which case we should not close the connection
// but rather let the user retry.
//
if ((NT_ERROR(Status) && STATUS_OBJECT_PATH_NOT_FOUND != Status) || CloseConnection) { UlCloseConnection( pRequest->pHttpConn->pConnection, TRUE, NULL, NULL ); }
//
// Uncheck either ConnectionSendBytes or GlobalSendBytes while we
// still have a reference on the HttpConnection.
//
if (Status != STATUS_PENDING) { UlUncheckSendLimit( pRequest->pHttpConn, ConnectionSendBytes, GlobalSendBytes ); }
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest); }
if (pResponse) { ASSERT(UL_IS_VALID_INTERNAL_RESPONSE(pResponse)); UL_DEREFERENCE_INTERNAL_RESPONSE(pResponse); }
//
// If the last response was an error case, log an error event here
//
if (ETW_LOG_MIN() && LastResponse && (NT_ERROR(Status) && Status != STATUS_OBJECT_PATH_NOT_FOUND)) { UlEtwTraceEvent( &UlTransGuid, ETW_TYPE_SEND_ERROR, (PVOID) &RequestId, sizeof(HTTP_REQUEST_ID), (PVOID) &StatusCode, sizeof(USHORT), NULL, 0 ); }
if (Status != STATUS_PENDING) { pIrp->IoStatus.Status = Status; UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
RETURN(Status);
} // UlSendEntityBodyIoctl
/***************************************************************************++
Routine Description:
This routine flushes a URL or URL tree from the response cache.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFlushResponseCacheIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_FLUSH_RESPONSE_CACHE_INFO pInfo = NULL; PUL_APP_POOL_PROCESS pProcess; UNICODE_STRING FullyQualifiedUrl;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, FLUSH_RESPONSE_CACHE);
PAGED_CODE();
RtlInitEmptyUnicodeString(&FullyQualifiedUrl, NULL, 0); VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_FLUSH_RESPONSE_CACHE_INFO, pInfo);
//
// Check the flag.
//
if (pInfo->Flags != (pInfo->Flags & HTTP_FLUSH_RESPONSE_FLAG_VALID)) { Status = STATUS_INVALID_PARAMETER; goto end; }
Status = UlProbeAndCaptureUnicodeString( &pInfo->FullyQualifiedUrl, pIrp->RequestorMode, &FullyQualifiedUrl, UNICODE_STRING_MAX_WCHAR_LEN );
if (NT_SUCCESS(Status)) { UlFlushCacheByUri( FullyQualifiedUrl.Buffer, FullyQualifiedUrl.Length, pInfo->Flags, pProcess ); } end:
UlFreeCapturedUnicodeString(&FullyQualifiedUrl); COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFlushResponseCacheIoctl
/***************************************************************************++
Routine Description:
This routine waits for demand start notifications.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlWaitForDemandStartIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUL_APP_POOL_PROCESS pProcess;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, WAIT_FOR_DEMAND_START);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, FALSE);
//
// make the call
//
UlTrace(IOCTL, ("UlWaitForDemandStartIoctl: pAppPoolProcess=%p, pIrp=%p\n", pProcess, pIrp ));
Status = UlWaitForDemandStart(pProcess, pIrp);
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlWaitForDemandStartIoctl
/***************************************************************************++
Routine Description:
This routine waits for the client to initiate a disconnect.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlWaitForDisconnectIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_WAIT_FOR_DISCONNECT_INFO pInfo; PUL_HTTP_CONNECTION pHttpConn = NULL; PUL_APP_POOL_PROCESS pProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, WAIT_FOR_DISCONNECT);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_WAIT_FOR_DISCONNECT_INFO, pInfo);
//
// Chase down the connection.
//
pHttpConn = UlGetConnectionFromId( pInfo->ConnectionId );
if (!pHttpConn) { Status = STATUS_CONNECTION_INVALID; goto end; }
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
//
// Do it.
//
Status = UlWaitForDisconnect(pProcess, pHttpConn, pIrp);
end: if (pHttpConn) { UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlWaitForDisconnectIoctl
//
// Private functions.
//
/***************************************************************************++
Routine Description:
Completion routine for UlSendHttpResponse().
Arguments:
pCompletionContext - Supplies an uninterpreted context value as passed to the asynchronous API. In this case, it's actually a pointer to the user's IRP.
Status - Supplies the final completion status of the asynchronous API.
Information - Optionally supplies additional information about the completed operation, such as the number of bytes transferred.
--***************************************************************************/ VOID UlpRestartSendHttpResponse( IN PVOID pCompletionContext, IN NTSTATUS Status, IN ULONG_PTR Information ) { PIRP pIrp; PIO_STACK_LOCATION pIrpSp; PUL_INTERNAL_RESPONSE pResponse;
//
// Snag the IRP from the completion context, fill in the completion
// status, then complete the IRP.
//
pIrp = (PIRP)pCompletionContext; pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
pResponse = (PUL_INTERNAL_RESPONSE)( pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer );
ASSERT( UL_IS_VALID_INTERNAL_RESPONSE( pResponse ) );
//
// Set pResponse->pIrp and pResponse->IoStatus so we can complete the
// IRP when the reference of the response drops to 0.
//
pResponse->pIrp = pIrp; pResponse->IoStatus.Status = Status; pResponse->IoStatus.Information = Information;
//
// Drop the initial/last reference of the response.
//
UL_DEREFERENCE_INTERNAL_RESPONSE( pResponse );
} // UlpRestartSendHttpResponse
/***************************************************************************++
Routine Description:
This routine stops request processing on the filter channel and cancels outstanding I/O.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlShutdownFilterIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUL_FILTER_PROCESS pProcess;
//
// sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, SHUTDOWN_FILTER_CHANNEL);
PAGED_CODE();
VALIDATE_FILTER_PROCESS(pIrpSp, pProcess);
//
// make the call
//
UlTrace(IOCTL, ("UlShutdownFilterIoctl: pFilterProcess=%p, pIrp=%p\n", pProcess, pIrp ));
UlShutdownFilterProcess( pProcess );
Status = STATUS_SUCCESS;
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
}
/***************************************************************************++
Routine Description:
This routine accepts a raw connection.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterAcceptIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PUL_FILTER_PROCESS pFilterProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, FILTER_ACCEPT);
PAGED_CODE();
VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess); VALIDATE_OUTPUT_BUFFER_SIZE(pIrpSp, HTTP_RAW_CONNECTION_INFO); VALIDATE_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, HTTP_RAW_CONNECTION_INFO);
//
// make the call
//
Status = UlFilterAccept(pFilterProcess, pIrp);
end: COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFilterAcceptIoctl
/***************************************************************************++
Routine Description:
This routine closes a raw connection.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterCloseIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_RAW_CONNECTION_ID pConnectionId; PUX_FILTER_CONNECTION pConnection = NULL; PUL_FILTER_PROCESS pFilterProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, FILTER_CLOSE);
PAGED_CODE();
VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_RAW_CONNECTION_ID, pConnectionId);
pConnection = UlGetRawConnectionFromId(*pConnectionId);
if (!pConnection) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
//
// make the call
//
Status = UlFilterClose(pFilterProcess, pConnection, pIrp);
end:
if (pConnection) { DEREFERENCE_FILTER_CONNECTION(pConnection); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFilterCloseIoctl
/***************************************************************************++
Routine Description:
This routine reads data from a raw connection.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterRawReadIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_RAW_CONNECTION_ID pConnectionId; PUX_FILTER_CONNECTION pConnection = NULL; PUL_FILTER_PROCESS pFilterProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, FILTER_RAW_READ);
PAGED_CODE();
VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess); VALIDATE_OUTPUT_BUFFER_SIZE(pIrpSp, UCHAR); VALIDATE_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, UCHAR);
//
// Immediately fork to the appropriate code if we're doing a combined
// read and write.
//
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(HTTP_FILTER_BUFFER_PLUS)) { return UlFilterAppWriteAndRawRead(pIrp, pIrpSp); }
__try { //
// Get the connection ID.
//
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_RAW_CONNECTION_ID, pConnectionId);
pConnection = UlGetRawConnectionFromId(*pConnectionId);
if (!pConnection) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
Status = UlFilterRawRead(pFilterProcess, pConnection, pIrp);
} __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); UlTrace( FILTER, ( "UlFilterRawReadIoctl: Exception hit! 0x%08X\n", Status )); }
end: if (pConnection) { DEREFERENCE_FILTER_CONNECTION(pConnection); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFilterRawReadIoctl
/***************************************************************************++
Routine Description:
This routine writes data to a raw connection.
Note: This is a METHOD_IN_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterRawWriteIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_RAW_CONNECTION_ID pConnectionId; PUX_FILTER_CONNECTION pConnection = NULL; PUL_FILTER_PROCESS pFilterProcess; BOOLEAN MarkedPending = FALSE;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(IN_DIRECT, FILTER_RAW_WRITE);
PAGED_CODE();
__try { VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_RAW_CONNECTION_ID, pConnectionId);
if (!pIrp->MdlAddress) { Status = STATUS_INVALID_PARAMETER; goto end; }
pConnection = UlGetRawConnectionFromId(*pConnectionId);
if (!pConnection) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
//
// make the call
//
IoMarkIrpPending(pIrp); MarkedPending = TRUE;
Status = UlFilterRawWrite( pFilterProcess, pConnection, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, pIrp ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); UlTrace( FILTER, ( "UlFilterRawWriteIoctl: Exception hit! 0x%08X\n", Status ));
}
end: if (pConnection) { DEREFERENCE_FILTER_CONNECTION(pConnection); }
//
// complete the request?
//
if (Status != STATUS_PENDING) { pIrp->IoStatus.Status = Status; UlCompleteRequest( pIrp, IO_NO_INCREMENT );
if (MarkedPending) { //
// Since we marked the IRP pending, we should return pending.
//
Status = STATUS_PENDING; }
} else { //
// If we're returning pending, the IRP better be marked pending.
//
ASSERT(MarkedPending); }
RETURN( Status );
} // UlFilterRawWriteIoctl
/***************************************************************************++
Routine Description:
This routine reads data from an http application.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterAppReadIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PUX_FILTER_CONNECTION pConnection = NULL; PHTTP_FILTER_BUFFER pFiltBuffer; PUL_FILTER_PROCESS pFilterProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, FILTER_APP_READ);
PAGED_CODE();
//
// Immediately fork to the appropriate code if we're doing a combined
// read and write.
//
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(HTTP_FILTER_BUFFER_PLUS)) { return UlFilterRawWriteAndAppRead(pIrp, pIrpSp); }
__try {
VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_FILTER_BUFFER, pFiltBuffer);
VALIDATE_OUTPUT_BUFFER_SIZE(pIrpSp, HTTP_FILTER_BUFFER); VALIDATE_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, HTTP_FILTER_BUFFER);
//
// Map the incoming connection ID to the corresponding
// UX_FILTER_CONNECTION object.
//
pConnection = UlGetRawConnectionFromId(pFiltBuffer->Reserved);
if (!pConnection) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
Status = UlFilterAppRead(pFilterProcess, pConnection, pIrp);
} __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); UlTrace( FILTER, ( "UlFilterAppReadIoctl: Exception hit! 0x%08X\n", Status ));
}
end: if (pConnection) { DEREFERENCE_FILTER_CONNECTION(pConnection); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFilterAppReadIoctl
/***************************************************************************++
Routine Description:
This routine writes data to an http application.
Note: This is a METHOD_IN_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlFilterAppWriteIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PUX_FILTER_CONNECTION pConnection = NULL; BOOLEAN MarkedPending = FALSE; PHTTP_FILTER_BUFFER pFiltBuffer; PUL_FILTER_PROCESS pFilterProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(IN_DIRECT, FILTER_APP_WRITE);
PAGED_CODE();
__try { VALIDATE_FILTER_PROCESS(pIrpSp, pFilterProcess); VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_FILTER_BUFFER, pFiltBuffer);
//
// Map the incoming connection ID to the corresponding
// UX_FILTER_CONNECTION object.
//
pConnection = UlGetRawConnectionFromId(pFiltBuffer->Reserved);
if (!pConnection) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
//
// make the call
//
IoMarkIrpPending(pIrp); MarkedPending = TRUE;
Status = UlFilterAppWrite(pFilterProcess, pConnection, pIrp); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); UlTrace( FILTER, ( "UlFilterAppWriteIoctl: Exception hit! 0x%08X\n", Status ));
}
end: if (pConnection) { DEREFERENCE_FILTER_CONNECTION(pConnection); }
//
// complete the request?
//
if (Status != STATUS_PENDING) { pIrp->IoStatus.Status = Status; UlCompleteRequest( pIrp, IO_NO_INCREMENT );
if (MarkedPending) { //
// Since we marked the IRP pending, we should return pending.
//
Status = STATUS_PENDING; }
} else { //
// If we're returning pending, the IRP better be marked pending.
//
ASSERT(MarkedPending); }
RETURN( Status );
} // UlFilterAppWriteIoctl
/***************************************************************************++
Routine Description:
This routine asks the SSL helper for a client certificate.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlReceiveClientCertIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_FILTER_RECEIVE_CLIENT_CERT_INFO pReceiveCertInfo; PUL_HTTP_CONNECTION pHttpConn = NULL; PUL_APP_POOL_PROCESS pProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, FILTER_RECEIVE_CLIENT_CERT);
PAGED_CODE();
__try { VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_FILTER_RECEIVE_CLIENT_CERT_INFO, pReceiveCertInfo);
VALIDATE_OUTPUT_BUFFER_SIZE(pIrpSp, HTTP_SSL_CLIENT_CERT_INFO); VALIDATE_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, PVOID);
//
// Map the incoming connection ID to the corresponding
// HTTP_CONNECTION object.
//
pHttpConn = UlGetConnectionFromId(pReceiveCertInfo->ConnectionId);
if (!pHttpConn) { Status = STATUS_CONNECTION_INVALID; goto end; }
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
//
// make the call
//
Status = UlReceiveClientCert( pProcess, &pHttpConn->pConnection->FilterInfo, pReceiveCertInfo->Flags, pIrp ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode()); }
end: if (pHttpConn) { UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn); }
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlFilterReceiveClientCertIoctl
/***************************************************************************++
Routine Description:
This routine returns the perfmon counter data for this driver
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlGetCountersIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUL_CONTROL_CHANNEL pControlChannel; PVOID pMdlBuffer = NULL; PHTTP_COUNTER_GROUP pCounterGroup = NULL; ULONG Blocks; ULONG Length = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; //
// Sanity check.
//
ASSERT_IOCTL_METHOD(OUT_DIRECT, GET_COUNTERS);
PAGED_CODE();
//
// If not returning STATUS_SUCCESS,
// IoStatus.Information *must* be 0.
//
pIrp->IoStatus.Information = 0;
//
// Validate Parameters
//
VALIDATE_CONTROL_CHANNEL(pIrpSp, pControlChannel);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_COUNTER_GROUP, pCounterGroup);
// Crack IRP and get MDL containing user's buffer
// Crack MDL to get user's buffer
// if no outbut buffer pass down in the Irp
// that means app is asking for the required
// field length
if ( NULL != pIrp->MdlAddress ) { GET_OUTPUT_BUFFER_ADDRESS_FROM_MDL(pIrp, pMdlBuffer); }
//
// Call support function to gather appropriate counter blocks
// and place in user's buffer.
//
if (HttpCounterGroupGlobal == *pCounterGroup) { VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, HTTP_GLOBAL_COUNTERS);
Status = UlGetGlobalCounters( pMdlBuffer, Length, &Length ); } else if (HttpCounterGroupSite == *pCounterGroup) { VALIDATE_BUFFER_ALIGNMENT(pMdlBuffer, HTTP_SITE_COUNTERS);
Status = UlGetSiteCounters( pControlChannel, pMdlBuffer, Length, &Length, &Blocks ); } else { Status = STATUS_NOT_IMPLEMENTED; }
if ( NT_SUCCESS(Status) || NT_INFORMATION(Status) ) { pIrp->IoStatus.Information = (ULONG_PTR)Length; }
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlGetCountersIoctl
/***************************************************************************++
Routine Description:
This routine adds a fragment cache entry.
Note: This is a METHOD_BUFFERED IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlAddFragmentToCacheIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_ADD_FRAGMENT_INFO pInfo = NULL; PUL_APP_POOL_PROCESS pProcess;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(BUFFERED, ADD_FRAGMENT_TO_CACHE);
PAGED_CODE();
VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE);
VALIDATE_INPUT_BUFFER(pIrp, pIrpSp, HTTP_ADD_FRAGMENT_INFO, pInfo);
//
// Add a new fragment cache entry.
//
Status = UlAddFragmentToCache( pProcess, &pInfo->FragmentName, &pInfo->DataChunk, &pInfo->CachePolicy, pIrp->RequestorMode );
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlAddFragmentToCacheIoctl
/***************************************************************************++
Routine Description:
This routine reads the data back from the fragment cache entry.
Note: This is a METHOD_OUT_DIRECT IOCTL.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UlReadFragmentFromCacheIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUL_APP_POOL_PROCESS pProcess; ULONG BytesRead = 0;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(NEITHER, READ_FRAGMENT_FROM_CACHE);
PAGED_CODE();
//
// Initialize total bytes read.
//
pIrp->IoStatus.Information = 0;
VALIDATE_APP_POOL(pIrpSp, pProcess, TRUE);
Status = UlReadFragmentFromCache( pProcess, pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrp->UserBuffer, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, pIrp->RequestorMode, &BytesRead );
pIrp->IoStatus.Information = BytesRead;
end:
COMPLETE_REQUEST_AND_RETURN( pIrp, Status );
} // UlReadFragmentFromCacheIoctl
/***************************************************************************++
Routine Description:
This routine sends Entity body on a request.
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcSendEntityBodyIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status; PHTTP_SEND_REQUEST_ENTITY_BODY_INFO pSendInfo; PUC_HTTP_REQUEST pRequest = 0; PUC_HTTP_SEND_ENTITY_BODY pKeEntity = 0; KIRQL OldIrql; BOOLEAN bDontFail = FALSE; BOOLEAN bLast;
//
// Sanity check.
//
ASSERT_IOCTL_METHOD(IN_DIRECT, SEND_REQUEST_ENTITY_BODY);
PAGED_CODE();
do { //
// Ensure this is really an app pool, not a control channel.
//
if (IS_SERVER(pIrpSp->FileObject) == FALSE) { //
// Not an server
//
Status = STATUS_INVALID_HANDLE;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
//
// Ensure the input buffer is large enough.
//
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*pSendInfo)) { //
// Input buffer too small.
//
Status = STATUS_BUFFER_TOO_SMALL;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
pSendInfo = (PHTTP_SEND_REQUEST_ENTITY_BODY_INFO)pIrp->AssociatedIrp.SystemBuffer;
//
// Now get the request from the request id.
// This gives us a reference to the request.
//
// NOTE: We don't have to worry about the RequestID being changed,
// since it's not a pointer.
//
pRequest = (PUC_HTTP_REQUEST) UlGetObjectFromOpaqueId(pSendInfo->RequestID, UlOpaqueIdTypeHttpRequest, UcReferenceRequest);
if (UC_IS_VALID_HTTP_REQUEST(pRequest) == FALSE) { //
// Couldn't map the UL_HTTP_REQUEST_ID.
//
Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, pRequest, NULL, NULL, UlongToPtr(Status) );
break; }
if(pRequest->pFileObject != pIrpSp->FileObject) { //
// Cant allow the app to use someone else's RequestID
//
Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, pRequest, NULL, NULL, UlongToPtr(Status) );
break; }
if(pSendInfo->Flags & (~HTTP_SEND_REQUEST_FLAG_VALID)) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, pRequest, NULL, NULL, UlongToPtr(Status) );
break; }
bLast = FALSE;
if(!(pSendInfo->Flags & HTTP_SEND_REQUEST_FLAG_MORE_DATA)) { //
// Remember that this was the last send. We shouldn't
// get any more data after this.
//
bLast = TRUE;
}
ExAcquireFastMutex(&pRequest->Mutex);
Status = UcCaptureEntityBody( pSendInfo, pIrp, pRequest, &pKeEntity, bLast );
ExReleaseFastMutex(&pRequest->Mutex);
if(!NT_SUCCESS(Status)) { //
// NOTE: If the SendEntity IRP fails for some reason or the other,
// we will be failing the entire request. This simplifies the code
// somewhat (For e.g. When we get a entity, we record some state
// in UC_HTTP_REQUEST. If we were not failing the entire request,
// we would have to unwind the state if an entity failed). It's a
// lot simpler to just fail the entire request and get the app to
// post another one.
//
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pKeEntity;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, pRequest, pKeEntity, pIrp, UlongToPtr(Status) );
Status = UcSendEntityBody(pRequest, pKeEntity, pIrp, pIrpSp, &bDontFail, bLast );
} while(FALSE);
if(Status == STATUS_SUCCESS) { pIrp->IoStatus.Status = Status; UlCompleteRequest(pIrp, IO_NO_INCREMENT); } else if(Status != STATUS_PENDING) { if(pKeEntity) { ASSERT(pRequest);
UcFreeSendMdls(pKeEntity->pMdlHead);
UL_FREE_POOL_WITH_QUOTA( pKeEntity, UC_ENTITY_POOL_TAG, NonPagedPool, pKeEntity->BytesAllocated, pRequest->pServerInfo->pProcess ); }
if(pRequest) {
if(!bDontFail) { UlAcquireSpinLock(&pRequest->pConnection->SpinLock, &OldIrql);
UcFailRequest(pRequest, Status, OldIrql); }
//
// Deref for the ref that we took above.
//
UC_DEREFERENCE_REQUEST(pRequest); }
pIrp->IoStatus.Status = Status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
return Status; } // UcSendEntityBodyIoctl
/***************************************************************************++
Routine Description:
This routine receives a HTTP response
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcReceiveResponseIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { PHTTP_RECEIVE_RESPONSE_INFO pInfo = NULL; NTSTATUS Status = STATUS_SUCCESS; PUC_HTTP_REQUEST pRequest = NULL; ULONG BytesTaken = 0;
ASSERT_IOCTL_METHOD(OUT_DIRECT, RECEIVE_RESPONSE);
do { if(!IS_SERVER(pIrpSp->FileObject)) { Status = STATUS_INVALID_HANDLE;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
//
// Grab the input buffer
//
pInfo = (PHTTP_RECEIVE_RESPONSE_INFO) pIrp->AssociatedIrp.SystemBuffer;
//
// See if the input buffer is large enough.
//
if( (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HTTP_RESPONSE)) || (pIrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(*pInfo)) ) { Status = STATUS_BUFFER_TOO_SMALL;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
//
// NOTE: We don't have to worry about the RequestID being changed,
// since it's not a pointer.
//
if(HTTP_IS_NULL_ID(&pInfo->RequestID) || pInfo->Flags != 0) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
pRequest = (PUC_HTTP_REQUEST) UlGetObjectFromOpaqueId( pInfo->RequestID, UlOpaqueIdTypeHttpRequest, UcReferenceRequest );
if (UC_IS_VALID_HTTP_REQUEST(pRequest) == FALSE) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
if(pRequest->pFileObject != pIrpSp->FileObject) { //
// Cant allow the app to use someone else's RequestID
//
Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
BytesTaken = 0; Status = UcReceiveHttpResponse( pRequest, pIrp, &BytesTaken );
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_NEW_RESPONSE, NULL, pRequest, pIrp, UlongToPtr(Status) );
} while(FALSE);
if(Status == STATUS_SUCCESS) { pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = (ULONG_PTR) BytesTaken; UlCompleteRequest(pIrp, IO_NO_INCREMENT);
} else if(Status != STATUS_PENDING) { if(pRequest) { UC_DEREFERENCE_REQUEST(pRequest); }
pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = (ULONG_PTR) BytesTaken;
//
// If we are not completing the IRP with STATUS_SUCCESS, the IO
// manager eats the pIrp->IoStatus.Information. But, the user wants
// to see this information (e.g. when we complete the IRP with
// STATUS_BUFFER_OVERFLOW, we want to tell the app how much to write.
//
// So, we convey this information using the app's pointer. Note that
// this can be done only if we are completing the IRP synchronously.
//
__try { // This is METHOD_OUT_DIRECT, so the input buffer comes from
// the IO manager. So, we don't have to worry about the app
// changing pInfo->pBytesTaken after we've probed it.
//
// We still have to probe & access it in a try except block,
// since this is a user mode pointer.
if(pInfo && pInfo->pBytesTaken) { UlProbeForWrite( pInfo->pBytesTaken, sizeof(ULONG), sizeof(ULONG), pIrp->RequestorMode );
*pInfo->pBytesTaken = BytesTaken; } } __except( UL_EXCEPTION_FILTER()) { }
UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
return Status; } // UcReceiveResponseIoctl
/***************************************************************************++
Routine Description:
This routine sets per server configuration information
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcSetServerContextInformationIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUC_PROCESS_SERVER_INFORMATION pServerInfo; PHTTP_SERVER_CONTEXT_INFORMATION pInfo;
ASSERT_IOCTL_METHOD(IN_DIRECT, SET_SERVER_CONTEXT_INFORMATION);
do { if(!IS_SERVER(IrpSp->FileObject)) { Status = STATUS_INVALID_HANDLE; break; }
//
// Pull out the connection information from the Irp, make sure it is
// valid.
//
pServerInfo = (PUC_PROCESS_SERVER_INFORMATION) IrpSp->FileObject->FsContext;
ASSERT( IS_VALID_SERVER_INFORMATION(pServerInfo) );
//
// See if the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*pInfo)) { Status = STATUS_BUFFER_TOO_SMALL; break; }
pInfo = (PHTTP_SERVER_CONTEXT_INFORMATION) pIrp->AssociatedIrp.SystemBuffer;
Status = UcSetServerContextInformation( pServerInfo, pInfo->ConfigID, pInfo->pInputBuffer, pInfo->InputBufferLength, pIrp );
} while(FALSE);
ASSERT(STATUS_PENDING != Status);
pIrp->IoStatus.Status = Status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
return Status; } // UcSetServerContextInformationIoctl
/***************************************************************************++
Routine Description:
This routine queries per server configuration information
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcQueryServerContextInformationIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PUC_PROCESS_SERVER_INFORMATION pServerInfo; PHTTP_SERVER_CONTEXT_INFORMATION pInfo = NULL; PVOID pAppBase, pMdlBuffer; ULONG Length = 0;
ASSERT_IOCTL_METHOD(OUT_DIRECT, QUERY_SERVER_CONTEXT_INFORMATION);
do { if(!IS_SERVER(IrpSp->FileObject)) { Status = STATUS_INVALID_HANDLE; break; }
//
// Pull out the connection information from the Irp, make sure it is
// valid.
//
pServerInfo = (PUC_PROCESS_SERVER_INFORMATION) IrpSp->FileObject->FsContext;
ASSERT( IS_VALID_SERVER_INFORMATION(pServerInfo) );
//
// See if the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*pInfo)) { Status = STATUS_BUFFER_TOO_SMALL; break; }
//
// Ensure that the output buffer looks good.
//
if(!pIrp->MdlAddress) { Status = STATUS_INVALID_PARAMETER; break; }
pInfo = (PHTTP_SERVER_CONTEXT_INFORMATION) pIrp->AssociatedIrp.SystemBuffer;
pMdlBuffer = MmGetSystemAddressForMdlSafe( pIrp->MdlAddress, LowPagePriority );
if (pMdlBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; }
//
// Make sure that the output buffer is ULONG aligned.
//
if(pMdlBuffer != ALIGN_UP_POINTER(pMdlBuffer, ULONG)) { Status = STATUS_DATATYPE_MISALIGNMENT_ERROR; break; }
pAppBase = (PSTR) MmGetMdlVirtualAddress(pIrp->MdlAddress);
Length = 0;
Status = UcQueryServerContextInformation( pServerInfo, pInfo->ConfigID, pMdlBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, &Length, pAppBase );
} while(FALSE);
ASSERT(STATUS_PENDING != Status);
if(Status != STATUS_SUCCESS) { //
// If we are not completing the IRP with STATUS_SUCCESS, the IO
// manager eats the pIrp->IoStatus.Information. But, the user wants
// to see this information (e.g. when we complete the IRP with
// STATUS_BUFFER_OVERFLOW, we want to tell the app how much to write.
//
// So, we convey this information using the app's pointer. Note that
// this can be done only if we are completing the IRP synchronously.
//
__try { // This is METHOD_OUT_DIRECT, so the input buffer comes from
// the IO manager. So, we don't have to worry about the app
// changing pInfo->pBytesTaken after we've probed it.
//
// We still have to probe & access it in a try except block,
// since this is a user mode pointer.
if(pInfo && pInfo->pBytesTaken) { UlProbeForWrite( pInfo->pBytesTaken, sizeof(ULONG), sizeof(ULONG), pIrp->RequestorMode );
*pInfo->pBytesTaken = Length; } } __except( UL_EXCEPTION_FILTER()) { } }
pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = (ULONG_PTR) Length;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
return Status; } // UcQueryServerContextInformationIoctl
/***************************************************************************++
Routine Description:
This routine sends a HTTP request
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcSendRequestIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION IrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_SEND_REQUEST_INPUT_INFO pHttpSendRequest = NULL; PUC_HTTP_REQUEST pHttpInternalRequest = 0; PUC_PROCESS_SERVER_INFORMATION pServerInfo; ULONG BytesTaken = 0;
ASSERT_IOCTL_METHOD(OUT_DIRECT, SEND_REQUEST);
do { if(!IS_SERVER(IrpSp->FileObject)) { Status = STATUS_INVALID_HANDLE;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
//
// Pull out the connection information from the Irp, make sure it is
// valid.
//
// IrpSp->FileObject->FsContext;
//
pServerInfo = (PUC_PROCESS_SERVER_INFORMATION) IrpSp->FileObject->FsContext;
ASSERT( IS_VALID_SERVER_INFORMATION(pServerInfo) );
//
// See if the input buffer is large enough.
//
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*pHttpSendRequest)) { Status = STATUS_BUFFER_TOO_SMALL; UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
pHttpSendRequest = (PHTTP_SEND_REQUEST_INPUT_INFO) pIrp->AssociatedIrp.SystemBuffer;
if(NULL == pHttpSendRequest->pHttpRequest) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
//
// Make sure that the SEND_REQUEST_FLAGS are valid.
//
if(pHttpSendRequest->HttpRequestFlags & (~HTTP_SEND_REQUEST_FLAG_VALID)) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
BytesTaken = 0;
Status = UcCaptureHttpRequest(pServerInfo, pHttpSendRequest, pIrp, IrpSp, &pHttpInternalRequest, &BytesTaken );
if(!NT_SUCCESS(Status)) { UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
//
// Save the captured request in the IRP.
//
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pHttpInternalRequest;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_NEW, NULL, pHttpInternalRequest, pIrp, UlongToPtr(Status) );
//
// We have to pin the request down to a connection regardless of
// whether we are going to send it or not. We need to do this to
// maintain the order in which the user passes requests to the driver.
//
Status = UcSendRequest(pServerInfo, pHttpInternalRequest);
} while(FALSE);
if (Status == STATUS_SUCCESS) { ASSERT(pHttpInternalRequest);
pIrp->IoStatus.Status = Status;
// For the IRP.
UC_DEREFERENCE_REQUEST(pHttpInternalRequest);
UlCompleteRequest(pIrp, IO_NO_INCREMENT); } else if (Status != STATUS_PENDING) { if(pHttpInternalRequest) { // For the IRP.
UC_DEREFERENCE_REQUEST(pHttpInternalRequest);
UcFreeSendMdls(pHttpInternalRequest->pMdlHead);
//
// We don't need the Request ID
//
if(!HTTP_IS_NULL_ID(&pHttpInternalRequest->RequestId)) { UlFreeOpaqueId( pHttpInternalRequest->RequestId, UlOpaqueIdTypeHttpRequest );
HTTP_SET_NULL_ID(&pHttpInternalRequest->RequestId);
UC_DEREFERENCE_REQUEST(pHttpInternalRequest); }
UC_DEREFERENCE_REQUEST(pHttpInternalRequest); }
pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = (ULONG_PTR) BytesTaken;
//
// If we are not completing the IRP with STATUS_SUCCESS, the IO
// manager eats the pIrp->IoStatus.Information. But, the user wants
// to see this information (e.g. when we complete the IRP with
// STATUS_BUFFER_OVERFLOW, we want to tell the app how much to write.
//
// So, we convey this information using the app's pointer. Note that
// this can be done only if we are completing the IRP synchronously.
//
__try { // This is METHOD_OUT_DIRECT, so the input buffer comes from
// the IO manager. So, we don't have to worry about the app
// changing pHttpSendRequest->pBytesTaken after we've probed it.
//
// We still have to probe & access it in a try except block,
// since this is a user mode pointer.
if(pHttpSendRequest && pHttpSendRequest->pBytesTaken) { UlProbeForWrite( pHttpSendRequest->pBytesTaken, sizeof(ULONG), sizeof(ULONG), pIrp->RequestorMode );
*pHttpSendRequest->pBytesTaken = BytesTaken; } } __except( UL_EXCEPTION_FILTER()) { }
UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
return Status; } // UcSendRequestIoctl
/***************************************************************************++
Routine Description:
This routine cancels a HTTP request
Arguments:
pIrp - Supplies a pointer to the IO request packet.
pIrpSp - Supplies a pointer to the IO stack location to use for this request.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/ NTSTATUS UcCancelRequestIoctl( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) { NTSTATUS Status = STATUS_SUCCESS; PHTTP_RECEIVE_RESPONSE_INFO pInfo; PUC_HTTP_REQUEST pRequest = NULL; PUC_PROCESS_SERVER_INFORMATION pServerInfo; KIRQL OldIrql;
ASSERT_IOCTL_METHOD(BUFFERED, CANCEL_REQUEST);
do { if(!IS_SERVER(pIrpSp->FileObject)) { Status = STATUS_INVALID_HANDLE;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_CANCELLED, NULL, NULL, pIrp, UlongToPtr(Status) );
break; }
//
// Pull out the connection information from the Irp, make sure it is
// valid.
//
// IrpSp->FileObject->FsContext;
//
pServerInfo = (PUC_PROCESS_SERVER_INFORMATION) pIrpSp->FileObject->FsContext;
ASSERT( IS_VALID_SERVER_INFORMATION(pServerInfo) );
//
// Grab the input buffer
//
pInfo = (PHTTP_RECEIVE_RESPONSE_INFO) pIrp->AssociatedIrp.SystemBuffer;
//
// See if the input buffer is large enough.
//
if( (pIrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(*pInfo)) ) { Status = STATUS_BUFFER_TOO_SMALL;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_CANCELLED, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
if(HTTP_IS_NULL_ID(&pInfo->RequestID) || pInfo->Flags != 0) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_CANCELLED, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
pRequest = (PUC_HTTP_REQUEST) UlGetObjectFromOpaqueId( pInfo->RequestID, UlOpaqueIdTypeHttpRequest, UcReferenceRequest );
if (UC_IS_VALID_HTTP_REQUEST(pRequest) == FALSE) { Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_REQUEST_CANCELLED, NULL, NULL, NULL, UlongToPtr(Status) );
break; }
if(pRequest->pFileObject != pIrpSp->FileObject) { //
// Cant allow the app to use someone else's RequestID
//
Status = STATUS_INVALID_PARAMETER;
UC_WRITE_TRACE_LOG( g_pUcTraceLog, UC_ACTION_ENTITY_NEW, pRequest, NULL, NULL, UlongToPtr(Status) );
break; }
UlAcquireSpinLock(&pRequest->pConnection->SpinLock, &OldIrql);
UcFailRequest(pRequest, STATUS_CANCELLED, OldIrql);
Status = STATUS_SUCCESS;
} while(FALSE);
if (Status == STATUS_SUCCESS) { UC_DEREFERENCE_REQUEST(pRequest);
pIrp->IoStatus.Status = Status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT); } else { ASSERT(Status != STATUS_PENDING);
if(pRequest) { UC_DEREFERENCE_REQUEST(pRequest); }
pIrp->IoStatus.Status = Status;
UlCompleteRequest(pIrp, IO_NO_INCREMENT); }
return Status; } // UcCancelRequestIoctl
|