/*++

Copyright (c) 1998-2001 Microsoft Corporation

Module Name:

    ioctl.cxx

Abstract:

    This module implements various IOCTL handlers.

Author:

    Keith Moore (keithmo)       10-Jun-1998

Revision History:

    Paul McDaniel (paulmcd)     15-Mar-1999     Modified SendResponse

--*/


#include "precomp.h"


#define VALIDATE_OFFSET( off, input )   ((off) < (input))
#define VALIDATE_LENGTH( len, input )   ((len) < (input))

#define VALIDATE_OFFSET_LENGTH_PAIR( off, len, input )                      \
    ( VALIDATE_OFFSET( off, input ) &&                                      \
      VALIDATE_LENGTH( len, input ) &&                                      \
      (((off) + (len)) < (input)) )


VOID
UlpRestartSendHttpResponse(
    IN PVOID pCompletionContext,
    IN NTSTATUS Status,
    IN ULONG_PTR Information
    );


#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, UlConfigGroupControlIoctl )
#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, UlAddTransientUrlIoctl )
#pragma alloc_text( PAGE, UlRemoveTransientUrlIoctl )
#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 )
#endif  // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpRestartSendHttpResponse
#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;
    PHTTP_CONTROL_CHANNEL_INFO  pInfo = NULL;
    PUL_CONTROL_CHANNEL         pControlChannel;
    PVOID                       pMdlBuffer;
    ULONG                       length;
    PVOID pMdlVa;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // better be a control channel
    //

    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not a control channel.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    pControlChannel = GET_CONTROL_CHANNEL(pIrpSp->FileObject);
    if (IS_VALID_CONTROL_CHANNEL(pControlChannel) == FALSE)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    //
    // Ensure the input buffer looks good
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONTROL_CHANNEL_INFO))
    {
        //
        // input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // fetch it
    //

    pInfo = (PHTTP_CONTROL_CHANNEL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    __try
    {
        switch (pInfo->InformationClass)
        {
        case HttpControlChannelStateInformation:
        case HttpControlChannelBandwidthInformation:
        case HttpControlChannelConnectionInformation:
        case HttpControlChannelAutoResponseInformation:

            // if no outbut buffer pass down in the Irp
            // that means app is asking for the required
            // field length

            if (!pIrp->MdlAddress)
            {
                pMdlBuffer = NULL;
            }
            else
            {
                pMdlBuffer = MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            LowPagePriority
                            );

                if (pMdlBuffer == NULL)
                {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    __leave;
                }

                // Also make sure that user buffer was good

                pMdlVa = MmGetMdlVirtualAddress(pIrp->MdlAddress);
                ProbeForWrite( pMdlVa,
                           pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
                           sizeof(UCHAR) );
            }

            Status = UlGetControlChannelInformation(
                            pControlChannel,
                            pInfo->InformationClass,
                            pMdlBuffer,
                            pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
                            &length
                            );

            if (!NT_SUCCESS(Status))
            {
                __leave;
            }

            pIrp->IoStatus.Information = (ULONG_PTR)length;
            break;

        default:
            Status = STATUS_INVALID_PARAMETER;
            __leave;
            break;
        }
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

end:
    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    PHTTP_CONTROL_CHANNEL_INFO  pInfo;
    PUL_CONTROL_CHANNEL         pControlChannel;
    PVOID                       pMdlBuffer;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // better be a control channel
    //

    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not a control channel.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    pControlChannel = GET_CONTROL_CHANNEL(pIrpSp->FileObject);
    if (IS_VALID_CONTROL_CHANNEL(pControlChannel) == FALSE)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    //
    // Ensure the input buffer looks good
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONTROL_CHANNEL_INFO))
    {
        //
        // input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    //
    // Ensure the output buffer looks good
    //

    if (!pIrp->MdlAddress)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // fetch it
    //

    pInfo = (PHTTP_CONTROL_CHANNEL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    switch (pInfo->InformationClass)
    {

    case HttpControlChannelStateInformation:
    case HttpControlChannelBandwidthInformation:
    case HttpControlChannelConnectionInformation:
    case HttpControlChannelAutoResponseInformation:
    case HttpControlChannelFilterInformation:
    case HttpControlChannelTimeoutInformation:
    case HttpControlChannelUTF8Logging:

        //
        // call the function
        //

        pMdlBuffer = MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            LowPagePriority
                            );

        if (pMdlBuffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
        else
        {
            Status = UlSetControlChannelInformation(
                            pControlChannel,
                            pInfo->InformationClass,
                            pMdlBuffer,
                            pIrpSp->Parameters.DeviceIoControl.OutputBufferLength
                            );
        }

        if (NT_SUCCESS(Status) == FALSE)
            goto end;

        break;


    default:
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

end:

    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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 = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be a control channel
    //
    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        // Not a control channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the output buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
        sizeof(HTTP_CONFIG_GROUP_INFO))
    {
        // output buffer too small.
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the output buffer
    //
    pInfo = (PHTTP_CONFIG_GROUP_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // it's pure output, wipe it to be sure
    //
    RtlZeroMemory(pInfo, sizeof(HTTP_CONFIG_GROUP_INFO));

    // Call the internal worker func
    //
    Status = UlCreateConfigGroup(
                    GET_CONTROL_CHANNEL(pIrpSp->FileObject),
                    &pInfo->ConfigGroupId
                    );
    if (NT_SUCCESS(Status) == FALSE)
        goto end;


end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;
        //  how much output should we return?
        //
        pIrp->IoStatus.Information = sizeof(HTTP_CONFIG_GROUP_INFO);

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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 = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be a control channel
    //
    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        // Not a control channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONFIG_GROUP_INFO))
    {
        // output buffer too small.
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // fetch out the input buffer
    //
    pInfo = (PHTTP_CONFIG_GROUP_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);


    // Call the internal worker func
    //
    __try
    {
        HTTP_CONFIG_GROUP_ID ConfigGroupId = pInfo->ConfigGroupId;

        Status = UlDeleteConfigGroup(ConfigGroupId);
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    PHTTP_CONFIG_GROUP_INFO     pInfo = NULL;
    PVOID                       pMdlBuffer;
    ULONG length = 0L;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // Going to access the url string from user mode memory
    //

    __try
    {

        //
        // better be a control channel
        //

        if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
        {
            //
            // Not a control channel.
            //

            Status = STATUS_INVALID_DEVICE_REQUEST;
            __leave;
        }

        //
        // Ensure the input buffer looks good
        //

        if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(HTTP_CONFIG_GROUP_INFO))
        {
            //
            // input buffer too small.
            //

            Status = STATUS_BUFFER_TOO_SMALL;
            __leave;
        }

        //
        // fetch it
        //

        pInfo = (PHTTP_CONFIG_GROUP_INFO)pIrp->AssociatedIrp.SystemBuffer;
        ASSERT(pInfo != NULL);

        switch (pInfo->InformationClass)
        {
#if DBG
        case HttpConfigGroupGetUrlInfo:
        {
            PHTTP_CONFIG_GROUP_DBG_URL_INFO pUrlInfo;
            PUL_URL_CONFIG_GROUP_INFO     pUrlCGInfo;

            // check the output buffer
            //
            if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
                sizeof(HTTP_CONFIG_GROUP_DBG_URL_INFO))
            {
                // output buffer too small.
                Status = STATUS_BUFFER_TOO_SMALL;
                __leave;
            }

            // grab the buffer
            //
            pUrlInfo = (PHTTP_CONFIG_GROUP_DBG_URL_INFO)
                MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            NormalPagePriority
                            );

            if (pUrlInfo == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                __leave;
            }

            pUrlCGInfo = (PUL_URL_CONFIG_GROUP_INFO)UL_ALLOCATE_POOL(
                                NonPagedPool,
                                sizeof(UL_URL_CONFIG_GROUP_INFO),
                                UL_CG_URL_INFO_POOL_TAG
                                );

            if (pUrlCGInfo == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                __leave;
            }

            // grab the url
            //
            if (pUrlInfo->Url.Length == 0 ||
                pUrlInfo->Url.MaximumLength > pUrlInfo->Url.Length ||
                pUrlInfo->Url.Buffer == NULL)

            {
                // output buffer too small.
                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }

            // good memory?
            //
            ProbeTestForRead(pUrlInfo->Url.Buffer,
                             pUrlInfo->Url.Length + sizeof(WCHAR),
                             sizeof(WCHAR));

            // must be null terminated
            //
            if (pUrlInfo->Url.Buffer[pUrlInfo->Url.Length/sizeof(WCHAR)] != UNICODE_NULL)
            {
                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }

            // CODEWORK: validate the incoming url.
            //

            // call the function
            //
            Status = UlGetConfigGroupInfoForUrl(
                            pUrlInfo->Url.Buffer,
                            NULL,   // PUL_HTTP_CONNECTION
                            pUrlCGInfo);
            if (NT_SUCCESS(Status) == FALSE)
                __leave;

            // copy it over
            //

            //pUrlInfo->Url;

            pUrlInfo->MaxBandwidth      = -1;
            if (pUrlCGInfo->pMaxBandwidth != NULL)
            {
                pUrlInfo->MaxBandwidth =
                    pUrlCGInfo->pMaxBandwidth->MaxBandwidth.MaxBandwidth;
            }

            pUrlInfo->MaxConnections    = -1;
            if (pUrlCGInfo->pMaxConnections != NULL)
            {
                pUrlInfo->MaxConnections =
                    pUrlCGInfo->pMaxConnections->MaxConnections.MaxConnections;
            }

            pUrlInfo->CurrentState      = pUrlCGInfo->CurrentState;
            pUrlInfo->UrlContext        = pUrlCGInfo->UrlContext;
            pUrlInfo->pReserved         = pUrlCGInfo;

            // update the output length
            //
            pIrp->IoStatus.Information = sizeof(HTTP_CONFIG_GROUP_DBG_URL_INFO);

        }
            break;

        case HttpConfigGroupFreeUrlInfo:
        {
            PHTTP_CONFIG_GROUP_DBG_URL_INFO pUrlInfo;

            // check the output buffer
            //
            if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
                sizeof(HTTP_CONFIG_GROUP_DBG_URL_INFO))
            {
                // output buffer too small.
                Status = STATUS_BUFFER_TOO_SMALL;
                __leave;
            }

            // grab the buffer
            //
            pUrlInfo = (PHTTP_CONFIG_GROUP_DBG_URL_INFO)
                MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            NormalPagePriority
                            );

            if (pUrlInfo == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                __leave;
            }

            // call the function
            //
            UlpConfigGroupInfoRelease(
                            (PUL_URL_CONFIG_GROUP_INFO)(pUrlInfo->pReserved)
                            );

            UL_FREE_POOL(
                (PUL_URL_CONFIG_GROUP_INFO)(pUrlInfo->pReserved),
                UL_CG_URL_INFO_POOL_TAG
                );

            // update the output length
            //
            pIrp->IoStatus.Information = 0;

        }
            break;

        case HttpConfigGroupUrlStaleTest:
        {
            Status = STATUS_NOT_SUPPORTED;
        }
            break;

#endif // DBG

        case HttpConfigGroupBandwidthInformation:
        case HttpConfigGroupConnectionInformation:
        case HttpConfigGroupStateInformation:
        case HttpConfigGroupConnectionTimeoutInformation:

            //
            // call the function
            //

            pMdlBuffer = MmGetSystemAddressForMdlSafe(
                                pIrp->MdlAddress,
                                LowPagePriority
                                );

            if (pMdlBuffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
            else
            {
                Status = UlQueryConfigGroupInformation(
                                pInfo->ConfigGroupId,
                                pInfo->InformationClass,
                                pMdlBuffer,
                                pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
                                &length
                                );
            }

            if (!NT_SUCCESS(Status))
            {
                __leave;
            }

            pIrp->IoStatus.Information = (ULONG_PTR)length;
            break;


        case HttpConfigGroupAutoResponseInformation:
        case HttpConfigGroupAppPoolInformation:
        case HttpConfigGroupSecurityInformation:
        default:
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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 = NULL;
    PVOID                       pMdlBuffer;
    PVOID                       pMdlVa;
    ULONG                       OutputLength;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // better be a control channel
    //

    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not a control channel.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer looks good
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONFIG_GROUP_INFO))
    {
        //
        // input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // fetch it
    //

    pInfo = (PHTTP_CONFIG_GROUP_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    OutputLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;

    switch (pInfo->InformationClass)
    {

        case HttpConfigGroupLogInformation:
        {

          __try
          {
            // if no outbut buffer pass down in the Irp
            // that probably means WAS is asking us to
            // remove the logging for this config_group
            // we will handle this case later...CODEWORK

            if (!pIrp->MdlAddress)
            {
                pMdlBuffer = NULL;
            }
            else
            {
                pMdlBuffer = MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            LowPagePriority
                            );

                if (pMdlBuffer == NULL)
                {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    __leave;
                }

                // Also make sure that user buffer was good

                pMdlVa = MmGetMdlVirtualAddress(pIrp->MdlAddress);
                ProbeTestForRead( pMdlVa,
                                  OutputLength,
                                  sizeof(UCHAR) );
            }

            UlTrace(IOCTL,
                    ("UlSetConfigGroupIoctl: CGroupId=%I64x, "
                     "LogInformation, pMdlBuffer=%p, length=%d\n",
                     pInfo->ConfigGroupId,
                     pMdlBuffer,
                     OutputLength
                     ));
            
            Status = UlSetConfigGroupInformation(
                            pInfo->ConfigGroupId,
                            pInfo->InformationClass,
                            pMdlBuffer,
                            OutputLength
                            );          
        }    
        __except( UL_EXCEPTION_FILTER() )
        {
            Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
        }

        if (NT_SUCCESS(Status) == FALSE)
            goto end;
    }
    break;

    case HttpConfigGroupAutoResponseInformation:
    case HttpConfigGroupAppPoolInformation:
    case HttpConfigGroupBandwidthInformation:
    case HttpConfigGroupConnectionInformation:
    case HttpConfigGroupStateInformation:
    case HttpConfigGroupSecurityInformation:
    case HttpConfigGroupSiteInformation:
    case HttpConfigGroupConnectionTimeoutInformation:

        //
        // call the function
        //

        pMdlBuffer = MmGetSystemAddressForMdlSafe(
                            pIrp->MdlAddress,
                            LowPagePriority
                            );

        if (pMdlBuffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
        else
        {
            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
                            );
        }

        if (NT_SUCCESS(Status) == FALSE)
            goto end;

        break;


    default:
        Status = STATUS_INVALID_PARAMETER;
        goto end;
        break;
    }

end:
    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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 = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be a control channel
    //
    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        // Not a control channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONFIG_GROUP_URL_INFO))
    {
        // bad buffer
        //
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the input buffer
    //
    pInfo = (PHTTP_CONFIG_GROUP_URL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // does it look appropriate?
    //
    if (pInfo->FullyQualifiedUrl.MaximumLength < pInfo->FullyQualifiedUrl.Length ||
        pInfo->FullyQualifiedUrl.Length == 0 ||
        (pInfo->FullyQualifiedUrl.Length % sizeof(WCHAR)) != 0 ||
        pInfo->FullyQualifiedUrl.Buffer == NULL)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // Going to access the url string from user mode memory
    //
    __try
    {
        // good memory?
        //
        ProbeTestForRead(pInfo->FullyQualifiedUrl.Buffer,
                         pInfo->FullyQualifiedUrl.Length + sizeof(WCHAR),
                         sizeof(WCHAR));

        // must be null terminated
        //
        if (pInfo->FullyQualifiedUrl.Buffer[pInfo->FullyQualifiedUrl.Length/sizeof(WCHAR)] != UNICODE_NULL)
        {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // CODEWORK: validate the incoming url.
        //

        // Call the internal worker func
        //
        Status = UlAddUrlToConfigGroup(pInfo->ConfigGroupId, &pInfo->FullyQualifiedUrl, pInfo->UrlContext);
        if (NT_SUCCESS(Status) == FALSE)
            __leave;

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );

}   // UlAddUrlToConfigGroupIoctl

/***************************************************************************++

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 = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be a control channel
    //
    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        // Not a control channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_CONFIG_GROUP_URL_INFO))
    {
        // bad buffer
        //
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the input buffer
    //
    pInfo = (PHTTP_CONFIG_GROUP_URL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // does it look appropriate?
    //
    if (pInfo->FullyQualifiedUrl.MaximumLength < pInfo->FullyQualifiedUrl.Length ||
        pInfo->FullyQualifiedUrl.Length == 0 ||
        (pInfo->FullyQualifiedUrl.Length % sizeof(WCHAR)) != 0 ||
        pInfo->FullyQualifiedUrl.Buffer == NULL)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // Going to access the url string from user mode memory
    //
    __try
    {
        // good memory?
        //
        ProbeTestForRead(pInfo->FullyQualifiedUrl.Buffer,
                         pInfo->FullyQualifiedUrl.Length + sizeof(WCHAR),
                         sizeof(WCHAR));

        // must be null terminated
        //
        if (pInfo->FullyQualifiedUrl.Buffer[pInfo->FullyQualifiedUrl.Length/sizeof(WCHAR)] != UNICODE_NULL)
        {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // CODEWORK: validate the incoming url.
        //

        // Call the internal worker func
        //
        Status = UlRemoveUrlFromConfigGroup(pInfo->ConfigGroupId, &pInfo->FullyQualifiedUrl);
        if (NT_SUCCESS(Status) == FALSE)
            __leave;

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );

}   // UlRemoveUrlFromConfigGroupIoctl

/***************************************************************************++

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 = NULL;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Ensure it's a control channel.
    //

    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*pInfo))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Fetch out the input buffer.
    //

    pInfo = (PHTTP_REMOVE_ALL_URLS_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    //
    // Call the internal worker function.
    //

    status = UlRemoveAllUrlsFromConfigGroup( pInfo->ConfigGroupId );
    goto end;

end:
    //
    // Complete the request.
    //

    if (status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = status;
        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( status );

}   // UlRemoveAllUrlsFromConfigGroupIoctl


/***************************************************************************++

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;
    PHTTP_APP_POOL_INFO pInfo = NULL;
    PVOID               pMdlBuffer;
    ULONG  length;
    PVOID  pMdlVa;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // better be an application pool
    //

    if (!IS_APP_POOL(pIrpSp->FileObject))
    {
        //
        // Not an application pool.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer looks good
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_APP_POOL_INFO))
    {
        //
        // input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    pInfo = (PHTTP_APP_POOL_INFO) pIrp->AssociatedIrp.SystemBuffer;

    ASSERT(pInfo != NULL);

    __try
    {
        switch (pInfo->InformationClass)
        {
        case HttpAppPoolDemandStartInformation:
        case HttpAppPoolDemandStartFlagInformation:
        case HttpAppPoolQueueLengthInformation:
        case HttpAppPoolStateInformation:

            // if no outbut buffer passed down in the Irp
            // that means app is asking for the required 
            // field length

            if (!pIrp->MdlAddress)
            {
                pMdlBuffer = NULL;
            }
            else
            {
                pMdlBuffer = MmGetSystemAddressForMdlSafe(
                                    pIrp->MdlAddress,
                                    LowPagePriority
                                    );

                if (pMdlBuffer == NULL)
                {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    __leave;
                }

                // Probe the user memory to make sure that it's good.
                
                pMdlVa = MmGetMdlVirtualAddress(pIrp->MdlAddress);

                ProbeForWrite( pMdlVa,
                        pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, 
                        sizeof(UCHAR) );
            }

            Status = UlQueryAppPoolInformation(
                            GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                            pInfo->InformationClass,
                            pMdlBuffer,
                            pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
                            &length
                            );

            if (!NT_SUCCESS(Status))
            {
                __leave;
            }

            pIrp->IoStatus.Information = (ULONG_PTR)length;
            break;

        default:
            Status = STATUS_INVALID_PARAMETER;
            __leave;
            break;
        }
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

end:
    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    PHTTP_APP_POOL_INFO pInfo;
    PVOID               pMdlBuffer;

    //
    // Sanity check
    //
    PAGED_CODE();

    //
    // better be an application pool
    //

    if (!IS_APP_POOL(pIrpSp->FileObject))
    {
        //
        // Not an application pool.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer looks good
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_APP_POOL_INFO))
    {
        //
        // input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Check that the input buffer is valid
    //

    if (NULL == pIrp->MdlAddress)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }
    
    __try
    {
        pInfo = (PHTTP_APP_POOL_INFO) pIrp->AssociatedIrp.SystemBuffer;

        switch (pInfo->InformationClass)
        {
        case HttpAppPoolDemandStartInformation:
        case HttpAppPoolDemandStartFlagInformation:
        case HttpAppPoolQueueLengthInformation:
        case HttpAppPoolStateInformation:

            //
            // call the function
            //

            pMdlBuffer = MmGetSystemAddressForMdlSafe(
                                pIrp->MdlAddress,
                                LowPagePriority
                                );

            if (pMdlBuffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
            else
            {
                Status = UlSetAppPoolInformation(
                                GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                                pInfo->InformationClass,
                                pMdlBuffer,
                                pIrpSp->Parameters.DeviceIoControl.OutputBufferLength
                                );
            }

            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
        goto end;
    }

end:
    //
    // complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );
}   // UlSetAppPoolInformationIoctl


/***************************************************************************++

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 = NULL;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Ensure this is really an app pool, not a control channel.
    //

    if (IS_APP_POOL(pIrpSp->FileObject))
    {
        //
        // Grab the input buffer
        //

        pInfo = (PHTTP_RECEIVE_REQUEST_INFO) pIrp->AssociatedIrp.SystemBuffer;

        if (NULL == pInfo)
        {
            Status = STATUS_INVALID_PARAMETER;
            goto end;
        }

        //
        // 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)))
        {
            Status = UlReceiveHttpRequest(
                            pInfo->RequestId,
                            pInfo->Flags,
                            GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                            pIrp
                            );
        }
        else
        {
            Status = STATUS_BUFFER_TOO_SMALL;
            // Add some padding
            pIrp->IoStatus.Information  = 3 * sizeof(HTTP_REQUEST) / 2;
        }

        UlTrace(ROUTING, (
            "UlReceiveHttpRequestIoctl: BytesNeeded=%d, status=0x%x\n",
            pIrp->IoStatus.Information, Status
            ));
        
    }
    else
    {
        Status = STATUS_INVALID_DEVICE_REQUEST;
    }

end:
    //
    // Complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );

}   // UlReceiveHttpRequestIoctl

/***************************************************************************++

Routine Description:

    This routine receives entity body data from an HTTP request.

    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
UlReceiveEntityBodyIoctl(
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION pIrpSp
    )
{
    NTSTATUS Status;
    PHTTP_RECEIVE_REQUEST_INFO pInfo;
    PUL_INTERNAL_REQUEST pRequest = NULL;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Ensure this is really an app pool, not a control channel.
    //

    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(HTTP_RECEIVE_REQUEST_INFO))
    {
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Map the incoming connection ID to the corresponding
    // PUL_HTTP_CONNECTION object.
    //

    pInfo = (PHTTP_RECEIVE_REQUEST_INFO)(pIrp->AssociatedIrp.SystemBuffer);

    //
    // Now get the request from the request id.
    // This gets us a reference to the request.
    //

    pRequest = UlGetRequestFromId(pInfo->RequestId);
    if (UL_IS_VALID_INTERNAL_REQUEST(pRequest) == FALSE)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    //
    // OK, now call the function
    //

    Status = UlReceiveEntityBody(
                    GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                    pRequest,
                    pIrp
                    );

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

end:
    if (pRequest != NULL)
    {
        UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
        pRequest = NULL;
    }

    //
    // Complete the request.
    //

    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );

}   // UlReceiveEntityBodyIoctl

/***************************************************************************++

Routine Description:

    This routine sends an HTTP response.

    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
UlSendHttpResponseIoctl(
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION pIrpSp
    )
{
    NTSTATUS                        Status = STATUS_SUCCESS;
    PHTTP_SEND_HTTP_RESPONSE_INFO   pSendInfo = NULL;
    PUL_INTERNAL_RESPONSE           pResponse = NULL;
    PHTTP_RESPONSE                  pHttpResponse = NULL;
    ULONG                           Flags;
    PUL_INTERNAL_REQUEST            pRequest = NULL;
    BOOLEAN                         ServedFromCache = FALSE;
    BOOLEAN                         CaptureCache;
    BOOLEAN                         ConnectionClosed = FALSE;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Ensure this is really an app pool, not a control channel.
    //

    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(*pSendInfo))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    pSendInfo = (PHTTP_SEND_HTTP_RESPONSE_INFO)pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer;

    //
    // Probe pSendInfo since we use NEITHER_IO.
    //

    __try
    {
        ProbeTestForRead(
            pSendInfo,
            sizeof(*pSendInfo),
            sizeof(PVOID)
            );

        ProbeTestForRead(
            pSendInfo->pEntityChunks,
            sizeof(HTTP_DATA_CHUNK) * pSendInfo->EntityChunkCount,
            sizeof(PVOID)
            );

        UlTrace(SEND_RESPONSE, (
            "UL!UlSendHttpResponseIoctl - Flags = %X\n",
            pSendInfo->Flags
            ));

        //
        // UlSendHttpResponse() *must* take a PHTTP_RESPONSE. This will
        // protect us from those whackos that attempt to build their own
        // raw response headers.
        //

        pHttpResponse = pSendInfo->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(pSendInfo->RequestId);

        if (pRequest == NULL)
        {
            //
            // Couldn't map the HTTP_REQUEST_ID.
            //

            Status = STATUS_INVALID_PARAMETER;
            goto end;
        }

        ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
        ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));

        //
        // Make sure only one response header goes back. We can test this
        // without acquiring the request resource, since the flag is only set
        // (never reset).
        //

        if (1 == InterlockedCompareExchange(
                    (PLONG)&pRequest->SentResponse,
                    1,
                    0
                    ))
        {
            //
            // already sent a response.  bad.
            //

            Status = STATUS_INVALID_DEVICE_STATE;

            UlTrace(SEND_RESPONSE, (
                "ul!UlSendHttpResponseIoctl(pRequest = %p (%I64x)) %x\n"
                "        Tried to send a second response!\n",
                pRequest,
                pRequest->RequestId,
                Status
                ));

            goto end;
        }

        //
        // Also ensure that all previous calls to SendHttpResponse
        // and SendEntityBody had the MORE_DATA flag set.
        //

        if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
        {
            //
            // Remember if the more data flag is not set.
            //

            if (1 == InterlockedCompareExchange(
                        (PLONG)&pRequest->SentLast,
                        1,
                        0
                        ))
            {
                Status = STATUS_INVALID_DEVICE_STATE;
                goto end;
            }
        }
        else
        if (pRequest->SentLast == 1)
        {
            Status = STATUS_INVALID_DEVICE_STATE;

            UlTrace(SEND_RESPONSE, (
                "ul!UlSendHttpResponseIoctl(pRequest = %p (%I64x)) %x\n"
                "        Tried to send again after last send!\n",
                pRequest,
                pRequest->RequestId,
                Status
                ));

            goto end;
        }

        //
        // OK, we have the connection. Now capture the incoming
        // HTTP_RESPONSE structure and map it to our internal
        // format.
        //

        if (pSendInfo->CachePolicy.Policy != HttpCachePolicyNocache)
        {
            CaptureCache = pRequest->CachePreconditions;
        }
        else
        {
            CaptureCache = FALSE;
        }

        //
        // Take the fast path if this is a single memory chunk that needs no
        // retransmission (<= 64k).
        //

        if (CaptureCache == FALSE
            && pSendInfo->EntityChunkCount == 1
            && pSendInfo->pEntityChunks->DataChunkType
                    == HttpDataChunkFromMemory
            && pSendInfo->pEntityChunks->FromMemory.BufferLength
                    <= MAX_BYTES_BUFFERED
            )
        {
            Status = UlpFastSendHttpResponse(
                            pSendInfo->pHttpResponse,
                            pSendInfo->pLogData,
                            pSendInfo->pEntityChunks,
                            1,
                            pSendInfo->Flags,
                            pRequest,
                            pIrp,
                            NULL
                            );

            goto end;
        }
        
        Status = UlCaptureHttpResponse(
                        pSendInfo->pHttpResponse,
                        pRequest,
                        pRequest->Version,
                        pRequest->Verb,
                        pSendInfo->EntityChunkCount,
                        pSendInfo->pEntityChunks,
                        UlCaptureNothing,
                        CaptureCache,
                        pSendInfo->pLogData,
                        &pResponse
                        );
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

    //
    // Save the captured response in the IRP so we can
    // dereference it after the IRP completes.
    //
    
    pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pResponse;
    
    //
    // Prepare the response (open files, etc).
    //

    Status = UlPrepareHttpResponse(
                    pRequest->Version,
                    pHttpResponse,
                    pResponse
                    );

    if (NT_SUCCESS(Status))
    {
        //
        // 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 );

        pIrp->IoStatus.Status = STATUS_PENDING;
        
        //
        // Try capture to cache and send
        //

        if (CaptureCache)
        {
            Status = UlCacheAndSendResponse(
                            pRequest,
                            pResponse,
                            GET_APP_POOL_PROCESS( pIrpSp->FileObject ),
                            pSendInfo->Flags,
                            pSendInfo->CachePolicy,
                            &UlpRestartSendHttpResponse,
                            pIrp,
                            &ServedFromCache
                            );

            if (NT_SUCCESS(Status) && !ServedFromCache)
            {
                //
                // Send the non-cached response
                //

                Status = UlSendHttpResponse(
                                pRequest,
                                pResponse,
                                pSendInfo->Flags,
                                &UlpRestartSendHttpResponse,
                                pIrp
                                );
            }
        }
        else
        {
            //
            // Non-cacheable request/response, send response directly.
            //

            Status = UlSendHttpResponse(
                            pRequest,
                            pResponse,
                            pSendInfo->Flags,
                            &UlpRestartSendHttpResponse,
                            pIrp
                            );
        }
    }
    else
    {
        //
        // BUGBUG: Do custom error thang here.
        //
        NTSTATUS CloseStatus;

        pIrp->IoStatus.Status = Status;

        CloseStatus = UlCloseConnection(
                            pRequest->pHttpConn->pConnection,
                            TRUE,
                            &UlpRestartSendHttpResponse,
                            pIrp
                            );

        ASSERT( CloseStatus == STATUS_PENDING );

        ConnectionClosed = TRUE;

        // UlCloseConnection always returns STATUS_PENDING
        // but we want to return the a proper error code here.
        // E.G. If the supplied file - as filename data chunk -
        // not found we will return STATUS_OBJECT_NAME_INVALID
        // as its returned by UlPrepareHttpResponse.
    }

    // paulmcd: is this the right time to deref?
    //
    // DEREFERENCE_HTTP_CONNECTION( pHttpConnection );

    if (Status != STATUS_PENDING  &&  !ConnectionClosed)
    {
        //
        // 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
            );

        Status = STATUS_PENDING;
    }

end:

    if (pRequest != NULL)
    {
        UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
        pRequest = NULL;
    }

    //
    // Complete the request.
    //

    if (Status != STATUS_PENDING  &&  !ConnectionClosed)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );

}   // UlSendHttpResponseIoctl


/***************************************************************************++

Routine Description:

    This routine sends an HTTP entity body.

    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
UlSendEntityBodyIoctl(
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION pIrpSp
    )
{
    NTSTATUS                        Status;
    PHTTP_SEND_HTTP_RESPONSE_INFO   pSendInfo;
    PUL_INTERNAL_RESPONSE           pResponse;
    ULONG                           Flags;
    PUL_INTERNAL_REQUEST            pRequest = NULL;
    BOOLEAN                         ConnectionClosed = FALSE;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Ensure this is really an app pool, not a control channel.
    //

    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(*pSendInfo))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    pSendInfo = (PHTTP_SEND_HTTP_RESPONSE_INFO)pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer;

    //
    // Probe pSendInfo since we use NEITHER_IO.
    //

    __try
    {
        ProbeTestForRead(
            pSendInfo,
            sizeof(*pSendInfo),
            sizeof(PVOID)
            );
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
        goto end;
    }


    UlTrace(SEND_RESPONSE, (
        "UL!UlSendEntityBodyIoctl - Flags = %X\n",
        pSendInfo->Flags
        ));

    //
    // Now get the request from the request id.
    // This gives us a reference to the request.
    //

    pRequest = UlGetRequestFromId(pSendInfo->RequestId);

    if (pRequest == NULL)
    {
        //
        // Couldn't map the HTTP_REQUEST_ID.
        //

        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
    ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));

    //
    // Ensure a response has already been sent. We can test this without
    // acquiring the request resource, since the flag is only set (never
    // reset).
    //

    if (pRequest->SentResponse == 0)
    {
        //
        // The application is sending entity without first having
        // send a response header. This is generally an error, however
        // we allow the application to override this by passing
        // the HTTP_SEND_RESPONSE_FLAG_RAW_HEADER flag.
        //

        if (pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_RAW_HEADER)
        {
            UlTrace(SEND_RESPONSE, (
                "ul!UlSendEntityBodyIoctl(pRequest = %p (%I64x))\n"
                "        Intentionally sending raw header!\n",
                pRequest,
                pRequest->RequestId
                ));

            if (1 == InterlockedCompareExchange(
                        (PLONG)&pRequest->SentResponse,
                        1,
                        0
                        ))
            {
                Status = STATUS_INVALID_DEVICE_STATE;
                goto end;
            }
        }
        else
        {
            Status = STATUS_INVALID_DEVICE_STATE;

            UlTrace(SEND_RESPONSE, (
                "ul!UlSendEntityBodyIoctl(pRequest = %p (%I64x)) %x\n"
                "        No response yet!\n",
                pRequest,
                pRequest->RequestId,
                Status
                ));

            goto end;
        }
    }

    //
    // Also ensure that all previous calls to SendHttpResponse
    // and SendEntityBody had the MORE_DATA flag set.
    //

    if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
    {
        //
        // Remember that this was the last send. We shouldn't
        // get any more data after this.
        //

        if (1 == InterlockedCompareExchange(
                    (PLONG)&pRequest->SentLast,
                    1,
                    0
                    ))
        {
            Status = STATUS_INVALID_DEVICE_STATE;
            goto end;
        }
    }
    else
    if (pRequest->SentLast == 1)
    {
        Status = STATUS_INVALID_DEVICE_STATE;

        UlTrace(SEND_RESPONSE, (
            "ul!UlSendEntityBodyIoctl(pRequest = %p (%I64x)) %x\n"
            "        Tried to send again after last send!\n",
            pRequest,
            pRequest->RequestId,
            Status
            ));

        goto end;
    }

    ASSERT(pSendInfo->pHttpResponse == NULL);

    __try
    {
        //
        // Take the fast path if this is a single memory chunk that needs no
        // retransmission (<= 64k).
        //

        if (pSendInfo->EntityChunkCount == 1
            && pSendInfo->pEntityChunks->DataChunkType
                    == HttpDataChunkFromMemory
            && pSendInfo->pEntityChunks->FromMemory.BufferLength
                    <= MAX_BYTES_BUFFERED
            )
        {
            Status = UlpFastSendHttpResponse(
                            NULL,
                            pSendInfo->pLogData,
                            pSendInfo->pEntityChunks,
                            1,
                            pSendInfo->Flags,
                            pRequest,
                            pIrp,
                            NULL
                            );
            
            goto end;
        }
        
        //
        // OK, we have the connection. Now capture the incoming
        // HTTP_RESPONSE structure and map it to our internal
        // format.
        //
        
        Status = UlCaptureHttpResponse(
                        NULL,
                        pRequest,
                        pRequest->Version,
                        pRequest->Verb,
                        pSendInfo->EntityChunkCount,
                        pSendInfo->pEntityChunks,
                        UlCaptureNothing,
                        FALSE,
                        pSendInfo->pLogData,
                        &pResponse
                        );
    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

    //
    // Save the captured response in the IRP so we can
    // dereference it after the IRP completes.
    //
    
    pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pResponse;
        
    //
    // Prepare the response (open files, etc).
    //

    Status = UlPrepareHttpResponse(
                    pRequest->Version,
                    NULL,
                    pResponse
                    );

    if (NT_SUCCESS(Status))
    {
        //
        // 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 );
        
        pIrp->IoStatus.Status = STATUS_PENDING;
        //
        // Send the response
        //

        Status = UlSendHttpResponse(
                        pRequest,
                        pResponse,
                        pSendInfo->Flags,
                        &UlpRestartSendHttpResponse,
                        pIrp
                        );
    }
    else
    {
        //
        // BUGBUG: Do custom error thang here.
        //

        NTSTATUS CloseStatus;

        pIrp->IoStatus.Status = Status;

        CloseStatus = UlCloseConnection(
                            pRequest->pHttpConn->pConnection,
                            TRUE,
                            &UlpRestartSendHttpResponse,
                            pIrp
                            );

        ASSERT( CloseStatus == STATUS_PENDING );

        ConnectionClosed = TRUE;
    }

    // paulmcd: is this the right time to deref?
    //
    // DEREFERENCE_HTTP_CONNECTION( pHttpConnection );

    if (Status != STATUS_PENDING  &&  !ConnectionClosed)
    {
        //
        // 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
            );

        Status = STATUS_PENDING;
    }

end:

    if (pRequest != NULL)
    {
        UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
        pRequest = NULL;
    }

    //
    // Complete the request.
    //

    if (Status != STATUS_PENDING  &&  !ConnectionClosed)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    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;
    PHTTP_FLUSH_RESPONSE_CACHE_INFO pInfo = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be an app pool
    //
    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        // 
        // Not an app pool.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_FLUSH_RESPONSE_CACHE_INFO))
    {
        // bad buffer
        //
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the input buffer
    //
    pInfo = (PHTTP_FLUSH_RESPONSE_CACHE_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // does it look appropriate?
    //
    if (pInfo->FullyQualifiedUrl.MaximumLength < pInfo->FullyQualifiedUrl.Length ||
        pInfo->FullyQualifiedUrl.Length == 0 ||
        (pInfo->FullyQualifiedUrl.Length % sizeof(WCHAR)) != 0 ||
        pInfo->FullyQualifiedUrl.Buffer == NULL)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // Going to access the url string from user mode memory
    //
    __try
    {
        // good memory?
        //
        ProbeTestForRead(pInfo->FullyQualifiedUrl.Buffer,
                         pInfo->FullyQualifiedUrl.Length + sizeof(WCHAR),
                         sizeof(WCHAR));

        // must be null terminated
        //
        if (pInfo->FullyQualifiedUrl.Buffer[pInfo->FullyQualifiedUrl.Length/sizeof(WCHAR)] != UNICODE_NULL)
        {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // check the flags
        //
        if (pInfo->Flags != (pInfo->Flags & HTTP_FLUSH_RESPONSE_FLAG_VALID)) {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // Call the internal worker func
        //

        UlFlushCacheByUri(
            pInfo->FullyQualifiedUrl.Buffer,
            pInfo->FullyQualifiedUrl.Length,
            pInfo->Flags,
            GET_APP_POOL_PROCESS( pIrpSp->FileObject )
            );

        Status = STATUS_SUCCESS;
        if (NT_SUCCESS(Status) == FALSE)
            __leave;

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }

    if (NT_SUCCESS(Status) == FALSE)
        goto end;

end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;

    //
    // sanity check.
    //

    PAGED_CODE();

    //
    // This had better be an app pool.
    //
    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // make the call
    //

    UlTrace(IOCTL,
            ("UlWaitForDemandStartIoctl: pAppPoolProcess=%p, pIrp=%p\n",
             GET_APP_POOL_PROCESS(pIrpSp->FileObject),
             pIrp
             ));
            
    Status = UlWaitForDemandStart(
                    GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                    pIrp
                    );

end:

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // This had better be an app pool.
    //

    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //

        status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Validate the input buffer.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(*pInfo))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    pInfo = (PHTTP_WAIT_FOR_DISCONNECT_INFO)pIrp->AssociatedIrp.SystemBuffer;

    //
    // Chase down the connection.
    //

    pHttpConn = UlGetConnectionFromId( pInfo->ConnectionId );

    if (!UL_IS_VALID_HTTP_CONNECTION(pHttpConn))
    {
        status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    //
    // Do it.
    //

    status = UlWaitForDisconnect(
                    GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                    pHttpConn,
                    pIrp
                    );

end:

    //
    // Complete the request?
    //

    if (status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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 ) );

    UL_DEREFERENCE_INTERNAL_RESPONSE( pResponse );

    //
    // Only overwrite the status field if it hasn't already been set
    // to an error
    //

    if (NT_SUCCESS(pIrp->IoStatus.Status))
        pIrp->IoStatus.Status = Status;

    pIrp->IoStatus.Information = Information;

    UlCompleteRequest( pIrp, g_UlPriorityBoost );

}   // UlpRestartSendHttpResponse


/***************************************************************************++

Routine Description:

    This routine adds a new transient URL prefix.

    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
UlAddTransientUrlIoctl(
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION pIrpSp
    )
{
    NTSTATUS                    Status;
    PHTTP_TRANSIENT_URL_INFO    pInfo = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be an application pool
    //
    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_TRANSIENT_URL_INFO))
    {
        // bad buffer
        //
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the input buffer
    //
    pInfo = (PHTTP_TRANSIENT_URL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // does it look appropriate?
    //
    if (pInfo->FullyQualifiedUrl.MaximumLength < pInfo->FullyQualifiedUrl.Length ||
        pInfo->FullyQualifiedUrl.Length == 0 ||
        (pInfo->FullyQualifiedUrl.Length % sizeof(WCHAR)) != 0 ||
        pInfo->FullyQualifiedUrl.Buffer == NULL)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // Going to access the url string from user mode memory
    //
    __try
    {
        // good memory?
        //
        ProbeTestForRead(pInfo->FullyQualifiedUrl.Buffer,
                         pInfo->FullyQualifiedUrl.Length + sizeof(WCHAR),
                         sizeof(WCHAR));

        // must be null terminated
        //
        if (pInfo->FullyQualifiedUrl.Buffer[pInfo->FullyQualifiedUrl.Length/sizeof(WCHAR)] != UNICODE_NULL)
        {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // CODEWORK: validate the incoming url.
        //

        // Call the internal worker func
        //
        Status = UlAddTransientUrl(
                        UlAppPoolObjectFromProcess(
                            GET_APP_POOL_PROCESS(pIrpSp->FileObject)
                            ),
                        &pInfo->FullyQualifiedUrl
                        );

        if (!NT_SUCCESS(Status))
            __leave;

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }


end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );
}   // UlAddTransientUrlIoctl


/***************************************************************************++

Routine Description:

    This routine removes a transient URL prefix.

    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
UlRemoveTransientUrlIoctl(
    IN PIRP pIrp,
    IN PIO_STACK_LOCATION pIrpSp
    )
{
    NTSTATUS                    Status;
    PHTTP_TRANSIENT_URL_INFO    pInfo = NULL;

    //
    // sanity check.
    //

    PAGED_CODE();

    // better be an application pool
    //
    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    // Ensure the input buffer is large enough.
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
        sizeof(HTTP_TRANSIENT_URL_INFO))
    {
        // bad buffer
        //
        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    // Fetch out the input buffer
    //
    pInfo = (PHTTP_TRANSIENT_URL_INFO)pIrp->AssociatedIrp.SystemBuffer;
    ASSERT(pInfo != NULL);

    // does it look appropriate?
    //
    if (pInfo->FullyQualifiedUrl.MaximumLength < pInfo->FullyQualifiedUrl.Length ||
        pInfo->FullyQualifiedUrl.Length == 0 ||
        (pInfo->FullyQualifiedUrl.Length % sizeof(WCHAR)) != 0 ||
        pInfo->FullyQualifiedUrl.Buffer == NULL)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    // Going to access the url string from user mode memory
    //
    __try
    {
        // good memory?
        //
        ProbeTestForRead(pInfo->FullyQualifiedUrl.Buffer,
                         pInfo->FullyQualifiedUrl.Length + sizeof(WCHAR),
                         sizeof(WCHAR));

        // must be null terminated
        //
        if (pInfo->FullyQualifiedUrl.Buffer[pInfo->FullyQualifiedUrl.Length/sizeof(WCHAR)] != UNICODE_NULL)
        {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        // CODEWORK: validate the incoming url.
        //

        // Call the internal worker func
        //
        Status = UlRemoveTransientUrl(
                        UlAppPoolObjectFromProcess(
                            GET_APP_POOL_PROCESS(pIrpSp->FileObject)
                            ),
                        &pInfo->FullyQualifiedUrl
                        );

        if (!NT_SUCCESS(Status))
            __leave;

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
    }


end:
    // complete the request.
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );
}   // UlRemoveTransientUrlIoctl

/***************************************************************************++

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;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // make the call
    //

    Status = UlFilterAccept(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pIrp
                    );

end:

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUX_FILTER_CONNECTION pConnection;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pConnection = NULL;

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(ConnectionId))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Map the incoming connection ID to the corresponding
    // UX_FILTER_CONNECTION object.
    //

    ConnectionId = *((PHTTP_RAW_CONNECTION_ID)pIrp->AssociatedIrp.SystemBuffer);

    pConnection = UlGetRawConnectionFromId(ConnectionId);

    if (!pConnection)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));

    //
    // make the call
    //

    Status = UlFilterClose(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pConnection,
                    pIrp
                    );

end:
    if (pConnection)
    {
        DEREFERENCE_FILTER_CONNECTION(pConnection);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUX_FILTER_CONNECTION pConnection;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pConnection = NULL;

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(ConnectionId))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Map the incoming connection ID to the corresponding
    // UX_FILTER_CONNECTION object.
    //

    ConnectionId = *((PHTTP_RAW_CONNECTION_ID)pIrp->AssociatedIrp.SystemBuffer);

    pConnection = UlGetRawConnectionFromId(ConnectionId);

    if (!pConnection)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));

    //
    // make the call
    //

    Status = UlFilterRawRead(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pConnection,
                    pIrp
                    );

end:
    if (pConnection)
    {
        DEREFERENCE_FILTER_CONNECTION(pConnection);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUX_FILTER_CONNECTION pConnection;
    BOOLEAN MarkedPending;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pConnection = NULL;
    MarkedPending = FALSE;

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(ConnectionId))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Ensure that there's an output buffer.
    //
    if (!pIrp->MdlAddress)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }

    //
    // Map the incoming connection ID to the corresponding
    // UX_FILTER_CONNECTION object.
    //

    ConnectionId = *((PHTTP_RAW_CONNECTION_ID)pIrp->AssociatedIrp.SystemBuffer);

    pConnection = UlGetRawConnectionFromId(ConnectionId);

    if (!pConnection)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));

    //
    // make the call
    //
    IoMarkIrpPending(pIrp);
    MarkedPending = TRUE;

    Status = UlFilterRawWrite(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pConnection,
                    pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
                    pIrp
                    );

end:
    if (pConnection)
    {
        DEREFERENCE_FILTER_CONNECTION(pConnection);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;
        UlCompleteRequest( pIrp, g_UlPriorityBoost );

        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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUX_FILTER_CONNECTION pConnection;
    PHTTP_FILTER_BUFFER pFiltBuffer;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pConnection = NULL;

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(HTTP_FILTER_BUFFER))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Ensure the output buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof(HTTP_FILTER_BUFFER))
    {
        //
        // Output buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Grab the filter buffer object.
    //

    pFiltBuffer = (PHTTP_FILTER_BUFFER) pIrp->AssociatedIrp.SystemBuffer;

    //
    // Map the incoming connection ID to the corresponding
    // UX_FILTER_CONNECTION object.
    //

    ConnectionId = pFiltBuffer->Reserved;

    pConnection = UlGetRawConnectionFromId(ConnectionId);

    if (!pConnection)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));

    //
    // make the call
    //

    Status = UlFilterAppRead(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pConnection,
                    pIrp
                    );

end:
    if (pConnection)
    {
        DEREFERENCE_FILTER_CONNECTION(pConnection);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUX_FILTER_CONNECTION pConnection;
    BOOLEAN MarkedPending;
    PHTTP_FILTER_BUFFER pFiltBuffer;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pConnection = NULL;
    MarkedPending = FALSE;

    //
    // This had better be a filter channel.
    //
    if (!IS_FILTER_PROCESS(pIrpSp->FileObject))
    {
        //
        // Not a filter channel.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(HTTP_FILTER_BUFFER))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Grab the filter buffer object.
    //

    pFiltBuffer = (PHTTP_FILTER_BUFFER) pIrp->AssociatedIrp.SystemBuffer;

    //
    // Map the incoming connection ID to the corresponding
    // UX_FILTER_CONNECTION object.
    //

    ConnectionId = pFiltBuffer->Reserved;

    pConnection = UlGetRawConnectionFromId(ConnectionId);

    if (!pConnection)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));

    //
    // make the call
    //
    IoMarkIrpPending(pIrp);
    MarkedPending = TRUE;

    Status = UlFilterAppWrite(
                    GET_FILTER_PROCESS(pIrpSp->FileObject),
                    pConnection,
                    pIrp
                    );

end:
    if (pConnection)
    {
        DEREFERENCE_FILTER_CONNECTION(pConnection);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;
        UlCompleteRequest( pIrp, g_UlPriorityBoost );

        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;
    HTTP_RAW_CONNECTION_ID ConnectionId;
    PUL_HTTP_CONNECTION pHttpConn;

    //
    // Sanity check.
    //
    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    pHttpConn = NULL;

    //
    // This had better be an app pool.
    //
    if (IS_APP_POOL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not an app pool.
        //
        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto end;
    }

    //
    // Ensure the input buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength <
            sizeof(HTTP_FILTER_RECEIVE_CLIENT_CERT_INFO))
    {
        //
        // Input buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Ensure the output buffer is large enough.
    //

    if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof(HTTP_SSL_CLIENT_CERT_INFO))
    {
        //
        // Output buffer too small.
        //

        Status = STATUS_BUFFER_TOO_SMALL;
        goto end;
    }

    //
    // Grab the cert receive object.
    //
    pReceiveCertInfo = (PHTTP_FILTER_RECEIVE_CLIENT_CERT_INFO)
                            pIrp->AssociatedIrp.SystemBuffer;

    //
    // Map the incoming connection ID to the corresponding
    // HTTP_CONNECTION object.
    //

    ConnectionId = pReceiveCertInfo->ConnectionId;

    pHttpConn = UlGetConnectionFromId(ConnectionId);

    if (!pHttpConn)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto end;
    }


    ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));

    //
    // make the call
    //

    Status = UlReceiveClientCert(
                    GET_APP_POOL_PROCESS(pIrpSp->FileObject),
                    &pHttpConn->pConnection->FilterInfo,
                    pReceiveCertInfo->Flags,
                    pIrp
                    );

end:
    if (pHttpConn)
    {
        UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
    }

    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( 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;
    PUL_CONTROL_CHANNEL     pControlChannel;
    ULONG                   Length;
    PVOID                   pMdlBuffer;
    PVOID                   pMdlVa;
    HTTP_COUNTER_GROUP      CounterGroup;

    //
    // Sanity check.
    //

    PAGED_CODE();

    //
    // Set up locals so we know how to clean up on exit.
    //
    Status = STATUS_SUCCESS;
    Length = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;

    //
    // better be a control channel
    //

    if (IS_CONTROL_CHANNEL(pIrpSp->FileObject) == FALSE)
    {
        //
        // Not a control channel.
        //

        Status = STATUS_INVALID_DEVICE_REQUEST;
        goto End;
    }

    pControlChannel = GET_CONTROL_CHANNEL(pIrpSp->FileObject);
    if (IS_VALID_CONTROL_CHANNEL(pControlChannel) == FALSE)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto End;
    }

    //
    // Find out which type of counters are requested
    //
    if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(HTTP_COUNTER_GROUP))
    {
        Status = STATUS_INVALID_PARAMETER;
        goto End;
    }

    CounterGroup = *((HTTP_COUNTER_GROUP *) pIrp->AssociatedIrp.SystemBuffer);

    // Crack IRP and get MDL contianing user's buffer
    // Crack MDL to get user's buffer

    __try
    {
        // if no outbut buffer pass down in the Irp
        // that means app is asking for the required
        // field length

        if (!pIrp->MdlAddress)
        {
            pMdlVa = NULL;
        }
        else
        {
            pMdlBuffer = MmGetSystemAddressForMdlSafe(
                pIrp->MdlAddress,
                LowPagePriority
                );

            if (pMdlBuffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                __leave;
            }

            // Also make sure that user buffer was good

            pMdlVa = MmGetMdlVirtualAddress(pIrp->MdlAddress);
            ProbeForWrite( pMdlVa,
                           Length,
                           sizeof(UCHAR) );
        }

        //
        // Call support function to gather apropriate counter blocks
        // and place in user's buffer.
        //

        if ( HttpCounterGroupGlobal == CounterGroup )
        {
            Status = UlGetGlobalCounters(
                        pMdlVa,
                        Length,
                        &Length
                        );
        }
        else if ( HttpCounterGroupSite == CounterGroup )
        {
            ULONG   Blocks;
            Status = UlGetSiteCounters(
                        pMdlVa,
                        Length,
                        &Length,
                        &Blocks
                        );
        }
        else
        {
            Status = STATUS_NOT_IMPLEMENTED;
            __leave;
        }

        if (!NT_SUCCESS(Status))
        {
            //
            // If not returning STATUS_SUCCESS,
            // IoStatus.Information *must* be 0.
            //
            pIrp->IoStatus.Information = 0;
            __leave;
        }
        else
        {
            pIrp->IoStatus.Information = (ULONG_PTR)Length;
        }

    }
    __except( UL_EXCEPTION_FILTER() )
    {
        Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
        pIrp->IoStatus.Information = 0;
    }

 End:
    //
    // complete the request?
    //
    if (Status != STATUS_PENDING)
    {
        pIrp->IoStatus.Status = Status;

        UlCompleteRequest( pIrp, g_UlPriorityBoost );
    }

    RETURN( Status );
} // UlGetCountersIoctl