How to add traces to IIS log from PHP

One of the new features added to FastCGI module in IIS 7.5 is ability to use IIS tracing infrastructure from FastCGI applications. This feature request was made by a customer who wanted to consolidate logging of the request processing in one log. All IIS events were present in IIS trace logs but traces generated by PHP applications were separate. So IIS trace log didn’t give a holistic view for PHP applications. ASP.Net already has a way to use IIS traces. So this limitation was specific to PHP. We closed this gap in PHP by providing a way for PHP developers to send traces to IIS using FastCGI’s STDERR stream which are then extracted by FastCGI module and sent to IIS tracing subsystem. This feature has been included in KB 980363 so that this can be made to work on IIS 7.0 as well. On IIS6 and below, these trace messages will be ignored as there is no tracing subsystem available.
 
FastCGI module parses STDERR stream looking for markers which identify the content as trace messages. Messages starting with “IIS_TRACE_ERROR:” are parsed as errors, “IIS_TRACE_WARNING:” as warnings and “IIS_TRACE_INFO” as info messages. Message end is recognized by marker “:IIS_TRACE_END”. Any function in PHP which can write to STDERR stream can be used to send these traces to FastCGI. So calling error_log(“IIS_TRACE_ERROR:This is an error.:IIS_TRACE_END”) will appear in IIS trace log as an error. Traces appear for CGI trace provider on IIS 7.0 and FastCGI trace provider on IIS 7.5. Trace messages are removed from STDERR stream before STDERR stream content goes through StderrMode logic so that traces don’t disturb existing STDERR behavior. Below is a simple helper class which you can use to generate trace messages.
 
<?php
 
class IIS_TRACE {
 
 const NONE    = 0;
 const ERROR   = 1;
 const WARNING = 2;
 const INFO    = 3;
 
 private static $verbosity = self::NONE;
 
 private static $traceErrorStart   = "IIS_TRACE_ERROR:";
 private static $traceWarningStart = "IIS_TRACE_WARNING:";
 private static $traceInfoStart    = "IIS_TRACE_INFO:";
 private static $traceEnd          = ":IIS_TRACE_END";
 
 public static function SetVerbosity ( $verbose )
 {
    if( $verbose != self::NONE    && $verbose != self::ERROR   &&
        $verbose != self::WARNING && $verbose != self::INFO )
    {
        throw new Exception( 'Function SetVerbosity called with invalid value' );
    }
 
    self::$verbosity = $verbose;
 }
 
 public static function WriteError ( $msg )
 {
    if (self::$verbosity >= self::ERROR )
        error_log( self::$traceErrorStart . $msg . self::$traceEnd );
 }
 
 public static function WriteWarning ( $msg )
 {
    if (self::$verbosity >= self::WARNING )
        error_log( self::$traceWarningStart . $msg . self::$traceEnd );
 }
 
 public static function WriteInfo ( $msg )
 {
    if (self::$verbosity >= self::INFO )
    error_log( self::$traceInfoStart . $msg .self::$traceEnd );
 }
}
 
?>
 
With this class present in your code, you can use WriteInfo, WriteWarning, WriteError functions available in IIS_TRACE class in your code to add traces to IIS trace log as below. When you have tracing enabled, these traces will appear with all other information about request processing.
 
<?php
 
require_once(‘iistrace.php’);
.
.
If($percent > 100)
{
    IIS_TRACE::WriteError(‘Invalid data provided. Percent = ’ . $percent);
}
.
.
 
?>
 
Hope this helps.
Kanwal

WaitForAllObjects to wait on more than MAXIMUM_WAIT_OBJECTS handles

One of the painful limitations of WaitForMultipleObjects function is that you can use it to wait for a maximum of MAXIMUM_WAIT_OBJECTS number of object handles. Value of MAXIMUM_WAIT_OBJECTS is 64. If you need to wait on more than MAXIMUM_WAIT_OBJECTS handles, you can either create a separate thread to wait on MAXIMUM_WAIT_OBJECTS and then do a wait on these threads to finish. Using this method you can create MAXIMUM_WAIT_OBJECTS threads each of those can wait for MAXIMUM_WAIT_OBJECTS object handles. Few months ago I needed code for doing this and couldn't find it on the internet. Here is how I implemented WaitForAllObjects.

typedef
struct _WAIT_THREAD_INFORMATION
{
    DWORD      dwWaitObjectCount;
    PHANDLE    pWaitHandles;
    DWORD      dwWaitMSeconds;
} WAIT_THREAD_INFORMATION, *PWAIT_THREAD_INFORMATION;

