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.
 
 
 
 
 
 

4890 lines
125 KiB

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