HOWTO: ISAPI Filter Redirection Walkthrough

I got the following question about ISAPI Filter the other day.

Question:

Hi
I found your contact on the web after searching info about ISAPI Filter. I am almost new to the IIS world, I have a task to write a simple filter which , but I struggle with C++ in windows.
1. Architecture: Windows Server 2003 , IIS 6
2. Development tool: Visual Studio C++ 6.0

The web flow is as follow:
[browser]-->[IIS 6.0]--->[CMS system]
The filter needs to intercept an answer from the CMS system when the response code is 403 [Forbidden] and redirect it to a predefined URL.

I tried to use the Wizard to create the ISAPI filter. But I don't know how to:
1. intercept the call from the CMS
2. read from a configuration file
3. write to a debug file.

I tried to use the HttpFilterProc but when I compile, there is a complain saying that this function is already used/defined.

Your help will be greatly appraciated.

Answer:

Welcome to Windows programming. It really is not that hard after you get used to all the available services Windows provides, accessible via Win32 API calls. MSDN and search should be your best friends when starting out. An ISAPI Filter is basically just a plain Win32 DLL that exposes a couple of entrypoints that IIS expects and invokes. Thus, only your first question has anything to do with ISAPI Filter; your other questions are basic Win32 questions that should be explored another time.

RE: Write to a debug file

If you plan on writing a file to the filesystem, then you will need to call the CreateFile API to do this. See: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createfile.asp . And if the filesystem is NTFS, you will need to make sure that the user identity performing the API call actually has write permissions to the file.

For an ISAPI Filter, IIS invokes its entrypoints using the process identity. The specific identity is determined by:

  1. IIS5 Compatibility mode - ISAPI Filters run in inetinfo.exe and process identity is LocalSystem
  2. IIS6 Worker Process Isolation mode (default) - ISAPI Filters run in w3wp.exe on a per-Application Pool basis, and process identity depends on the identity of the Application Pool. This is Network Service by default, but can be user configurable. It is also guaranteed to be a member of the IIS_WPG group since all application pool identities must be in that group

RE: read from a configuration file

You have several basic choices for storing configuration:

  • Registry Key
  • INI File
  • Custom file format

Out of those choices, Registry Key and INI File are the easiest to implement because Win32 APIs already exist to use them. Custom file format requires you to use CreateFile to open, read, and parse the file, so that is basically code you need to figure out on your own.

The Registry API is documented at: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/registry_functions.asp . Registry Keys are ACL'd like the file system, so the same rules for writing with CreateFile apply to reading using these APIs. The user identity making the function call must have access. You will most likely use RegOpenKeyEx to open the key, RegQueryValueEx to read the key, and RegCloseKey to close the key handle.

The INI File API is documented at the same URL as the Registry. INI files are based off the filesystem, so ACLs apply as well. You will most likely use GetPrivateProfileString to read data from an INI file. Its logical format is basically:

 [section]
key=value1
key2=value2

RE: intercepting the call from CMS

How you do this completely depends on how CMS integrates with IIS.

The key point you must remember is that ISAPI Filters are invoked as IIS is processing a request and sending a response. If the ISAPI Filter wants to alter request processing (such as send its own 302 redirection response or alter the response data completely), then it must make sure to suppress the response that IIS is otherwise planning to send out. If you fail to do this, the resulting response is likely going to be invalid HTTP or HTML to the client and severely confuse it -- and it will be all your fault. ISAPI Filters are in control of IIS, so it can make it do legal or illegal things.

Now, you did not clarify what you mean by "redirect it to a predefined URL". Did you mean:

  1. Instead of the 403 response, send back a 302 redirect response pointing to the predefined URL
  2. Instead of the 403 response, send back a 200 response containing the contents of the predefined URL

I am going to guess you want #1, but since it is a 302 redirection, the predefined URL will show up in the client's location/URL bar. #2 will look transparent to the client and will not change the client's location/URL bar because as far as the client is concerned, it made a request, and a 200 response with some entity body came back. It does not know there was a 403 nor predefined URL content was retrieved.

Now, how you capture the 403 completely depends on how CMS works on IIS.

If CMS is running on the same machine as IIS and is launched directly by IIS, then your ISAPI Filter can subscribe to the SF_NOTIFY_SEND_RESPONSE event, which will fire whenever CMS (or anything else on the server) sends a response back to the client. You are responsible for deciding which response to which URL you should act upon. In this event, you will get a HTTP_FILTER_SEND_RESPONSE structure with a HttpStatus DWORD field that indicates the status code of the response. You will obviously look for 403 responses for the matching URL namespace of CMS, and then you will perform either #1 or #2 mentioned above.

If CMS is running on a different machine, then it is most likely something is reverse proxying the response from CMS over IIS. In this case, you will need your ISAPI Filter to subscribe to the SF_NOTIFY_SEND_RAW event, which will fire whenever any raw data is sent back to the client. This event is streaming, meaning you have no idea of the structure of the response and must parse to figure out what is going on. This will not be easy and requires that you understand HTTP RFC2616 very thoroughly. I am not going to try to explain all the details; you will have to study and uncover them for yourself. You will basically parse and figure out if the response code is 403 and if so, perform your redirection

Conclusion:

I have intentionally NOT written any code in this case because you need to know how to do this for yourself.

I would not bother with the Wizard in Visual Studio. It is useless for a developer that doesn't know ISAPI, and it is equally useless for a developer that knows ISAPI.

There are plenty of sample code, even from Microsoft. I mean, I have some fully functional filter examples elsewhere in my blog, organized, that can be used as illustration. I know non-programmers have taken that code and successfully compiled/linked/debugged with instructions purely from my blog posts, so you should be able to do it.

But, since you are tasked to write the filter, you must learn all the basics, such as reading documentation, reading MSDN, searching for clues, reading RFCs for HTTP -- all are essential to successfully write an ISAPI Filter.

Good Luck,

//David