VOID
WaitForAllObjects(
    DWORD     dwWaitHandleCount,
    HANDLE *  pWaitHandles,
    DWORD     dwMaxWaitMSeconds
)
{
    HANDLE *                  pThreadWaitHandles    = NULL;
    WAIT_THREAD_INFORMATION * pWaitInformationArray = NULL;

    DWORD                     dwThreadCount         = 0;
    DWORD                     dwLastWaitCount       = 0;
    DWORD                     dwWaitHandleIndex     = 0;
    DWORD                     dwMaximumWaitObjects  = MAXIMUM_WAIT_OBJECTS;

    ASSERT( dwWaitHandleCount    >  0 );
    ASSERT( pWaitHandles         != NULL );
    ASSERT( dwMaxWaitMSeconds    >= 1000 );
    ASSERT( dwMaximumWaitObjects <= MAXIMUM_WAIT_OBJECTS );

    if( dwWaitHandleCount <= dwMaximumWaitObjects )
    {
        WaitForMultipleObjects( dwWaitHandleCount,
                                pWaitHandles,
                                TRUE,
                                dwMaxWaitMSeconds );
    }
    else
    {
        //
        // Create separate threads to wait on maximum wait objects
        // and then make this thread wait on thread handles
        //
        dwThreadCount   = dwWaitHandleCount / dwMaximumWaitObjects;
        dwLastWaitCount = dwWaitHandleCount % dwMaximumWaitObjects;

        if( dwLastWaitCount > 0 )
        {
            dwThreadCount++;
        }

        //
        // This function can handle a maximum of
        // MAXIMUM_WAIT_OBJECTS * MAXIMUM_WAIT_OBJECT handles
        //
        if( dwThreadCount > dwMaximumWaitObjects )
        {
            dwThreadCount     = dwMaximumWaitObjects;
            dwLastWaitCount   = 0;
        }

        pThreadWaitHandles    = new HANDLE[ dwThreadCount ];
        pWaitInformationArray = new WAIT_THREAD_INFORMATION[ dwThreadCount ];

        if( pThreadWaitHandles == NULL || pWaitInformationArray == NULL )
        {
            //
            // Failure
            //
            goto Finished;
        }

        for( DWORD count = 0; count < dwThreadCount; count++)
        {
            //
            // Set information for the thread
            //
            pWaitInformationArray[ count ].dwWaitMSeconds =
                dwMaxWaitMSeconds;
            pWaitInformationArray[ count ].pWaitHandles =
                &pWaitHandles[ dwWaitHandleIndex ];

            if( count != dwThreadCount - 1 || dwLastWaitCount == 0 )
            {
                pWaitInformationArray[ count ].dwWaitObjectCount =
                    dwMaximumWaitObjects;
                dwWaitHandleIndex += dwMaximumWaitObjects;
            }
            else
            {
                pWaitInformationArray[count].dwWaitObjectCount =
                    dwLastWaitCount;
                dwWaitHandleIndex += dwLastWaitCount;
            }

            pThreadWaitHandles[ count ] =
                CreateThread( NULL,
                              0,
                              WaitForMultipleObjectsThread,
                              &pWaitInformationArray[ count ],
                              0,
                              NULL );
            if( pThreadWaitHandles[ count ] == NULL )
            {
                //
                // Not able to create threads break from the loop
                // and wait for threads
we already created.
                // dwThreadCount doesnt include this iteration
                //
                dwThreadCount = count;
                break;
            }
        }

        //
        // Failure is ignored
        //
        WaitForMultipleObjects( dwThreadCount,
                                pThreadWaitHandles,
                                TRUE,
                                dwMaxWaitMSeconds );
    }

Finished:

    if( pThreadWaitHandles != NULL )
    {
        for( DWORD count = 0; count < dwThreadCount; count++)
        {
            if( pThreadWaitHandles[ count ] != NULL )
            {
                CloseHandle( pThreadWaitHandles[ count ] );
                pThreadWaitHandles[ count ] = NULL;
            }
        }

        delete [] pThreadWaitHandles;
        pThreadWaitHandles = NULL;
    }

    if( pWaitInformationArray != NULL )
    {
        delete [] pWaitInformationArray;
        pWaitInformationArray = NULL;
    }

    return;
}

//static
DWORD
WINAPI
WaitForMultipleObjectsThread(
    LPVOID lpParameter = NULL
)
//
// Thread routine which calls WaitForMultipleObjects
// This is used to wait for more than MAXIMUM_WAIT_OBJECTS objects
//
{
    DWORD                    dwReturnValue      = 0;
    PWAIT_THREAD_INFORMATION pThreadInformation = NULL;

    ASSERT( lpParameter != NULL );

    pThreadInformation = ( PWAIT_THREAD_INFORMATION )lpParameter;

    ASSERT(pThreadInformation->dwWaitObjectCount <= MAXIMUM_WAIT_OBJECTS );
    ASSERT(pThreadInformation->pWaitHandles      != NULL );
    ASSERT(pThreadInformation->dwWaitMSeconds    != INFINITE );
    ASSERT(pThreadInformation->dwWaitMSeconds    >= 1000 );

    dwReturnValue = WaitForMultipleObjects(
                        pThreadInformation->dwWaitObjectCount,
                        pThreadInformation->pWaitHandles,
                        TRUE,
                        pThreadInformation->dwWaitMSeconds );

    return dwReturnValue;

}

Sample usage of this method will look like below.

//
// Allocate memory for wait handle array
//
pWaitHandles = new HANDLE[ dwWaitHandleCount ];

...

//
// Call WaitForAllObjects
//
WaitForAllObjects( dwWaitHandleCount,
                   pWaitHandles,
                   dwMaximumWait );

...

//
// Free memory held by pWaitHandles
//
if( pWaitHandles != NULL )
{
    delete [] pWaitHandles;
    pWaitHandles = NULL;
}

Hope this helps.
Kanwal

Tool to find native module’s pipeline events registration (source code)

//
// Filename – getmreg.cxx 
// Build this as an executable.
// Keep this exe and module dll
// in the same folder
//
// Run using "getmreg.exe <path to module>"
//
// This tool uses hostable web core functionality
// of IIS7. So this will only run on machine with
// IIS7 installed. You don’t need to have w3svc running.
//

