tags:

views:

31

answers:

3

I wrote this ISAPI filter to rewrite the URL because we had some sites that moved locations... Basically the filter looks at the referrer, and if it's the local server, it looks at the requested URL and compared it to the full referrer. If the first path is identical, nothing is done, however if not, it takes the first path from the full referrer and prepends it to the URL. For example: /Content/imgs/img.jpg from a referrer of http://myserver/wr/apps/default.htm would be rewritten as /wr/Content/imgs/img.jpg.

When I view the log file, everything looks good. However the DLL keeps faulting with the following information: Faulting application w3wp.exe, version 6.0.3790.3959, faulting module URLRedirector.dll, version 0.0.0.0, fault address 0x0002df25.

Here's the code:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <httpfilt.h>
#include <time.h>
#include <string.h>


#ifdef _DEBUG
#define TO_FILE  // uncomment out to use a log file
#ifdef TO_FILE
#define DEST ghFile
#define DebugMsg(x) WriteToFile x;
HANDLE ghFile;
#define LOGFILE "W:\\Temp\\URLRedirector.log"
void WriteToFile (HANDLE hFile, char *szFormat, ...) {
    char szBuf[1024];
    DWORD dwWritten;
    va_list list;
    va_start (list, szFormat);
    vsprintf (szBuf, szFormat, list);
    hFile = CreateFile (LOGFILE, GENERIC_WRITE, 
                        0, NULL, OPEN_ALWAYS, 
                        FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
        SetFilePointer (hFile, 0, NULL, FILE_END);
        WriteFile (hFile, szBuf, lstrlen (szBuf), &dwWritten, NULL);
        CloseHandle (hFile);
    }
    va_end (list);
}
#endif
#endif

BOOL WINAPI __stdcall GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
    /* Specify the types and order of notification */

    pVer->dwFlags = (SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT
                     | SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_END_OF_NET_SESSION);

    pVer->dwFilterVersion = HTTP_FILTER_REVISION;

    strcpy(pVer->lpszFilterDesc, "URL Redirector, Version 1.0");

    return TRUE;
}

DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData)
{
    CHAR *pPhysPath;
    PHTTP_FILTER_URL_MAP pURLMap;
    PHTTP_FILTER_PREPROC_HEADERS pHeaderInfo;
    CHAR szReferrer[255], szServer[255], szURL[255], szNewURL[255];
    DWORD dwRSize = sizeof(szReferrer);
    DWORD dwSSize = sizeof(szServer);
    DWORD dwUSize = sizeof(szURL);
    int iTmp, iTmp2;
    CHAR *pos, tmp[255], *tmp2;

    switch (NotificationType) {

        case SF_NOTIFY_PREPROC_HEADERS :
            pHeaderInfo = (PHTTP_FILTER_PREPROC_HEADERS)pvData;

            if (pfc->GetServerVariable(pfc, "HTTP_REFERER", szReferrer, &dwRSize))
            {
                DebugMsg(( DEST,
                           "Referrer: %s\r\n", szReferrer ));

                if (pfc->GetServerVariable(pfc, "SERVER_NAME", szServer, &dwSSize))
                    DebugMsg(( DEST,
                               "Server Name: %s\r\n", szServer ));

                if (pHeaderInfo->GetHeader(pfc, "URL", szURL, &dwUSize))
                    DebugMsg(( DEST,
                               "URL: %s\r\n", szURL ));

                iTmp = strnstr(szReferrer, szServer, strlen(szReferrer));
                if(iTmp > 0)
                {
                    //Referred is our own server...
                    strcpy(tmp, szReferrer + iTmp);
                    DebugMsg(( DEST,
                               "tmp: %s - %d\r\n", tmp, strlen(tmp) ));
                    pos = strchr(tmp+1, '/');
                    DebugMsg(( DEST,
                               "pos: %s - %d\r\n", pos, strlen(pos) ));

                    iTmp2 = strlen(tmp) - strlen(pos) + 1;

                    strncpy(tmp2, tmp, iTmp2);
                    tmp2[iTmp2] = '\0';
                    DebugMsg(( DEST,
                               "tmp2: %s\r\n", tmp2));

                    if(strncmp(szURL, tmp2, iTmp2) != 0)
                    {
                        //First paths don't match, create new URL...
                        strncpy(szNewURL, tmp2, iTmp2-1);
                        strcat(szNewURL, szURL);
                        DebugMsg(( DEST,
                                   "newURL: %s\r\n", szNewURL));
                        pHeaderInfo->SetHeader(pfc, "URL", szNewURL);
                        return SF_STATUS_REQ_HANDLED_NOTIFICATION;
                    }
                }
            }

            break;

        default :

            break;    
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}


/* simple function to compare two strings and return the position at which the compare ended */
static int strnstr ( const char *string, const char *strCharSet, int n)
{
    int len = (strCharSet  != NULL ) ? ((int)strlen(strCharSet )) : 0 ;
    int ret, I, J, found;

    if ( 0 == n || 0 == len )
    {
        return -1;
    }

    ret = -1;
    found = 0;
    for (I = 0 ; I <= n - len && found != 1 ; I++)
    {
        J = 0 ;
        for ( ; J < len ; J++ )
        {
            if (toupper(string[I + J]) != toupper(strCharSet [J]))
            {
                break; // Exit For(J)
            }
        }

        if ( J == len)
        {
            ret = I + (J);
            found = 1;
        } 
    }

    return ret;
}
+1  A: 

I know you're asking a programming question, but you could avoid the whole thing by abandoning the effort to write an ISAPI filter, and instead use an off-the-shelf general purpose rewriter, like IIRF.

This is the rule that would do what you want:

RewriteCond %{HTTP_REFERER} ^http://localserver/([^/]+)/etc/etc$
RewriteCond $1              !*1
RewriteRule ^/([^/]+)/([^/]+)\.(jpg|gif|png)$    /*1/$1/$2.$3    [L]
Cheeso
I guess I'm lost, because IIS doesn't allow you to do this out of the box and I can't exactly install something on our corporate web servers without jumping through lots of hoops and going through software reviews...
Brad
I don't understand. You are writing an ISAPI filter, right? IIRF is an isapi filter, too. You deploy it the same way you would deploy your simple filter. Whatever concerns you have with using IIRF, would be the same as with your own custom filter. No? or are you saying, they will allow a simple filter that you wrote, but not an externally-sourced filter like IIRF?
Cheeso
A: 

One potential problem is if szReferrer and szServer are the exact same values (not sure if that is possible). But if it does happen, then strnstr returns the length of the string value. In other words, it returns the length of the first string. In that situation, the strcpy into tmp I think puts only a zero termination byte into tmp. The following strchr(tmp+1, ‘/’); then has undefined behavior. The value returned from it could be anything (it could in fact cause an access violation because it could read beyond memory that it is supposed to). Things could fall apart after that.

Mark Wilkins
A: 

If you really want to write and maintain a custom ISAPI Filter, I highly recommend two things:

  1. Debug using Visual Studio
    .
    If the problem is reproducible, use Visual Studio or another debugger to attach to w3wp.exe in order to catch the exception. What you need to do is start IIS and run a request through it, so that the filter is loaded. Be sure the pdb file for the DLL is co-located with the DLL.
    .
    Then you need to start Visual Studio, then attach to the w3wp.exe process within the debugger. Then run the request that leads to the fault. Inside Visual Studio, the debugger will show you the exact line leading to the problem.

  2. Use StackWalker
    .
    If the problem is not reliably reproducible, you'll need to arrange to record the callstack dump, including line numbers, when the problem does occur, in order to assist diagnosing it. For this use the codeplex Stackwalker project. When the exception occurs, your ISAPI can dump the callstack into a log file, which allows you to then pinpoint the line in your code leading to the fault.

Cheeso