Kanwaljeet Singla's Blog

  • Implementing IAppHostPathMapper in C

    Few days ago I was required to implement IAppHostPathMapper interface in native C to map configuration path MACHINE/WEBROOT/APPHOST to DefaultAppPool.config and struggled with finding good documentation. With help of some incomplete, hard to find documentation and some head banging here is what worked for me. Hopefully this will be useful for few others J. Read more ...

    View the original post

  • New features in configuration system and appcmd in IIS 7.5

    Following new features have been added to IIS configuration system and appcmd command line tool in IIS 7.5.

    Configuration System

    1.       Configuration system tracing and logging
    In IIS 7.5, IIS native configuration system can generate trace events capturing all IIS configuration activity. Because all IIS administrative tools (WMI, appcmd, MWA, UI, Powershell etc) call into native configuration system, events are generated irrespective of which administrative tool is used to read/write IIS configuration. Tracing is not enabled by default. You can go to “Application and Service Logs->Microsoft->Windows->IIS-Configuration” in event viewer and enable tracing. IIS generates 4 kinds of events. These are administrative, operational, analytic and debug. Right click on areas in event viewer and select “enable log” for categories you want to enable tracing. Read more ...

    View the original post

  • IIS 7.5 updates to custom errors and compression

    Looking at number of people reaching my first blog post while searching for information on IIS 7.5, I figured I should do few more posts on changes which are coming in IIS 7.5. In this blog post I am covering new features which have been added to compression and custom errors modules in IIS 7.5.

    Changes to custom errors

    1.       system.webServer/httpErrors section is made delegation safe.
    Read more ...

    View the original post

  • Improvements to FastCGI in IIS 7.5

    Following improvements have been made to FastCGI module in Win7 (and Win2K8 R2).

    1.       Monitor changes to a file

    We have added ability to monitor changes to a file for each FastCGI process pool. Module will recycle FastCGI processes for the process pool if a change to the file is detected. Users can specify file path to monitor using system.webServer/fastCgi/application@monitorChangesTo property. Value can be absolute path to the file or path relative to the folder containing FastCGI executable. In case of php, if fullPath property is set to “c:\php\php-cgi.exe”, monitorChangesTo property can be set to “php.ini” or “c:\php\php.ini”. If file to monitor is not found, status code 500 is returned on each request. Default value of this property is blank which means no file monitoring. Read more ...

    View the original post

  • Generating trace information in native modules

    IIS7 has really nice tracing capabilities which are available to all module developers. If you want to make it easy for you and your customers to debug problems in your modules, you should definitely be using them. You can use IHttpTraceContext::RaiseTraceEvent or IHttpTraceContext::QuickTrace in your IIS7 native modules to generate trace information. Once you have your module generating trace information, you or your customers can set trace rules to collect trace data for particular requests. Read more ...

    View the original post

  • Using IMetadataInfo::GetModuleContextContainer to store configuration data

    If you write an IIS7 module, you would typically want to control its behavior based on some configuration data. IIS7 makes it really easy for developers to extend configuration schema so that their customers can put configuration data for their modules with other IIS configuration in applicationHost.config and web.config. Native module developers can then use AhAdmin APIs to read this configuration data. Reading configuration data for each request can be expensive. Developers would typically think of improving the performance by reading the configuration data once and then keeping the data in memory which can be used for other requests. To do this right, you need to worry about following things.

    • Keep unique data only for the paths where configuration changes and not for all possible configuration paths.
    • Detect configuration changes so that module always behave as per the latest configuration.
    • Only delete the configuration data of paths which are affected by the change and not throw everything.
    • Make sure this configuration store is accessed in thread safe manner.
    Instead of implementing this from scratch, you can use IMetadata interface available in httpserv.h. Call IHttpContext::GetMetadata() to get IMetadata object which is different for each unique configuration path and then use the IMetadataInfo::GetModuleContextContainer()->SetModuleContext() to store any data you like for your module. Configuration data is a perfect example of kind of data you would like to store for a unique configuration path. Stored data can be retrieved using IMetadataInfo::GetModuleContextContainer()->GetModuleContext(). In addition to making sure that a unique metadata object is available only for paths where configuration changes, IIS will also take care of deleting proper stored contexts when a change notification arrives. Here is how doing this will look like in code.

    //
    // Globals
    //
    HTTP_MODULE_ID g_pModuleContext = NULL;
    IHttpServer *  g_pGlobalInfo    = NULL;

    HRESULT
    WINAPI
    RegisterModule(
        DWORD                          dwServerVersion,
        IHttpModuleRegistrationInfo *  pModuleInfo,
        IHttpServer *                  pGlobalInfo
    )
    {
        ...

        g_pGlobalInfo    = pGlobalInfo;
        g_pModuleContext = pModuleInfo->GetId( );

        ...
    }

    class MODULE_CONFIG : public IHttpStoredContext
    {
    public:

        //
        // Always call this method to get configuration data
        //
       
    static
        HRESULT
        GetModuleConfig(
            IHttpContext *   pContext,
            MODULE_CONFIG ** ppModuleConfig
        );

        HRESULT
        Initialize(
            IHttpContext * pContext
        );

        // virtual
        VOID
        CleanupStoredContext(
            VOID
        )
        {
            delete this;
        }

        //
        // Stored Configuration Data
        //
    };

    //static
    HRESULT
    MODULE_CONFIG::GetModuleConfig(
        IHttpContext *   pContext,
        MODULE_CONFIG ** ppModuleConfig
    )
    {
        HRESULT                       hr                 = S_OK;
        MODULE_CONFIG *               pModuleConfig      = NULL;
        IHttpModuleContextContainer * pMetadataContainer = NULL;

        pMetadataContainer = pContext->GetMetadata()->GetModuleContextContainer();
        pModuleConfig = (MODULE_CONFIG *)pMetadataContainer->GetModuleContext( g_pModuleContext );

        if ( pModuleConfig != NULL )
        {
            //
            // We found stored data for this module for the metadata
            // object which is different for unique configuration path
            //
            *ppModuleConfig = pModuleConfig;
            return S_OK;
        }

        //
        // If we reach here, that means this is first request or first
        // request after a configuration change IIS core will throw stored context
        // if a change notification arrives for this metadata path
        //
        pModuleConfig = new MODULE_CONFIG();
        if ( pModuleConfig == NULL )
        {
            return E_OUTOFMEMORY;
        }

        //
        // Read module configuration data and store in MODULE_CONFIG
        //
        hr = pModuleConfig->Initialize( pContext );
        if ( FAILED( hr ) )
        {
            pModuleConfig->CleanupStoredContext();
            pModuleConfig = NULL;

            return hr;
        }

        //
        // Store MODULE_CONFIG data as metadata stored context
        //
        hr = pMetadataContainer->SetModuleContext( pModuleConfig,
                                                   g_pModuleContext );
        if ( FAILED( hr ) )
        {
            pModuleConfig->CleanupStoredContext();
            pModuleConfig = NULL;

            //
            // It is possible that some other thread stored context before this thread
            // could do. Check returned hr and return context stored by other thread
            //
            if ( hr == HRESULT_FROM_WIN32( ERROR_ALREADY_ASSIGNED ) )
            {
                *ppModuleConfig = (MODULE_CONFIG *)pMetadataContainer->GetModuleContext( g_pModuleContext );
                return S_OK;
            }
        }

        *ppModuleConfig = pModuleConfig;
        return hr;
    }

    HRESULT
    MODULE_CONFIG::Initialize(
        IHttpContext * pContext
    )
    {
        HRESULT                hr               = S_OK;
        IAppHostAdminManager * pAdminManager    = NULL;
        IAppHostElement *      pAppHostElement  = NULL;
        BSTR                   bstrSectionName  = NULL;
        BSTR                   bstrConfigPath   = NULL;

        ...

        pAdminManager = g_pGlobalInfo->GetAdminManager();
        ...

        //
        // Read configuration data at metapath of IMetadataInfo object
        //
        bstrConfigPath = SysAllocString( pContext->GetMetadata()->GetMetaPath() );
        ...

        hr = pAdminManager->GetAdminSection( bstrSectionName,
                                             bstrConfigPath,
                                             &pAppHostElement);
        ...
    }

    Hope this helps.
    Kanwal

  • Generating configuration to allow/deny access to countries

    There have been few requests on forums where people wanted to control access to sites based on country from where request originated. We recommended people to use IP restriction module functionality which required people to add IP address ranges of the countries they want to grant or deny access in ipSecurity section. This is easier said than done. There are a number of IP-to-country mapping lists available for free and updated frequently. These lists are usually in CSV format and identify countries (and sometimes even regions in the country) to which a particular IP-range is assigned. One such list can be downloaded from here. Entries in the CSV file go from 0 to max 32-bit unsigned integer. As the IP ranges assigned to a country are not contiguous, there are many entries (sometimes thousands) for one country. IIS7 IPRestriction module let you specify an IP-range using start-IP address and subnet mask. To move a single entry from CSV to IIS configuration you are required to create start IP address from a 32-bit integer and also create a subnet mask from IP-range both of which are not trivial. Also because most countries have hundreds of IP ranges assigned to them, going through CSV picking up these entries and then calculating start IP address and subnet mask manually is extremely difficult. Few days ago I wrote a script which does this for you. You can download the IP-to-country mapping list from here, unzip it (to say ip-to-country.csv) and then use the attached script (save ipres.js.txt as ipres.js).

    To see list of countries for which IP ranges are given in CSV, use “cscript.exe //nologo ipres.js /f ip-to-country.csv /l”
    To generate ipSecurity configuration to block access to a country, use “cscript.exe //nologo ipres.js /f ip-to-country.csv /d FewCharsToFilterCountry”
    To generate ipSecurity configuration to grant access to a country, use “cscript.exe //nologo ipres.js /f ip-to-country.csv /a FewCharsToFilterCountry”
    To find a particular IP address in this list, use "cscript.exe //nologo ipres.js /f ip-to-country.csv /g ip-address"

    You can specify more than one country separated by commas to filter on. Also you can use ‘*’, ‘?’ wildcard characters in country name filter. Script will dump the configuration on the console which you can paste in ipSecurity section. Feel free to change the script to make it emit adsutil.vbs calls to add entries to IIS6 metabase.

    In IIS7, ipSecurity section is locked by default. If you want to block access to a site, unlock ipSecurity section and add configuration for the site only. If you are adding the entries in web.config, you can use configSource option to keep the ipSecurity configuration in a separate file. Keep in mind that changes to configSource target file are not automatically picked up unless web.config file containing configSource attribute is changed. Also if you run into web.config file size limit, you can increase it by changing MaxWebConfigFileSizeInKB as specified in this blog.

    Hope this helps.
    Kanwal

  • GenSCode updated to help use AhAdmin in C#, JavaScript

    As promised in my previous post, I updated genscode tool to provide intellisense for AhAdmin code in C# and JavaScript. GenSCode now accepts an optional codetype switch which can be AhAJavaScript to help use AhAdmin in JavaScript, AhACSharp to help use AhAdmin in C# or MWACSharp (which is the default value) to ease using MWA in C#. Click here to download the updated tool. Read more ...

    View the original post

  • Enabling intellisense for AhAdmin code in scripts

    MWA (Microsoft.Web.Administration) is pretty popular among developers who wish to read/write IIS configuration because of ease of writing managed code (which enables its use in powershell as well) and also because it provides a more intuitive wrapper over the IIS configuration system which is easier to work with. MWA has concept of application pool collection, sites, bindings etc while IIS configuration system only understands sections, elements, properties, location paths etc and not application pools, virtual directories which makes life of developers writing AhAdmin code very difficult. If you disagree, try adding a binding using MWA and then using AhAdmin. Presence of tools like genscode makes using MWA even more easier. Read more ...

    View the original post

  • Tool to generate AhAdmin code for actions in IIS7 UI

    If you ever write AhAdmin code, this is for you.I have written a tool which generates equivalent JavaScript/C#/VB.Net AhAdmin code for actions done in IIS7 UI.

    IIS7 configuration system gives access to IIS config files via a set of COM APIs. You can read/write to IIS configuration using these APIs in a script or C/C++ or using any of the managed language. All the tools which are shipped with IIS like managed APIs (Microsoft.Web.Administration), UI (IIS Manager), WMI provider uses these COM APIs internally to perform all the configuration operations. When you do an action in the UI, UI is making calls to these COM APIs (actually via MWA). Programming against COM APIs provided by IIS configuration system requires extensive understanding of these APIs and also IIS schema. Also you are required to know what exactly you want to change in the configuration. So when if you want to enable IIS detailed error messages programmatically, you are required to know which property should be changed to what value to have the desired effect. IIS Manager does present the properties in more readable form which makes it easier to decide what changes you want to make to the system which is helpful to the administrators but not developers. Imagine if you could just do an operation using IIS Manager and get the equivalent AhAdmin code which you can copy-paste in your application. This is exactly what AhAdminCodeGenerator is written to do. In the snapshot below, you are looking at JavaScript code generated when I created a site using IIS Manager.