#include <stdio.h>
#include <windows.h>
#include "httpserv.h"
#include "hwebcore.h"

HRESULT
WriteContentToConfigFile(
    HANDLE hConfigFile,
    DWORD dwPort,
    PCSTR pszModulePath
);

extern
"C"
INT
__cdecl
wmain(
    INT argc,
    __in_ecount(argc) PCWSTR argv[]
)
{
    //
    // Introduction
    //
    wprintf(L"GetMReg: Prints event registration information of native modules.\r\n");
    wprintf(L" Author: Kanwaljeet Singla\r\n\r\n");

   
HRESULT hr = S_OK;
    DWORD dwErr = 0;
    DWORD dwSize = 0;

    if(argc != 2)
    {
        wprintf(L"Usage: getmreg <path to native module>\r\nAborting\r\n");
        goto finished;
    }
   
    //
    // Set the module path in an environment variable
    // which module can read
    //
    if(SetEnvironmentVariable(
            L"MODULEPATH",
            argv[1]) == FALSE)
    {
        wprintf(L"Call to SetEnvironmentVariable failed\r\nAborting\r\n");
        goto finished;
    }

    //
    // Build path to hwebcore.dll using GetSystemDirectory
    //
    HINSTANCE hHWebCore = NULL;
    PWSTR pszSystemDir = NULL;
   
    dwSize = GetSystemDirectory(pszSystemDir, 0);
    if(dwSize == 0)
    {
        wprintf(L"Call to GetSystemDirectory failed\r\nAborting\r\n");
        goto finished;
    }

    pszSystemDir = (PWSTR)malloc((dwSize + 1)* sizeof(WCHAR));
    if(pszSystemDir == NULL)
    {
        wprintf(L"Out of memory error\r\nAborting\r\n");
        goto finished;
    }

    if(GetSystemDirectory(pszSystemDir, dwSize + 1) == 0)
    {
        wprintf(L"Call to GetSystemDirectory gave unexpected error\r\nAborting\r\n");
        goto finished;
    }

    PWSTR hwebcore = (PWSTR)malloc(MAX_PATH * sizeof(WCHAR));
    if(hwebcore == NULL)
    {
        wprintf(L"Out of memory error\r\nAborting\r\n");
        goto finished;

    }


    if(wcslen(pszSystemDir) > MAX_PATH – 22)
    {
        wprintf(L"hwebcore.dll path larger than MAX_PATH\r\nAborting\r\n");
        goto finished;
    }

 

 

    wcscat(hwebcore, L"\\inetsrv\\hwebcore.dll");
   
    //
    // Load hwebcore.dll
    //
    hHWebCore = LoadLibraryEx(hwebcore, NULL, 0);
    if(hHWebCore == NULL)
    {
        wprintf(L"Could not load hwebcore.dll\r\nAborting\r\n");
        goto finished;
    }
    //
    // Get entry point WebcoreActivate and WebcoreShutdown
    //
    PFN_WEB_CORE_ACTIVATE pfnWebcoreActivate = NULL;
    PFN_WEB_CORE_SHUTDOWN pfnWebcoreShutdown = NULL;

    pfnWebcoreActivate = (PFN_WEB_CORE_ACTIVATE)GetProcAddress(
        hHWebCore,
        WEB_CORE_ACTIVATE_DLL_ENTRY);

    pfnWebcoreShutdown = (PFN_WEB_CORE_SHUTDOWN) GetProcAddress(
        hHWebCore,
        WEB_CORE_SHUTDOWN_DLL_ENTRY
        );

    if(pfnWebcoreActivate == NULL ||
       pfnWebcoreShutdown == NULL)
    {
        wprintf(L"Didn’t get WebcoreActivate or WebcoreShutdown entry points\r\nAborting\r\n");
        goto finished;
    }

    //
    // Get current process path
    //
    PSTR pszHelperModulePath = NULL;
    pszHelperModulePath = (PSTR)malloc(MAX_PATH * sizeof(CHAR));
    dwSize = GetModuleFileNameA(NULL,
        pszHelperModulePath,
        MAX_PATH);

    if(dwSize == 0)
    {
        wprintf(L"Call to GetModuleFileNameA failed\r\nAborting\r\n");
        goto finished;
    }

    //
    // Get the folder and add GetMRegModule.dll to build module path
    //
    PSTR pszLastSlash = strrchr(pszHelperModulePath, ‘\\’);

    *(pszLastSlash + 1) = 0;

    if(strlen(pszHelperModulePath) > MAX_PATH – 18)
    {
        wprintf(L"GetMRegModule.dll path larger than MAX_PATH\r\nAborting\r\n");
        goto finished;
    }

 

    strcat(pszHelperModulePath, "GetMRegModule.dll");

    //
    // Create config file in temp folder
    // Ports starting from 31212 will be tried if
    // WebcoreActivate fails with error 0x80070038
    //

    DWORD dwPort = 31212;
    PWSTR pszTempPath = NULL;
    dwSize = GetTempPath(0, pszTempPath);

    if(dwSize == 0)
    {
        wprintf(L"Call to GetTempPath failed\r\nAborting\r\n");
        goto finished;
    }

    pszTempPath = (PWSTR)malloc((dwSize + 1)* sizeof(WCHAR));
    if(pszTempPath == NULL)
    {
        wprintf(L"Out of memory error\r\nAborting\r\n");
        goto finished;
    }

    if(GetTempPath(dwSize + 1, pszTempPath) == 0)
    {
        wprintf(L"Call to GetTempPath gave unexpected error\r\nAborting\r\n");
        goto finished;
    }

    PWSTR configPath = (PWSTR)malloc(MAX_PATH * sizeof(WCHAR));
    wcscpy(configPath, pszTempPath);
    wcscat(configPath, L"getmreg.config");
   
    //
    // CREATE_ALWAYS overwrite existing file
    //
createconfig:
    HANDLE hConfigFile = CreateFile(configPath,
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if(hConfigFile == NULL)
    {
        wprintf(L"Couldn’t create file\r\nAborting\r\n");
        goto finished;
    }

    hr = WriteContentToConfigFile(
        hConfigFile,
        dwPort,
        pszHelperModulePath);
    if(FAILED(hr))
    {
        goto finished;
    }

    if(hConfigFile != NULL)
    {
        CloseHandle(hConfigFile);
        hConfigFile = NULL;
    }

    //
    // Call WebcoreActivate which should call RegisterModule of the module
    //
    hr = pfnWebcoreActivate(configPath, L"", L"GETMREG");
    if(FAILED(hr))
    {
        wprintf(L"Call to webcore activate failed %d\r\n", hr);
        if(hr == 0x80070038)
        {
            wprintf(L"Trying different port\r\n");
            dwPort++;
            goto createconfig;
        }
        else
        {
            wprintf(L"Aborting\r\n");
            goto finished;
        }
    }

    //
    // Call WebcoreShutdown
    //
    hr = pfnWebcoreShutdown(1);
    if(FAILED(hr))
    {
        wprintf(L"Call to webcore shutdown failed\r\n");
        wprintf(L"Aborting\r\n");
        goto finished;
    }

finished:
    //
    // Unload hwebcore.dll
    //
    if(hHWebCore != NULL)
    {
        FreeLibrary(hHWebCore);
        pfnWebcoreActivate = NULL;
        pfnWebcoreShutdown = NULL;
        hHWebCore = NULL;
    }

    //
    // free allocated string buffers
    //
    if(pszSystemDir != NULL)
    {
        free(pszSystemDir);
        pszSystemDir = NULL;
    }

    if(hwebcore != NULL)
    {
        free(hwebcore);
        hwebcore = NULL;
    }

    if(pszHelperModulePath != NULL)
    {
        free(pszHelperModulePath);
        pszHelperModulePath = NULL;
    }

    if(pszTempPath != NULL)
    {
        free(pszTempPath);
        pszTempPath = NULL;
    }

    if(configPath != NULL)
    {
        free(configPath);
        configPath = NULL;
    }
   
    //
    // Close file handles
    //
    if(hConfigFile != NULL)
    {
        CloseHandle(hConfigFile);
        hConfigFile = NULL;
    }

    return 0;
}   // wmain

HRESULT
WriteContentToConfigFile(
    IN HANDLE hConfigFile,
    IN DWORD dwPort,
    IN PCSTR pszModulePath
)
{
    HRESULT hr = S_OK;
    PCSTR pszConfigContent1 =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
        "\r\n"
        "<configuration>\r\n"
        "\r\n"
        "    <configSections>\r\n"
        "        <sectionGroup name=\"system.applicationHost\">\r\n"
        "            <section name=\"applicationPools\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"customMetadata\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"listenerAdapters\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"log\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"sites\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"webLimits\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "        </sectionGroup>\r\n"
        "\r\n"
        "        <sectionGroup name=\"system.webServer\">\r\n"
        "            <section name=\"asp\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"caching\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"cgi\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"defaultDocument\" overrideModeDefault=\"Allow\" />\r\n"
        "            <section name=\"directoryBrowse\" overrideModeDefault=\"Allow\" />\r\n"
        "            <section name=\"globalModules\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"handlers\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"httpCompression\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"httpErrors\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"httpLogging\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"httpProtocol\" overrideModeDefault=\"Allow\" />\r\n"
        "            <section name=\"httpRedirect\" overrideModeDefault=\"Allow\" />\r\n"
        "            <section name=\"httpTracing\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"isapiFilters\" allowDefinition=\"MachineToApplication\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"modules\" allowDefinition=\"MachineToApplication\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"odbcLogging\" overrideModeDefault=\"Deny\" />\r\n"
        "            <sectionGroup name=\"security\">\r\n"
        "                <section name=\"access\" overrideModeDefault=\"Deny\" />\r\n"
        "                <section name=\"applicationDependencies\" overrideModeDefault=\"Deny\" />\r\n"
        "                <sectionGroup name=\"authentication\">\r\n"
        "                    <section name=\"anonymousAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                    <section name=\"basicAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                    <section name=\"clientCertificateMappingAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                    <section name=\"digestAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                    <section name=\"iisClientCertificateMappingAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                    <section name=\"windowsAuthentication\" overrideModeDefault=\"Deny\" />\r\n"
        "                </sectionGroup>\r\n"
        "                <section name=\"authorization\" overrideModeDefault=\"Deny\" />\r\n"
        "                <section name=\"ipSecurity\" overrideModeDefault=\"Deny\" />\r\n"
        "                <section name=\"isapiCgiRestriction\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\r\n"
        "                <section name=\"requestFiltering\" overrideModeDefault=\"Deny\" />\r\n"
        "            </sectionGroup>\r\n"
        "            <section name=\"serverRuntime\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"serverSideInclude\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"staticContent\" overrideModeDefault=\"Deny\" />\r\n"
        "            <sectionGroup name=\"tracing\">\r\n"
        "                <section name=\"traceFailedRequests\" overrideModeDefault=\"Deny\" />\r\n"
        "                <section name=\"traceProviderDefinitions\" overrideModeDefault=\"Deny\" />\r\n"
        "            </sectionGroup>\r\n"
        "            <section name=\"urlCompression\" overrideModeDefault=\"Deny\" />\r\n"
        "            <section name=\"validation\" overrideModeDefault=\"Allow\" />\r\n"
        "        </sectionGroup>\r\n"
        "    </configSections>\r\n"
        "\r\n"
        "    <configProtectedData>\r\n"
        "        <providers>\r\n"
        "            <add name=\"IISWASOnlyRsaProvider\" type=\"\" description=\"Uses RsaCryptoServiceProvider to encrypt and decrypt\" keyContainerName=\"iisWasKey\" cspProviderName=\"\" useMachineContainer=\"true\" useOAEP=\"false\" />\r\n"
        "        </providers>\r\n"
        "    </configProtectedData>\r\n"
        "\r\n"
        "    <system.applicationHost>\r\n"
        "\r\n"
        "        <applicationPools>\r\n"
        "            <add name=\"MyAppPool\" />\r\n"
        "        </applicationPools>\r\n"
        "\r\n"
        "        <listenerAdapters>\r\n"
        "            <add name=\"http\" />\r\n"
        "        </listenerAdapters>\r\n"
        "\r\n"
        "        <sites>\r\n"
        "            <site name=\"My Web Site\" id=\"31212\">\r\n"
        "                <application path=\"/\">\r\n"
        "                    <virtualDirectory path=\"/\" physicalPath=\"%systemdrive%\\inetpub\\wwwroot\\\" />\r\n"
        "                </application>\r\n"
        "                <bindings>\r\n"
        "                    <binding protocol=\"http\" bindingInformation=\"*:";
   
    PCSTR pszConfigContent2 =
        ":\" />\r\n"
        "                </bindings>\r\n"
        "            </site>\r\n"
        "            <applicationDefaults applicationPool=\"MyAppPool\" />\r\n"
        "            <virtualDirectoryDefaults allowSubDirConfig=\"true\" />\r\n"
        "        </sites>\r\n"
        "\r\n"
        "    </system.applicationHost>\r\n"
        "\r\n"
        "    <system.webServer>\r\n"
        "\r\n"
        "        <globalModules>\r\n"
        "            <add name=\"GetMRegModule\" image=\"";

   
PCSTR pszConfigContent3 =
        "\" />\r\n"
        "        </globalModules>\r\n"
        "\r\n"
        "    </system.webServer>\r\n"
        "\r\n"
        "</configuration>\r\n";

   
//
    // Put port number in the config string
    //
    PSTR pszPort = NULL;
    pszPort = (PSTR)malloc(6 * sizeof(CHAR));
    _itoa(dwPort, pszPort, 10);
   
   PSTR pszConfigContent = NULL;
    pszConfigContent = (PSTR)malloc(
        strlen(pszConfigContent1) +
        strlen(pszPort) +
        strlen(pszConfigContent2) +
        strlen(pszModulePath) +
        strlen(pszConfigContent3) + 1);

    strcpy(pszConfigContent, pszConfigContent1);
    strcat(pszConfigContent, pszPort);
    strcat(pszConfigContent, pszConfigContent2);
    strcat(pszConfigContent, pszModulePath);
    strcat(pszConfigContent, pszConfigContent3);

    //
    // Write the contents to the temp config file
    //
    DWORD cbBytes;
    if(WriteFile(
        hConfigFile,
        pszConfigContent,
        strlen(pszConfigContent),
        &cbBytes,
        NULL) == 0)
    {
        wprintf(L"No bytes written to config file\r\nAborting\r\n");
        hr = E_FAIL;
        goto finished;
    }

finished:
    if(pszPort != NULL)
    {
        free(pszPort);
        pszPort = NULL;
    }
    if(pszConfigContent != NULL)
    {
        free(pszConfigContent);
        pszConfigContent = NULL;
    }

 

 
}
 

 

 

// Build this as a library and export RegisterModule
// Keep the name as GetMRegModule.dll (or change the name
// in other executable accordingly
//

#include "httpserv.h"
#include "http.h"
#include <stdio.h>
 class MyRegistrationProvider : IHttpModuleRegistrationInfo
{
public:
    MyRegistrationProvider(
        IHttpModuleRegistrationInfo *pModuleInfo
    )
    {
        _pModuleInfo    = pModuleInfo;
        _pModuleFactory = NULL;
        _pGlobalModule  = NULL;
    }

    ~MyRegistrationProvider()
    {
        if(_pModuleFactory != NULL)
        {
            _pModuleFactory->Terminate();
        }

        if(_pGlobalModule != NULL)
        {
            _pGlobalModule->Terminate();
        }
    }

    PCWSTR
    GetName(
        VOID
    ) const
    {
        return _pModuleInfo->GetName();
    }

    HTTP_MODULE_ID
    GetId(
        VOID
    ) const
    {
        return _pModuleInfo->GetId();
    }

    HRESULT
    SetRequestNotifications(
        IN IHttpModuleFactory * pModuleFactory,
        IN DWORD                dwRequestNotifications,
        IN DWORD                dwPostRequestNotifications
    )
    {
        //
        // Print request notifications
        //
        _pModuleFactory = pModuleFactory;
        wprintf(L"Request notifications\r\n");
        PrintRequestNotifications(dwRequestNotifications, dwPostRequestNotifications);
        wprintf(L"\r\n");

        //
        // Blindly return S_OK. We don’t actually want to
        // the notifications
        //
        return S_OK;
    }

    HRESULT
    SetGlobalNotifications(
        IN CGlobalModule *      pGlobalModule,
        IN DWORD                dwGlobalNotifications
    )
    {
        //
        // Print global notifications
        //
        _pGlobalModule = pGlobalModule;
        wprintf(L"Global notifications\r\n");
        PrintGlobalNotifications(dwGlobalNotifications);
        wprintf(L"\r\n");

        //
        // Blindly return S_OK
        //
        return S_OK;
    }

    HRESULT
    SetPriorityForRequestNotification(
        IN DWORD                dwRequestNotification,
        IN PCWSTR               pszPriority
    )
    {
        //
        // Print request notification priority information
        //
        wprintf(L"Setting priority for request notifications\r\n");
        PrintRequestNotifications(dwRequestNotification, 0);
        wprintf(L"%s\r\n", pszPriority);
        wprintf(L"\r\n");

        //
        // Blindly return S_OK
        //
        return S_OK;
    }

    HRESULT
    SetPriorityForGlobalNotification(
        IN DWORD                dwGlobalNotification,
        IN PCWSTR               pszPriority
    )
    {
        //
        // Print global notification priority information
        //
        wprintf(L"Setting priority for request notifications\r\n");
        PrintGlobalNotifications(dwGlobalNotification);
        wprintf(L"%s\r\n", pszPriority);
        wprintf(L"\r\n");

        //
        // Blindly return S_OK
        //
        return S_OK;
    }

private
:
    IHttpModuleRegistrationInfo * _pModuleInfo;
    IHttpModuleFactory          * _pModuleFactory;
    CGlobalModule               * _pGlobalModule;

    VOID PrintRequestNotifications(
        DWORD dwRequestNotifications,
        DWORD dwPostRequestNotifications
    );

    VOID PrintGlobalNotifications(
        DWORD dwGlobalNotifications
    );
};

VOID MyRegistrationProvider::PrintRequestNotifications(
    DWORD dwRequestNotifications,
    DWORD dwPostRequestNotifications
)
{
    DWORD count = 0;
    PCWSTR rnNames[16] = {
        L"RQ_BEGIN_REQUEST",
        L"RQ_AUTHENTICATE_REQUEST",
        L"RQ_AUTHORIZE_REQUEST",
        L"RQ_RESOLVE_REQUEST_CACHE",
        L"RQ_MAP_REQUEST_HANDLER",
        L"RQ_ACQUIRE_REQUEST_STATE",
        L"RQ_PRE_EXECUTE_REQUEST_HANDLER",
        L"RQ_EXECUTE_REQUEST_HANDLER",
        L"RQ_RELEASE_REQUEST_STATE",
        L"RQ_UPDATE_REQUEST_CACHE",
        L"RQ_LOG_REQUEST",
        L"RQ_END_REQUEST",
        L"RQ_MAP_PATH",
        L"RQ_READ_ENTITY",
        L"RQ_SEND_RESPONSE",
        L"RQ_CUSTOM_NOTIFICATION"
    };

    //
    // Print request registration information
    //
    for(DWORD i = 0; i <= 11; i++)
    {
        count = (1 << i);
        if((dwRequestNotifications & count) != 0)
        {
            wprintf(L"%s\r\n", rnNames[i]);
        }
    }

    //
    // Print asynchronous request registration information
    //
    for(DWORD i = 0; i <= 3; i++)
    {
        count = (1 << (31 – i));
        if((dwRequestNotifications & count) != 0)
        {
            wprintf(L"%s\r\n", rnNames[12 + i]);
        }
    }

    //
    // Print POST request registration information
    //
    for(DWORD i = 0; i <= 11; i++)
    {
        count = (1 << i);
        if((dwPostRequestNotifications & count) != 0)
        {
            wprintf(L"POST_%s\r\n", rnNames[i]);
        }
    }

    //
    // Print asynchronous POST request registration information
    //
    for(DWORD i = 0; i <= 3; i++)
    {
        count = (1 << (31 – i));
        if((dwPostRequestNotifications & count) != 0)
        {
            wprintf(L"POST_%s\r\n", rnNames[12 + i]);
        }
    }
}

VOID MyRegistrationProvider::PrintGlobalNotifications(
    DWORD dwGlobalNotifications
)
{
    DWORD count = 0;
    PCWSTR gnNames[16] = {
        L"",
        L"GL_STOP_LISTENING",
        L"GL_CACHE_CLEANUP",
        L"",
        L"GL_CACHE_OPERATION",
        L"GL_HEALTH_CHECK",
        L"GL_CONFIGURATION_CHANGE",
        L"GL_FILE_CHANGE",
        L"GL_PRE_BEGIN_REQUEST",
        L"GL_APPLICATION_START",
        L"GL_APPLICATION_RESOLVE_MODULES",
        L"GL_APPLICATION_STOP",
        L"GL_RSCA_QUERY",
        L"GL_TRACE_EVENT",
        L"GL_CUSTOM_NOTIFICATION",
        L"GL_THREAD_CLEANUP"
    };

    //
    // Print global registration information
    //
    for(DWORD i = 0; i <= 15; i++)
    {
        count = (1 << i);
        if((dwGlobalNotifications & count) != 0)
        {
            wprintf(L"%s\r\n", gnNames[i]);
        }
    }
}

HRESULT
RegisterModule(
    DWORD                           dwServerVersion,
    IHttpModuleRegistrationInfo *   pModuleInfo,
    IHttpServer *                   pGlobalInfo
)
{
    HRESULT hr = S_OK;

    //
    // Load another module
    //
    HINSTANCE hModule = NULL;
    PWSTR pszModulePath = NULL;
    DWORD dwSize = 0;

    //
    // Get the size of the buffer to be allocated
    //
    dwSize = GetEnvironmentVariable(
        L"MODULEPATH",
        pszModulePath,
        0);

    if(dwSize == 0)
    {
        wprintf(L"Environment variable MODULEPATH should point to module\r\n");
        goto finished;
    }

    //
    // Allocate buffer and call again
    //
    pszModulePath = (PWSTR)malloc(dwSize * sizeof(WCHAR));
    if(GetEnvironmentVariable(
        L"MODULEPATH",
        pszModulePath,
        dwSize) == 0)
    {
        wprintf(L"Error getting environment variable MODULEPATH\r\n");
        goto finished;
    }

    wprintf(L"Registration information for module %s\r\n\r\n", pszModulePath);

    //
    // Load the module whose registrations need to be found
    //
    hModule = LoadLibraryEx(
        pszModulePath,
        NULL,
        0);

    if(hModule == NULL)
    {
        wprintf(L"Couldn’t load module\r\n");
        wprintf(L"Aborting\r\n");
        goto finished;
    }

    //
    // Get RegisterModule entry point
    //
    PFN_REGISTERMODULE  pfnRegisterModule;
    pfnRegisterModule = (PFN_REGISTERMODULE)GetProcAddress(hModule, "RegisterModule" );

    if(pfnRegisterModule == NULL)
    {
        wprintf(L"RegisterModule entry point not found\r\n");
        wprintf(L"Aborting\r\n");
        goto finished;
    }

    //
    // Call register module now
    //
    MyRegistrationProvider * pRegProvider = new MyRegistrationProvider(pModuleInfo);

    hr = pfnRegisterModule(
        dwServerVersion,
        (IHttpModuleRegistrationInfo *)pRegProvider,
        pGlobalInfo);

    if(FAILED(hr))
    {
        wprintf(L"RegisterModule returned error\r\n");
        goto finished;
    }

finished:
    if(pRegProvider != NULL)
    {
        delete pRegProvider;
        pRegProvider = NULL;
    }

    if(hModule != NULL)
    {
        FreeLibrary(hModule);
        hModule = NULL;
    }

    if(pszModulePath != NULL)
    {
        free(pszModulePath);
        pszModulePath = NULL;
    }

    return S_OK;
}

Use attached GetMRegInstall.zip containing GetMRegInstall.exe to install the program.

-Kanwal

//
// Filename – module.cxx
//

 

    return hr;

    wcscpy(hwebcore, pszSystemDir);

 

Sample forms authentication test in C#

This sample test is doing the following:
1. Sending request to a page which requires forms authentication. This results in 302 to login page.
2. Send request to login page.
3. Parse response from 2 and create response entity containing username/password to be used in next post request to login page.
4. Do a POST to login page. If successful this should return a 302 with Set-Cookie and location header.
5. Send request to location pointed to in last response (this is original page we requested in 1) with request cookie as returned in 4. Expect 200.

using System;
using System.IO;
using System.Net;

namespace FormsAuthTest
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            StreamReader sr = null;

            String originalUri = "http://localhost/default.aspx";

            System.Diagnostics.ConsoleTraceListener trace =
                    new System.Diagnostics.ConsoleTraceListener();

            //
            // Request page protected by forms authentication.
            // This request will get a 302 to login page
            //
            trace.Write("Requesting : " + originalUri);
            request = (HttpWebRequest)WebRequest.Create(originalUri);
            request.AllowAutoRedirect = false;

            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode == HttpStatusCode.Found)
            {
                trace.Write("Response: 302 ");
                trace.WriteLine(response.StatusCode);
            }
            else
            {
                trace.Fail("Response status is " + response.StatusCode + ". Expected was Found");
            }

           
