home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / software / pelne / optionp / iis4_07.cab / ckymunge.cpp < prev    next >
C/C++ Source or Header  |  1997-10-25  |  25KB  |  854 lines

  1. // CkyMunge: ISAPI filter for ASP session state for cookieless browsers.
  2. // An inglorious hack that munges URLs embedded in outgoing ASP pages,
  3. // embedding the ASPSESSIONID cookie in them.  Also useful as an example of
  4. // an ISAPI filter that does something non-trivial with rawdata.
  5.  
  6.  
  7. #include "CkyPch.h"
  8.  
  9. #include "debug.h"
  10. #include "isapiflt.h"
  11. #include "utils.h"
  12. #include "notify.h"
  13. #include "filter.h"
  14. #include "globals.h"
  15. #include "keyword.h"
  16.  
  17.  
  18. #define MAJOR_VERSION 1
  19. #define MINOR_VERSION 1
  20.  
  21. // the munging mode.  This will be read from the registry upon initialization
  22. int    g_mungeMode = MungeMode_Off;
  23.  
  24. // the session ID size is either 16 or 24 chars (depends on the server version)
  25. long g_SessionIDSize=-1;
  26.  
  27. // the "cookie extra" is the string appended after "ASPSESSIONID" and before
  28. // "=".  It is actually the process ID (with some extra mangling)
  29. static volatile long        g_fCookieExtraSet = 0;
  30. static CRITICAL_SECTION     g_csCookieExtra;
  31. CHAR                        g_szCookieExtra[ COOKIE_NAME_EXTRA_SIZE + 1 ];
  32.  
  33.  
  34. static LPCTSTR szRegKey = "Software\\Microsoft\\CkyMunge";
  35. static LPCTSTR szRegValue = "MungeMode";
  36.  
  37. void SetSessionIDSize( HTTP_FILTER_CONTEXT* );
  38. void SetCookieExtra( LPCSTR );
  39. bool IsValidCookieExtra( LPCSTR );
  40.  
  41. //
  42. // Optional entry/exit point for all DLLs
  43. //
  44.  
  45. extern "C"
  46. BOOL
  47. WINAPI
  48. DllMain(
  49.     HINSTANCE /*hInstance*/,
  50.     DWORD     dwReason,
  51.     LPVOID    /*lpReserved*/)
  52. {
  53.     if (dwReason == DLL_PROCESS_ATTACH)
  54.     {
  55.         DEBUG_INIT();
  56.         TRACE("%s starting up\n", EVENT_MODULE);
  57.  
  58.         // get the munge mode from the registry
  59.         HKEY hKey;
  60.         DWORD dwDisposition;
  61.         if ( ::RegCreateKeyEx(
  62.             HKEY_LOCAL_MACHINE,
  63.             szRegKey,
  64.             0,
  65.             REG_NONE,
  66.             0,
  67.             KEY_ALL_ACCESS,
  68.             NULL,
  69.             &hKey,
  70.             &dwDisposition ) != ERROR_SUCCESS )
  71.         {
  72.             TRACE( "Couldn't create/open key in registry\n" );
  73.             return FALSE;
  74.         }
  75.  
  76.         DWORD dwType;
  77.         DWORD dwValue;
  78.         DWORD dwBufferSize = sizeof( dwValue );
  79.  
  80.         if ( ::RegQueryValueEx(
  81.             hKey,
  82.             szRegValue,
  83.             NULL,
  84.             &dwType,
  85.             reinterpret_cast<BYTE*>(&dwValue),
  86.             &dwBufferSize ) != ERROR_SUCCESS )
  87.         {
  88.             TRACE( "No munge mode set, defaulting to smart mode\n" );
  89.             dwValue = MungeMode_Smart;
  90.  
  91.             ::RegSetValueEx(
  92.                 hKey,
  93.                 szRegValue,
  94.                 0,
  95.                 REG_DWORD,
  96.                 reinterpret_cast<BYTE*>(&dwValue),
  97.                 sizeof( DWORD ) );
  98.         }
  99.                 
  100.         g_mungeMode = static_cast<int>( dwValue );
  101.         TRACE("MungeMode = %d\n", g_mungeMode);
  102.  
  103.         if (! InitUtils()  ||  ! InitKeywords())
  104.             return FALSE;
  105.  
  106.         g_szCookieExtra[0]='\0';
  107.         ::InitializeCriticalSection( &g_csCookieExtra );
  108.  
  109.     }
  110.     else if (dwReason == DLL_PROCESS_DETACH)
  111.     {
  112.         ::DeleteCriticalSection( &g_csCookieExtra );
  113.  
  114.         if (! TerminateUtils()  ||  ! TerminateKeywords())
  115.             return FALSE;
  116.         TRACE("%s shutting down\n", EVENT_MODULE);
  117.         DEBUG_TERM();
  118.     }
  119.  
  120.     return TRUE;    // ok
  121. }
  122.  
  123.  
  124.  
  125. //
  126. // Required initialization entrypoint for all ISAPI filters
  127. //
  128.  
  129. BOOL
  130. WINAPI
  131. GetFilterVersion(
  132.     HTTP_FILTER_VERSION* pVer)
  133. {
  134.     EventReport("", "", EVENTLOG_INFORMATION_TYPE, CMFI_LOADED); 
  135.  
  136.     pVer->dwFilterVersion = HTTP_FILTER_REVISION;
  137.  
  138.     //  Specify the types and order of notification
  139.     pVer->dwFlags = ((SF_NOTIFY_SECURE_PORT  | SF_NOTIFY_NONSECURE_PORT)
  140.                      | SF_NOTIFY_ORDER_MEDIUM
  141.                      | SF_NOTIFY_PREPROC_HEADERS
  142.                      | SF_NOTIFY_URL_MAP
  143.                      | SF_NOTIFY_SEND_RAW_DATA
  144.                      | SF_NOTIFY_END_OF_REQUEST
  145.                      );
  146.  
  147.     // Set the filter description
  148.     wsprintf(pVer->lpszFilterDesc,
  149.              "Active Server Pages ISAPI filter for munging ASPSESSIONID "
  150.              "cookies, v%d.%02d",
  151.              MAJOR_VERSION, MINOR_VERSION);
  152.     TRACE("%s\n", pVer->lpszFilterDesc);
  153.  
  154.     return TRUE;
  155. }
  156.  
  157.  
  158.  
  159. //
  160. // Required dispatch entrypoint for all ISAPI filters
  161. //
  162.  
  163. DWORD
  164. WINAPI
  165. HttpFilterProc(
  166.     HTTP_FILTER_CONTEXT* pfc,
  167.     DWORD                dwNotificationType,
  168.     VOID*                pvData)
  169. {
  170.     // first verify the session ID size
  171.     if ( g_SessionIDSize == -1 )
  172.     {
  173.         SetSessionIDSize( pfc );
  174.     }
  175.  
  176.      if ( g_mungeMode == MungeMode_Off )
  177.     {
  178.         // just get out as quick as possible
  179.         return SF_STATUS_REQ_NEXT_NOTIFICATION;
  180.     }
  181.  
  182.     CNotification* pNotify = CNotification::Get(pfc);
  183.     if ( pNotify != NULL )
  184.     {
  185.         if ( pNotify->MungingOff() )
  186.         {
  187.             // we must've figured out that the browser is
  188.             // accepting cookies.
  189.             return SF_STATUS_REQ_NEXT_NOTIFICATION;
  190.         }
  191.     }
  192.  
  193.     switch (dwNotificationType)
  194.     {
  195.     case SF_NOTIFY_READ_RAW_DATA:
  196.         return OnReadRawData(pfc,    (PHTTP_FILTER_RAW_DATA) pvData);
  197.  
  198.     case SF_NOTIFY_PREPROC_HEADERS:
  199.         return OnPreprocHeaders(pfc, (PHTTP_FILTER_PREPROC_HEADERS) pvData);
  200.  
  201.     case SF_NOTIFY_URL_MAP:
  202.         return OnUrlMap(pfc,         (PHTTP_FILTER_URL_MAP) pvData);
  203.  
  204.     case SF_NOTIFY_AUTHENTICATION:
  205.         return OnAuthentication(pfc, (PHTTP_FILTER_AUTHENT) pvData);
  206.  
  207.     case SF_NOTIFY_ACCESS_DENIED:
  208.         return OnAccessDenied(pfc,   (PHTTP_FILTER_ACCESS_DENIED) pvData);
  209.  
  210.     case SF_NOTIFY_SEND_RAW_DATA:
  211.         return OnSendRawData(pfc,    (PHTTP_FILTER_RAW_DATA) pvData);
  212.  
  213.     case SF_NOTIFY_END_OF_REQUEST:
  214.         return OnEndOfRequest(pfc);
  215.  
  216.     case SF_NOTIFY_LOG:
  217.         return OnLog(pfc,            (PHTTP_FILTER_LOG) pvData);
  218.  
  219.     case SF_NOTIFY_END_OF_NET_SESSION:
  220.         return OnEndOfNetSession(pfc);
  221.         
  222.     default:
  223.         TRACE("Unknown notification: %x, context: %p, data: %p\n",
  224.               dwNotificationType, pfc, pvData);
  225.         return SF_STATUS_REQ_NEXT_NOTIFICATION;
  226.     }
  227. }
  228.  
  229.  
  230.  
  231. //
  232. // Read raw data from the client (browser)
  233. //
  234.  
  235. DWORD
  236. OnReadRawData(
  237.     PHTTP_FILTER_CONTEXT  pfc,
  238.     PHTTP_FILTER_RAW_DATA pRawData)
  239. {
  240.     TRACE("OnReadRawData(%p, %p)\n", pfc, pRawData);
  241.  
  242.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  243. }
  244.  
  245.  
  246.  
  247. //
  248. // Preprocess the headers of the client's request before the server handles
  249. // the request
  250. //
  251.  
  252. DWORD
  253. OnPreprocHeaders(
  254.     PHTTP_FILTER_CONTEXT         pfc,
  255.     PHTTP_FILTER_PREPROC_HEADERS pHeaders)
  256. {
  257.     TRACE("OnPreprocHeaders(%p)\n", pfc, pHeaders);
  258.  
  259.     CHAR  szUrl[1024*5];
  260.     DWORD cbUrl = sizeof szUrl;
  261.  
  262.     // Get the URL for this request
  263.     if (! pHeaders->GetHeader(pfc, "url", szUrl, &cbUrl))
  264.     {
  265.         TRACE("GetHeader(\"url\") failed\n");
  266.         EventReport("OnPreprocHeaders", "url",
  267.                     EVENTLOG_ERROR_TYPE, CMFE_GETHEADER);
  268.         return SF_STATUS_REQ_ERROR;
  269.     }
  270.  
  271.     CNotification* pNotify = NULL;
  272.     CHAR szSessionID[ MAX_SESSION_ID_SIZE + 1 ];
  273.     *szSessionID = '\0';
  274.  
  275.     // Does the URL contain an embedded Session ID, such as
  276.     // /foo/bar.asp-ASP=PVZQGHUMEAYAHMFV ?
  277.  
  278.     if (DecodeURL(szUrl, szSessionID))
  279.     {
  280.         pNotify = CNotification::SetSessionID(pfc, szSessionID);
  281.  
  282.         // Set the URL to one without the Session ID
  283.         if (!pHeaders->SetHeader(pfc, "url", szUrl))
  284.         {
  285.             TRACE("Failed to set Url header!\n", szUrl);
  286.             EventReport("OnPreprocHeaders", szUrl,
  287.                         EVENTLOG_ERROR_TYPE, CMFE_SETHEADER);
  288.             return SF_STATUS_REQ_ERROR;
  289.         }
  290.     }
  291.  
  292.  
  293.     // Look for a "Cookie:" header
  294.  
  295.     CHAR  szCookie[1024*4];
  296.     DWORD cbCookie = sizeof szCookie;
  297.     BOOL  fCookie = pHeaders->GetHeader(pfc, "Cookie:", szCookie, &cbCookie);
  298.  
  299.     if (fCookie  &&  cbCookie > 0)
  300.     {
  301.         TRACE("Cookie: %s\n", szCookie);
  302.         // if the Cookie header includes ASPSESSIONID=<whatever>, use that
  303.         pNotify = CNotification::SetSessionID(pfc, szCookie);
  304.  
  305.         // got a header with a cookie, so don't munge anymore
  306.         if ( pNotify )
  307.         {
  308.             TRACE( "Cookies accepted: stop munging\n" );
  309.             pNotify->m_fTestCookies = false;
  310.         }
  311.     }
  312.     else if (pNotify != NULL  &&  *szSessionID != '\0')
  313.     {
  314.         // No cookie header, so we synthesize an ASPSESSIONID cookie header
  315.         // from the Session ID embedded in the URL
  316.         CHAR sz[ SZ_SESSION_ID_COOKIE_NAME_SIZE + MAX_SESSION_ID_SIZE
  317.                 + 1 + COOKIE_NAME_EXTRA_SIZE + 1];
  318.         wsprintf(sz, "%s%s=%s", SZ_SESSION_ID_COOKIE_NAME,
  319.                  g_szCookieExtra, szSessionID);
  320.         TRACE("About to AddHeader(\"%s\")\n", sz);
  321.  
  322.         if (!pHeaders->AddHeader(pfc, "Cookie:", sz))
  323.         {
  324.             TRACE("Failed to AddHeader(\"Cookie:\", %s)\n", sz);
  325.             EventReport("OnPreprocHeaders", szUrl,
  326.                         EVENTLOG_ERROR_TYPE, CMFE_ADDHEADER);
  327.             return SF_STATUS_REQ_ERROR;
  328.         }
  329.  
  330.         // if we were testing cookies, we now know that the browser isn't
  331.         // sending any to us, so start munging
  332.         if ( pNotify->m_fTestCookies )
  333.         {
  334.             TRACE( "Cookies not accepted: continue munging\n" );
  335.             pNotify->m_fTestCookies = false;
  336.             pNotify->m_fEatCookies = true;
  337.         }
  338.     }
  339.  
  340.     // Kill the "Connection: Keep-alive" header, so that browser will
  341.     // terminate session properly.  If it's present, the server
  342.     // will send back a "Connection: Keep-Alive" header in response to
  343.     // requests for .htm pages (though not for .asp pages).  The
  344.     // browser will think that there's more data to come, when
  345.     // there's not, and it will show an hourglass cursor and eventually
  346.     // put up an error messagebox.
  347.  
  348.     BOOL fKeepAlive = pHeaders->SetHeader(pfc, "Connection:", "");
  349.  
  350.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  351. }
  352.  
  353.  
  354.  
  355. //
  356. // We have mapped the URL to the corresponding physical file
  357. //
  358.  
  359. DWORD
  360. OnUrlMap(
  361.     PHTTP_FILTER_CONTEXT pfc,
  362.     PHTTP_FILTER_URL_MAP pMapInfo)
  363. {
  364.     TRACE("OnUrlMap(%p, %p, %s)\n", pfc, pMapInfo, pMapInfo->pszURL);
  365.  
  366.     // Can we safely ignore this URL based on its MIME type (e.g., image/gif)?
  367.     if (IsIgnorableUrl(pMapInfo->pszURL))
  368.     {
  369.         CNotification::Destroy(pfc);
  370.         TRACE("Ignoring <%s>\n", pMapInfo->pszURL);
  371.     }
  372.     else
  373.     {
  374.         CNotification* pNotify = CNotification::Get(pfc);
  375.  
  376.         if (pNotify == NULL)
  377.             pNotify = CNotification::Create(pfc, NULL);
  378.  
  379.         pNotify->m_nState = HN_SEEN_URL;
  380.     }
  381.  
  382.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  383. }
  384.  
  385.  
  386.  
  387. //
  388. // Authenticating the user
  389. //
  390.  
  391. DWORD
  392. OnAuthentication(
  393.     PHTTP_FILTER_CONTEXT pfc,
  394.     PHTTP_FILTER_AUTHENT pAuthent)
  395. {
  396.     TRACE("OnAuthentication(%p, %p)\n", pfc, pAuthent);
  397.  
  398.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  399. }
  400.  
  401.  
  402.  
  403. //
  404. // Authentication failed
  405. //
  406.  
  407. DWORD
  408. OnAccessDenied(
  409.     PHTTP_FILTER_CONTEXT       pfc,
  410.     PHTTP_FILTER_ACCESS_DENIED pAccess)
  411. {
  412.     TRACE("OnAccessDenied(%p, %p)\n", pfc, pAccess);
  413.  
  414.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  415. }
  416.  
  417.  
  418.  
  419. //
  420. // Do the hard work of munging the data.  Note that we may be in one of
  421. // three interesting states: HN_SEEN_URL (initially), HN_IN_HEADER (looking
  422. // at the outgoing HTTP headers), and HN_IN_BODY (in the body of the
  423. // response).  If the browser has cached this URL already (not the case
  424. // with .ASP pages, but typically the case with ordinary HTML pages or
  425. // images), no body is sent and we never move into the HN_IN_BODY state.
  426. //
  427. // The data will be sent in one or more packets, and we may need to buffer
  428. // portions of those packets, as tokens may be split across two or more
  429. // packets.  The code assumes that an individual header will not be split
  430. // across packets.
  431. //
  432.  
  433. DWORD
  434. OnSendRawData(
  435.     PHTTP_FILTER_CONTEXT  pfc,
  436.     PHTTP_FILTER_RAW_DATA pRawData)
  437. {
  438.     TRACE("OnSendRawData(%p, %p)\n", pfc, pRawData);
  439.  
  440.    CNotification* pNotify = CNotification::Get(pfc);
  441.  
  442.     if (pNotify == NULL  ||  pNotify->m_nState == HN_UNDEFINED)
  443.         return SF_STATUS_REQ_NEXT_NOTIFICATION;
  444.  
  445.     if ( pNotify->MungingOff() )
  446.     {
  447.         // either munging has been turned off, or we detected that
  448.         // munging isn't necessary
  449.         return SF_STATUS_REQ_NEXT_NOTIFICATION;
  450.     }
  451.  
  452.     LPSTR   pszData = (LPSTR) pRawData->pvInData;
  453.     int     iStart = 0; // offset of the beginning of the body data
  454.     
  455.     // first time in OnSendRawData?
  456.     if (pNotify->m_nState == HN_SEEN_URL)
  457.     {
  458.         // Assume Content-Type header is in first packet
  459.         LPCSTR pszContentType = FindHeaderValue("Content-Type:", "text/html",
  460.                                                 pRawData, 0);
  461.  
  462.         if (pszContentType == NULL)
  463.         {
  464.             pNotify->m_nState = HN_UNDEFINED;   // not HTML; ignore
  465.             return SF_STATUS_REQ_NEXT_NOTIFICATION;
  466.         }
  467.         else
  468.         {
  469.             pNotify->m_nState = HN_IN_HEADER;
  470.             pNotify->m_ct = CT_TEXT_HTML;
  471.         }
  472.     }
  473.  
  474.     if (pNotify->m_nState == HN_IN_HEADER)
  475.     {
  476.         static const char szSetCookie[] = "Set-Cookie:";
  477.         LPSTR pszCookie = FindString(szSetCookie, pRawData, 0);
  478.         
  479.         // multiple Set-Cookie headers may be present in the header
  480.         while (pszCookie != NULL)
  481.         {
  482.             pNotify->m_nState = HN_IN_HEADER;
  483.             
  484.             // Header lines are supposed to be terminated by "\r\n"
  485.             LPSTR pszEoln = strchr(pszCookie, '\r');
  486.             
  487.             if (pszEoln != NULL)
  488.             {
  489.                 *pszEoln = '\0';
  490.                 TRACE("%s\n", pszCookie);
  491.                 
  492.                 // ASP only sends the ASPSESSIONID cookie if a session ID
  493.                 // hasn't already been picked, which happens either when
  494.                 // Session_OnStart is executed (if it and global.asa are
  495.                 // present) or when the Session object is first modified
  496.                 // by user code.
  497.                 LPCSTR szCookieName;
  498.                 if ( ( szCookieName =
  499.                        strstr(pszCookie, SZ_SESSION_ID_COOKIE_NAME) ) != NULL)
  500.                 {
  501.                     // need to figure out what's tacked onto the cookie name.
  502.                     if ( !g_fCookieExtraSet )
  503.                     {
  504.                         SetCookieExtra( szCookieName );
  505.                     }
  506.                     
  507.                     // we know this cookie contains ASPSESSIONID, but is it
  508.                     // ours? (the cookie extra parts must match)
  509.                     if ( strstr( szCookieName, g_szCookieExtra ) != NULL )
  510.                     {
  511.                         VERIFY(CNotification::SetSessionID(pfc, pszCookie)
  512.                                == pNotify);
  513.                         TRACE("Update: %s\n", pNotify->SessionID());
  514.                         
  515.                         *pszEoln = '\r';    // restore
  516.                         
  517.                         // Eat outgoing "Set-Cookie: ASPSESSIONIDXXXXXXXX=..."
  518.                         // header?  Benign for cookie-less browsers; will keep
  519.                         // cookie-warning browsers quiet.
  520.                         if (pNotify->m_fEatCookies)
  521.                         {
  522.                             TRACE("Deleting cookie\n");
  523.                             DeleteLine(szSetCookie, pRawData, pszCookie);
  524.                         }
  525.                     }
  526.                 }
  527.                 else
  528.                 {
  529.                     *pszEoln = '\r';    // restore
  530.                 }
  531.                 
  532.                 pszCookie =
  533.                     FindString(szSetCookie, pRawData,
  534.                             pszEoln - static_cast<LPCSTR>(pRawData->pvInData));
  535.             }
  536.             else
  537.             {
  538.                 pszCookie = NULL; // terminate loop
  539.             }
  540.         }
  541.  
  542.         // If a Content-Length header is present, we need to destroy it
  543.         // because there is no way we can guess a priori how much longer
  544.         // the data will become.  If we don't destroy it, the browser will
  545.         // believe the header and become very confused.
  546.         
  547.         static const char szContentLength[] = "Content-Length:";
  548.         LPSTR pszCL = FindString(szContentLength, pRawData, 0);
  549.  
  550.         if (pszCL != NULL)
  551.         {
  552.             char szFmt[ARRAYSIZE(szContentLength) + 10];
  553.             sprintf(szFmt, "%s %%u", szContentLength);
  554.             sscanf(pszCL, szFmt, &pNotify->m_cchContentLength);
  555.             TRACE("%s is %u\n", szContentLength, pNotify->m_cchContentLength);
  556.             DeleteLine(szContentLength, pRawData, pszCL);
  557.         }
  558.  
  559.         // Is the end-of-headers marker present?
  560.         LPCSTR pszEndHeaderBlock = FindString("\n\r\n", pRawData, 0);
  561.  
  562.         if (pszEndHeaderBlock != NULL)
  563.         {
  564.             pNotify->m_nState = HN_IN_BODY;
  565.             iStart = pszEndHeaderBlock + 3 - pszData;
  566.         }
  567.     }
  568.  
  569.     // We're in the body.  Let's do some real work.
  570.  
  571.     if (pNotify->m_nState == HN_IN_BODY)
  572.     {
  573.         LPSTR pszBuf = pszData;
  574.         int iEnd;
  575.  
  576.         // Have we got a partial line from the last packet?  If so, it
  577.         // means that the last character in the buffer on that packet was
  578.         // not a token boundary character, such as '\n' or '>'.  Prepend
  579.         // that data to the current batch.
  580.         
  581.         if (pNotify->m_pbPartialToken != NULL)
  582.         {
  583.             ASSERT(iStart == 0);
  584.             ASSERT(pNotify->m_cbPartialToken > 0);
  585.  
  586.             pNotify->AppendToken(pfc, pszBuf, pRawData->cbInData);
  587.  
  588.             pRawData->pvInData = pszBuf = (LPSTR) pNotify->m_pbPartialToken;
  589.             pRawData->cbInData = pRawData->cbInBuffer
  590.                 = pNotify->m_cbPartialToken;
  591.                 
  592.             iEnd = g_trie.EndOfBuffer(pRawData, iStart);
  593.  
  594.             if (iEnd < 0)
  595.             {
  596.                 // Don't let IIS send any data on this pass
  597.                 pRawData->pvInData = NULL;
  598.                 pRawData->cbInData = pRawData->cbInBuffer = 0;
  599.  
  600.                 return SF_STATUS_REQ_NEXT_NOTIFICATION;
  601.             }
  602.             else
  603.             {
  604.                 // Have a complete token
  605.                 pNotify->m_pbPartialToken = NULL;
  606.                 pNotify->m_cbPartialToken = 0;
  607.             }
  608.         }
  609.  
  610.         ASSERT(pNotify->m_pbPartialToken == NULL
  611.                &&  pNotify->m_cbPartialToken == 0);
  612.  
  613.         // Is the last token in the block incomplete?
  614.         iEnd = g_trie.EndOfBuffer(pRawData, iStart);
  615.  
  616.         if (iEnd != pRawData->cbInData)
  617.         {
  618.             LPSTR pszBoln = (iEnd < 0)  ?  pszBuf + iStart  :  pszBuf + iEnd ;
  619.  
  620.             TRACE("Partial Token: ");
  621.             pNotify->AppendToken(pfc, pszBoln,
  622.                                  (pszBuf + pRawData->cbInData) - pszBoln);
  623.             pRawData->cbInData -= pNotify->m_cbPartialToken;
  624.         }
  625.  
  626.         pNotify->m_cchContentLength -= pRawData->cbInData;
  627.  
  628.         // Filter whatever is left
  629.         const int nExtra = Filter(pfc, pRawData, pszBuf,
  630.                                   pRawData->cbInData, iStart,
  631.                                   pNotify->m_szSessionID);
  632.     }
  633.  
  634.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  635. }
  636.  
  637.  
  638.  
  639. //
  640. // The transaction is over.  Pump out any remaining data before the
  641. // connection closes.
  642. //
  643.  
  644. DWORD
  645. OnEndOfRequest(
  646.     PHTTP_FILTER_CONTEXT pfc)
  647. {
  648.     TRACE("OnEndOfRequest(%p)\n", pfc);
  649.  
  650.     CNotification* pNotify = CNotification::Get(pfc);
  651.  
  652.     if (pNotify != NULL  &&  pNotify->m_pbPartialToken != NULL)
  653.     {
  654.         // append a '\n', which is guaranteed to be a token boundary char
  655.         pNotify->m_pbPartialToken[pNotify->m_cbPartialToken] = '\n';
  656. #ifdef _DEBUG
  657.         pNotify->m_pbPartialToken[pNotify->m_cbPartialToken + 1] = '\0';
  658. #endif
  659.         LPBYTE pbTemp = pNotify->m_pbPartialToken;
  660.         DWORD  cb = pNotify->m_cbPartialToken + 1;
  661.  
  662.         // Note: WriteClient ends up calling OnSendRawData.  Destroy the
  663.         // partial token before it's called.
  664.         pNotify->m_pbPartialToken = NULL;
  665.         pNotify->m_cbPartialToken = 0;
  666.  
  667.         if (!pfc->WriteClient(pfc, pbTemp, &cb, 0))
  668.             TRACE("WriteClient failed, err %x.\n", GetLastError());
  669.     }
  670.  
  671.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  672. }
  673.  
  674.  
  675.  
  676. //
  677. // Log the details of the transaction
  678. //
  679.  
  680. DWORD
  681. OnLog(
  682.     PHTTP_FILTER_CONTEXT pfc,
  683.     PHTTP_FILTER_LOG     pLog)
  684. {
  685.     TRACE("OnLog(%p, %p)\n", pfc, pLog);
  686.  
  687.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  688. }
  689.  
  690.  
  691.  
  692. //
  693. // The HTTP session (transaction) is over and the connection has been closed.
  694. //
  695.  
  696. DWORD
  697. OnEndOfNetSession(
  698.     PHTTP_FILTER_CONTEXT pfc)
  699. {
  700.     TRACE("OnEndOfNetSession(%p)\n", pfc);
  701.  
  702.     CNotification::Destroy(pfc);
  703.  
  704.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  705. }
  706.  
  707.  
  708.  
  709. void
  710. SetSessionIDSize(
  711.     PHTTP_FILTER_CONTEXT pfc )
  712. {
  713.     static const char szVersion4B2[] = "Microsoft-IIS/4.0 Beta 2";
  714.     static const char szVersion4[]   = "Microsoft-IIS/4.0";
  715.     const long version3Size = 16;
  716.     const long version4Size = MAX_SESSION_ID_SIZE;
  717.     const long version4B2Size = 16;
  718.  
  719.     long size = version3Size;
  720.  
  721.     DWORD dwBufferSize = 0;
  722.     pfc->GetServerVariable( pfc, "SERVER_SOFTWARE", NULL, &dwBufferSize );
  723.     if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  724.     {
  725.         LPSTR str = (LPSTR)_alloca( ++dwBufferSize );
  726.         if (pfc->GetServerVariable(pfc, "SERVER_SOFTWARE", str, &dwBufferSize))
  727.         {
  728.             TRACE( "Server Software: %s\n", str );
  729.             if ( strcmp( szVersion4B2, str ) == 0 )
  730.             {
  731.                 TRACE( "Using version 4 beta 2 session ID size (%d bytes)\n",
  732.                        version4B2Size );
  733.                 size = version4B2Size;
  734.             }
  735.             else if ( strncmp( szVersion4, str, strlen( szVersion4 ) ) == 0 )
  736.             {
  737.                 TRACE( "Using version 4 session ID size (%d bytes)\n",
  738.                        version4Size );
  739.                 size = version4Size;
  740.             }
  741.             else
  742.             {
  743.                 TRACE( "Using version 3 session ID size (%d bytes)\n",
  744.                        version3Size );
  745.             }
  746.         }
  747.         else
  748.         {
  749.             TRACE( "Failed to get server variable, error: %d\n",
  750.                    ::GetLastError() );
  751.         }
  752.     }
  753.     else
  754.     {
  755.         TRACE( "Failed to get server variable(SERVER_SOFTWARE), error: %d\n",
  756.                ::GetLastError() );
  757.     }
  758.     ::InterlockedExchange( &g_SessionIDSize, size );
  759. }
  760.  
  761.  
  762.  
  763. void
  764. SetCookieExtra(
  765.     LPCSTR  szCookieName )
  766. {
  767.     ::EnterCriticalSection( &g_csCookieExtra );
  768.     // need to check again in case the cookie extra was set
  769.     // while we were waiting on the critical section
  770.     if ( !g_fCookieExtraSet )
  771.     {
  772.         szCookieName += SZ_SESSION_ID_COOKIE_NAME_SIZE;
  773.         if ( *szCookieName != '=' )
  774.         {
  775.             CHAR szExtra[ COOKIE_NAME_EXTRA_SIZE + 1 ];
  776.             strncpy( szExtra, szCookieName, COOKIE_NAME_EXTRA_SIZE );
  777.             szExtra[ COOKIE_NAME_EXTRA_SIZE ] = 0;
  778.  
  779.             if ( IsValidCookieExtra( szExtra ) )
  780.             {
  781.                 // copy the cookie name extra
  782.                 strcpy( g_szCookieExtra, szExtra );
  783.                 TRACE("SetCookieExtra(%s)\n", g_szCookieExtra);
  784.                 ::InterlockedExchange( (long*)&g_fCookieExtraSet, 1 );
  785.             }
  786.             else
  787.             {
  788.                 TRACE( "Cookie extra validation failed\n" );
  789.             }
  790.         }
  791.     }
  792.     ::LeaveCriticalSection( &g_csCookieExtra );
  793. }
  794.  
  795.  
  796.  
  797. // Check to see if this `extra part' is a valid value for this server.
  798. // The extra part is derived from the process ID and then randomized
  799. // slightly.  So we can tell if the extra is reasonable based on the
  800. // process ID.
  801. bool
  802. IsValidCookieExtra(
  803.     LPCSTR  szExtra )
  804. {
  805.     bool rc = true;
  806.     
  807.     CHAR szProcessID[ COOKIE_NAME_EXTRA_SIZE ];
  808.  
  809.     // Process ID
  810.     wsprintf(szProcessID, "%08X", GetCurrentProcessId());
  811.  
  812.     // check based on how we know the process ID is munged to the cookie extra
  813.     static const char *pszDigitsToLetters[2] = {"GHIJKLMNOP","QRSTUVWXYZ"};
  814.  
  815.     for (int i = 0; i < COOKIE_NAME_EXTRA_SIZE; i++)
  816.     {
  817.         char cp = szProcessID[i];
  818.         char ce = szExtra[i];
  819.         if ( ( cp >= '0' ) && ( cp <= '9' ) )
  820.         {
  821.             int ndx = cp - '0';
  822.             if ( ( pszDigitsToLetters[0][ndx] == ce )
  823.                  || ( pszDigitsToLetters[1][ndx] == ce ) )
  824.             {
  825.                 // okay, keep checking
  826.             }
  827.             else
  828.             {
  829.                 // no good
  830.                 rc = false;
  831.                 i = COOKIE_NAME_EXTRA_SIZE;
  832.             }
  833.         }
  834.         else
  835.         {
  836.             if ( cp == ce )
  837.             {
  838.                 // okay, keep checking
  839.             }
  840.             else
  841.             {
  842.                 // no good
  843.                 rc = false;
  844.                 i = COOKIE_NAME_EXTRA_SIZE;
  845.             }
  846.         }
  847.     }
  848. #ifdef _DEBUG
  849.     if (!rc)
  850.         TRACE("`%s' is not a valid extra\n", szExtra);
  851. #endif
  852.     return rc;
  853. }
  854.