ISAPI 筛选器(ISAPI filter)导出函数
1、BOOL WINAPI GetFilterVersion( PHTTP_FILTER_VERSION pVer);
该函数是DLL筛选器第一次被加载到站点处理进程时被调用。
2、DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,DWORD notificationType,LPVOID pvNotification);
该函数用于响应注册在 GetFilterVersion 的形参PHTTP_FILTER_VERSION 中的dwFlags通知事件。根据所注册的通知事件进行相应的筛选处理。
3、BOOL WINAPI TerminateFilter(DWORD dwFlags);
该函数是DLL筛选器被站点处理进程卸载时时所调用的处理。
源代码如下所示
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <httpfilt.h>
#include <httpext.h>
#define _CRT_SECURE_NO_WARNINGS
/*
BOOL GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
pVer->dwFlags = (SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_AUTHENTICATION |
SF_NOTIFY_URL_MAP | SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_LOG | SF_NOTIFY_END_OF_NET_SESSION );
pVer->dwFilterVersion = HTTP_FILTER_REVISION;
strcpy(pVer->lpszFilterDesc, "UMFilter ISAPI");
return TRUE;
}
DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData)
{
CFile myFile("G:\\mylist.html", CFile::modeWrite);
myFile.SeekToEnd();
switch (NotificationType) {
case SF_NOTIFY_URL_MAP :
myFile.Write("SF_NOTIFY_URL_MAP",strlen("SF_NOTIFY_URL_MAP>"));
break;
case SF_NOTIFY_PREPROC_HEADERS :
myFile.Write("SF_NOTIFY_PREPROC_HEADERS",strlen("SF_NOTIFY_PREPROC_HEADERS"));
break;
default :
break;
}
myFile.Close();
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
*/
DWORD DoSendResponse(HTTP_FILTER_CONTEXT * pfc,HTTP_FILTER_SEND_RESPONSE * pResponse);
//当ISAPI过滤器加载初始化时 GetFilterVersion 只调用一次
//IIS加载初始化ISAPI过滤器时,创建和填充部分HTTP_FILTER_VERSION结构。然后调用过滤器的GetFilterVersion函数,传递一个指针到新结构作为一个参数
//ISAPI过滤器使用版本信息和描述令牌填充HTTP_FILTER_VERSION结构。更重要的是。可以使用HTTP_FILTER_VERSION接收指定通知事件和声明过滤器通常优先级
BOOL WINAPI __stdcall GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
const char *name = "UMFilter ISAPI";
size_t len = strlen(name) + 1;
//SF_NOTIFY_ORDER_LOW:低优先级
//SF_NOTIFY_SEND_RESPONSE:在IIS处理请求之后,但在将任何标头发送回客户端之前发生
//dwFlags:包含一些标志,这些标志指示应通知过滤器的通知事件类型以及过滤器的优先级。下表列出了有效的位掩码。
//dwFilterVersion:ISAPI筛选器使用的ISAPI的版本号。可以使用httpfilt.h头文件中的HTTP_FILTER_REVISION定义来设置过滤器使用的版本。
pVer->dwFlags = ( SF_NOTIFY_ORDER_LOW | SF_NOTIFY_SEND_RESPONSE );
//dwFilterVersion:ISAPI筛选器使用的ISAPI的版本号。可以使用httpfilt.h头文件中的HTTP_FILTER_REVISION定义来设置过滤器使用的版本。
pVer->dwFilterVersion = HTTP_FILTER_REVISION;
//lpszFilterDesc:指向以空值结尾的字符串,该字符串提供了ISAPI筛选器的简短描述
//errno_t strncpy_s(
// char *strDest,
// size_t numberOfElements,
// const char *strSource,
// size_t count
//);
strncpy_s(pVer->lpszFilterDesc, len, "UMFilter ISAPI", len);
return TRUE;
}
#pragma warning(suppress: 28251)
//该GetExtensionVersion中的功能是在IIS中的第一入口点函数。此功能允许您的ISAPI扩展在IIS中注册其版本信息。
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
//dwExtensionVersion:扩展的版本
//typedef struct _HSE_VERSION_INFO {
// DWORD dwExtensionVersion;
// CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];
//} HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
pVer->dwExtensionVersion = HSE_VERSION;
//_TRUNCATE 或 (size – 1): 表示截断
strncpy_s(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN,
"UMFilter ISAPI Extension", _TRUNCATE);
return TRUE;
}
//每当发生已为其注册过滤器的通知事件时,IIS就会调用HttpFilterProc入口点函数。IIS使用此功能将信息和控制传递给ISAPI筛选器。
DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, VOID *pvData)
{
//pfc指向与当前活动的HTTP事务关联,HTTP_FILTER_PROC使用此结构来获取有关当前请求的信息
//notificationType:指向一个位掩码,该位掩码指示正在处理的通知事件的类型。以下是有效的事件类型以及可能发生的情况:
//SF_NOTIFY_SEND_RESPONSE:在IIS处理请求之后,但在将任何标头发送回客户端之前发生
switch (NotificationType)
{
case SF_NOTIFY_SEND_RESPONSE :
//ISAPI筛选器在将标头发送到客户端之前立即收到此通知
//在为SF_NOTIFY_SEND_RESPONSE事件注册了过滤器后,HttpFilterProc的pvNotification参数将指向此结构
return DoSendResponse(pfc, (HTTP_FILTER_SEND_RESPONSE *) pvData);
default :
break;
}
//使用SF_STATUS_REQ_NEXT_NOTIFICATION返回值告诉服务器,过滤器已成功地完成使命
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
//设置返回的内容
DWORD DoSendResponse(HTTP_FILTER_CONTEXT * pfc,HTTP_FILTER_SEND_RESPONSE * pResponse)
{
BOOL fServer = TRUE;
DWORD dwServerError;
//SetHeader:指向SetHeader函数,该函数更改或删除标头的值。该功能可用于更改请求行中包含的特殊值
//所述SetHeader可以回调函数用于通过ISAPI滤波器更改或删除的报头的值。该功能可用于更改请求行中包含的特殊值。
//参数
//pfc 指向与当前活动的HTTP事务关联的HTTP_FILTER_CONTEXT结构。
//lpszName指向要更改或删除的标头的名称。
//lpszValue指向标题的新字符串,或指向“ \ 0”(删除标题)
fServer = pResponse->SetHeader(pfc, "UMFilter:", "Enabled");
if ( !fServer )
{
dwServerError = GetLastError();
//pFilterContext:指向过滤器要与此请求关联的任何上下文信息
//HttpStatus:当前的HTTP状态代码
pfc->pFilterContext = (LPVOID)(DWORD64)pResponse->HttpStatus;
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
void WriteContext(EXTENSION_CONTROL_BLOCK *pECB, char *pszFormat, ...)
{
char szBuffer[1024];
//声明一个变量来转换参数列表
va_list arg_ptr;
//va_start,函数名称,读取可变参数的过程其实就是在堆栈中,使用指针,遍历堆栈段中的参数列表,从低地址到高地址一个一个地把参数内容读出来的过程·
va_start(arg_ptr, pszFormat);
//sprintf_s(szInfo, sizeof(szInfo), szFormat, argPtr);
vsprintf_s(szBuffer, 1024, pszFormat, arg_ptr);
//结束变量列表,和va_start成对使用
va_end(arg_ptr);
DWORD dwSize = strlen(szBuffer);
//WriteClient函数将给定缓冲区中存在的数据发送到发出请求的客户端
//参数
//指定响应数据应发送到的客户端的连接标识符,HTTP服务器分配的唯一编号;此数字不应修改
//指向要发送的数据
//lpdwSizeofBuffer:指向DWORD,该DWORD包含来自缓冲区的字节数,该字节数将在进行调用时写入客户端。
//dwSync:指定一个DWORD,该DWORD包含指示如何处理I / O操作的标志
pECB->WriteClient(pECB->ConnID, szBuffer, &dwSize, 0);
}
BOOL ReadContext(EXTENSION_CONTROL_BLOCK *pECB, LPVOID buffer, DWORD bufferSize, LPDWORD length)
{
BOOL result;
*length = bufferSize;
//ReadClient函数从客户端的HTTP请求的主体读取数据。
result = pECB->ReadClient(pECB->ConnID, buffer, length);
WCHAR num[12];
// converts number to string
//_itow_s(connectionId, num, sizeof(num), 10);
//将无符号长整数转换为字符串
//errno_t _ultow_s(
// unsigned long value, 将转换的数字
// wchar_t *str, 字符串结果
// size_t sizeOfstr, str 的大小 (以字节为单位)
// int radix Base of value.
//);
_ultow_s(*length, num, 12, 10);
//OutputDebugString(TEXT("ISAPI: "));
//OutputDebugString(num);
//OutputDebugString(TEXT(" bytes read\n"));
return result;
}
void StartContext(EXTENSION_CONTROL_BLOCK *pECB)
{
WriteContext(pECB, "<html>\r\n<body>\r\n");
}
void EndContext(EXTENSION_CONTROL_BLOCK *pECB)
{
WriteContext(pECB, "</body>\r\n</html>");
}
#pragma warning(suppress: 28251)
//该HttpExtensionProc功能是通过IIS称为ISAPI扩展的主要切入点。它公开了IIS用于访问扩展程序公开的功能的方法。lpECB:指向与当前活动请求关联的EXTENSION_CONTROL_BLOCK数据结构
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
StartContext(pECB);
WriteContext(pECB, "<pre>");
WriteContext(pECB, "%s", pECB->lpbData);
//cbAvailable:字节可用数量(总数的cbTotalBytes)
//字节可用数量(总数的cbTotalBytes)在缓冲由指向lpbData。如果cbTotalBytes与cbAvailable相同,则lpbData变量将指向一个缓冲区,该缓冲区包含客户端发送的所有数据。否则,cbTotalBytes将包含接收到的数据的字节总数。然后,ISAPI扩展将需要使用回调函数ReadClient来读取其余数据(从cbAvailable的偏移量开始)
if (pECB->cbAvailable > pECB->cbTotalBytes) {
// TODO REad buffer
//pECB->ReadClient
}
WriteContext(pECB, "</pre>");
EndContext(pECB);
//该扩展程序已完成处理,服务器应断开客户端连接并释放分配的资源
return HSE_STATUS_SUCCESS;
}