//
            // Get the url of login page from location header
            //
            String locationHeader = response.GetResponseHeader("Location");
            trace.WriteLine("Location header is " + locationHeader);
            trace.WriteLine("");

           
//
            // Request login page
            //
            String loginPageUrl = "http://localhost" + locationHeader;
            Console.WriteLine("Requesting " + loginPageUrl);
            request = (HttpWebRequest)WebRequest.Create(loginPageUrl);
            request.AllowAutoRedirect = false;

            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode == HttpStatusCode.OK)
            {
                trace.Write("Response: 200 ");
                trace.WriteLine(response.StatusCode);
            }
            else
            {
                trace.Fail("Response status is " + response.StatusCode + ". Expected was OK");
            }

            trace.WriteLine("Parsing login page to create post message");
            trace.WriteLine("");

            sr = new StreamReader(response.GetResponseStream());
            String loginResponse = sr.ReadToEnd();
            sr.Close();

           
String
eventTargetVar = "__EVENTTARGET=";
            String eventTargetValue = "";

           
String
eventArgumentVar = "__EVENTARGUMENT=";
            String eventArgumentValue = "";

           
String
viewStateVar = "__VIEWSTATE=";
            String viewStateSearchString = "name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\"";
            int viewStateStartIndex = loginResponse.IndexOf(viewStateSearchString);
            loginResponse = loginResponse.Substring(viewStateStartIndex + viewStateSearchString.Length);
            String viewStateValue = Uri.EscapeDataString(
                                                           loginResponse.Substring(0, loginResponse.IndexOf("\" />"))
                                                       );
            loginResponse = loginResponse.Substring(loginResponse.IndexOf("\" />"));

           
String
 lcSearchStr = "input name=";
            int lcSearchIndex = 0;

            //
            // Look for logon control id
            // Use any valid username and password

            //
            lcSearchIndex = loginResponse.IndexOf(lcSearchStr);
            loginResponse = loginResponse.Substring(lcSearchIndex + lcSearchStr.Length + 1);
            String userNameVar = Uri.EscapeDataString(
                                                       loginResponse.Substring(0, loginResponse.IndexOf("\""))
                                                   ) + "=";
            String userNameValue = "Alice";

            lcSearchIndex = loginResponse.IndexOf(lcSearchStr);
            loginResponse = loginResponse.Substring(lcSearchIndex + lcSearchStr.Length + 1);
            String passwordVar = Uri.EscapeDataString(
                                                       loginResponse.Substring(0, loginResponse.IndexOf("\""))
                                                    ) + "=";
            String passwordValue = "alice123";

            lcSearchStr = "type=\"submit\" name=";
            lcSearchIndex = loginResponse.IndexOf(lcSearchStr);
            loginResponse = loginResponse.Substring(lcSearchIndex + lcSearchStr.Length + 1);
            String loginButtonVar = Uri.EscapeDataString(
                                                           loginResponse.Substring(0, loginResponse.IndexOf("\""))
                                                       ) + "=";
            String loginButtonValue = "Log+In";

           
String
eventValidationVar = "__EVENTVALIDATION=";
            String eventValSearchString =
                "name=\"__EVENTVALIDATION\" id=\"__EVENTVALIDATION\" value=\"";
            int eventValStartIndex = loginResponse.IndexOf(eventValSearchString);
            loginResponse = loginResponse.Substring(eventValStartIndex + eventValSearchString.Length);
            String eventValidationValue =
                Uri.EscapeDataString(
                    loginResponse.Substring(0, loginResponse.IndexOf("\" />"))
                );

            String postString = eventTargetVar + eventTargetValue;
            postString += "&" + eventArgumentVar + eventArgumentValue;
            postString += "&" + viewStateVar + viewStateValue;
            postString += "&" + userNameVar + userNameValue;
            postString += "&" + passwordVar + passwordValue;
            postString += "&" + loginButtonVar + loginButtonValue;
            postString += "&" + eventValidationVar + eventValidationValue;

           
//
            // Do a POST to login.aspx now
            // This should result in 302 with Set-Cookie header
            //
            Console.WriteLine("POST request to http://localhost" + locationHeader);
            request = (HttpWebRequest)WebRequest.Create("http://localhost" + locationHeader);
            request.AllowAutoRedirect = false;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] requestData = encoding.GetBytes(postString);
            request.ContentLength = requestData.Length;

           
Stream
requestStream = request.GetRequestStream();
            requestStream.Write(requestData, 0, requestData.Length);
            requestStream.Close();

            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode == HttpStatusCode.Found)
            {
                trace.Write("Response: 302 ");
                trace.WriteLine(response.StatusCode);
            }
            else
            {
                trace.Fail("Response status is " + response.StatusCode + ". Expected was Found");
            }

            locationHeader = response.GetResponseHeader("Location");
            trace.WriteLine("Location header is " + locationHeader);
            String cookie = response.GetResponseHeader("Set-Cookie");
            trace.WriteLine("Set-Cookie header is " + cookie);
            trace.WriteLine("");

           
//
            // Send request to originalUri with the cookie
            // We should be able to see originalUri contents
            //
            trace.WriteLine("Requesting http://localhost" + locationHeader + " with cookie");
            request = (HttpWebRequest)WebRequest.Create("http://localhost" + locationHeader);
            request.AllowAutoRedirect = false;
            request.Headers.Add(HttpRequestHeader.Cookie, cookie);

            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode == HttpStatusCode.OK)
            {
                trace.Write("Response: 200 ");
                trace.WriteLine(response.StatusCode);
            }
            else
            {
                trace.Fail("Response status is " + response.StatusCode + ". Expected was OK");
            }
            trace.WriteLine("");

            trace.WriteLine("Contents of " + originalUri);
            trace.WriteLine("");

            sr = new StreamReader(response.GetResponseStream());
            trace.WriteLine(sr.ReadToEnd());
            sr.Close();
        }
    }
}

Above sample is requesting aspx content. You can remove precondition from FormsAuthentication module on your server and use the same code to request non-aspx content as well.

Kanwal