Open×××引入了一个虚拟的网段,进而每个节点都有一个虚拟的IP地址。为了在加入Open×××之后保证原有的网络拓扑不更改,必要的做法就是不让虚拟IP跑出×××节点之外,也就是说虚拟IP地址不能在任何非×××网段出现,因为在加入Open×××之前,根本就没有这些IP地址,现在加入了,也不应该有。或许很多的防火墙会把这种奇怪的地址直接屏蔽掉。
        然而当Open×××用于Windows平台的时候,问题就来了。由于Windows在发出数据包的时候,其源地址完全是根据路由选择而来的,人们无法干预源地址的选择,一旦数据包需要经由虚拟网卡发出,那么其源地址自然也就成了虚拟IP地址,这样为了保证网络拓扑的无改动,需要在服务端数据包从真实网卡路由出去的口子上作一个SNAT将源地址改回Windows的真实IP地址。这一次的SNAT导致了通信变成了单向的,也就是说只允许Windows客户端通过×××发起访问,而其它网段无法通过×××对Windows进行访问。要想将通信变为双向的,就需要再做一次DNAT,一个Windows客户端接入导致了服务端两条NAT规则的添加,这明显是一种修补策略,太撇脚了。
        如果能让通信从Windows客户端发起的时候就使用真实IP地址作为源地址,任何问题就解决了。也就是说,如果Windows的路由命令可以支持Linux的iproute2的src参数就好了:
ip route add 1.2.3.4/32 via $×××服务端虚拟地址 dev tap0 src $客户端真实地址
遗憾的是,Windows不支持这个,即便是Windows 2008也不支持用户手工指定源IP地址。搞技术的碰到这个问题,最显然的想法就是解决它而不是避开它,目标只有一个,那就是不让虚拟IP地址流到×××网络之外,并且不通过地址转换来做,因为我知道Linux的地址转换是基于流的,要配置就要配两条,很不爽,非Linux的也没有实验环境,最关键的,我就是Linux程序员,术业有专攻。解决这个问题的方法最直接的就是编写NDIS驱动,然而那样动作太大了,并且它只和Open×××(目前)有关,因此就想到使用Open×××的plugin,注册up事件来做,这是合理的。然而问题是怎么做,由于不熟悉Windows开发环境,也不能恋战,不知怎么突然就想到了LSP,那就顺着这个路子走下去吧,幸亏能走通,否则就又和去年折腾虚拟文件系统时那样一场悲剧了。我只知道LSP可以在标准的Winsock上添加自己的逻辑,就是个钩子链,具体的代码还是需要请教公司的Windows高手以及互联网上查找现有的代码,虽然有时我也浏览MSDN,还是没有百度来的快,对于这种代码,百度比google快。
        大概花了5个小时时间,代码调通了,并且很好的和Open×××联动,也就是实现了Open×××的plugin,如果一个去往1.2.3.4的包需要走虚拟网卡,本机的物理网卡IP地址11.22.33.44,那么可以完美实现数据包从虚拟网卡发出时,封装11.22.33.44作为源地址,而不是封装虚拟地址128.129.0.3。代码如下:
#include <ws2spi.h> #include <errno.h> #include <fstream> #include <list> #include <Sporder.h>        // 定义了WSCWriteProviderOrder函数 #include <windows.h> #include <stdio.h>  #pragma comment(lib, "Rpcrt4.lib") #pragma   comment(lib,"Ws2_32.lib") GUID filterguid = {0xd3c21122, 0x85e1, 0x48f3, {0x9a,0xb6,0x23,0xd9,0x0c,0x73,0x07,0xef}}; LPWSAPROTOCOL_INFOW  ProtoInfo=NULL; WSPPROC_TABLE        NextProcTable; DWORD                ProtoInfoSize=0; int                  TotalProtos=0;  using namespace std;  //将socket与“是否bind源IP地址”进行关联 typedef struct  {     SOCKET s;     BOOL bd; }sockets; list<sockets*> li;  /*************************************************************************/ /*                            以下安装LSP                                */ /* 代码来自:http://hi.baidu.com/hiliqirun/item/194805be5695c6462bebe3f0 */ /*************************************************************************/ GUID ProviderGuid = {0xd3c21122, 0x85e1, 0x48f3, {0x9a,0xb6,0x23,0xd9,0x0c,0x73,0x07,0xef}}; LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols) {     DWORD dwSize = 0;     int nError;     LPWSAPROTOCOL_INFOW pProtoInfo = NULL;      // 取得需要的长度     if(::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR){         if(nError != WSAENOBUFS)             return NULL;     }      pProtoInfo = (LPWSAPROTOCOL_INFOW)::GlobalAlloc(GPTR, dwSize);     *lpnTotalProtocols = ::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);     return pProtoInfo; } void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo) {     ::GlobalFree(pProtoInfo); } BOOL InstallProvider(WCHAR *pwszPathName) {     WCHAR wszLSPName[] = L"ZetsinLSP";     LPWSAPROTOCOL_INFOW pProtoInfo;     int nProtocols;     WSAPROTOCOL_INFOW OriginalProtocolInfo[3];     DWORD             dwOrigCatalogId[3];     int nArrayCount = 0;     DWORD dwLayeredCatalogId;        // 我们分层协议的目录ID号     int nError;      // 找到我们的下层协议,将信息放入数组中     // 枚举所有服务程序提供者     pProtoInfo = GetProvider(&nProtocols);     BOOL bFindUdp = FALSE;     BOOL bFindTcp = FALSE;     BOOL bFindRaw = FALSE;     for(int i=0; i<nProtocols; i++){         if(pProtoInfo[i].iAddressFamily == AF_INET){             if(!bFindUdp && pProtoInfo[i].iProtocol == IPPROTO_UDP){                 memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                  dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                 bFindUdp = TRUE;             }             if(!bFindTcp && pProtoInfo[i].iProtocol == IPPROTO_TCP){                 memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                  dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                 bFindTcp = TRUE;             }             if(!bFindRaw && pProtoInfo[i].iProtocol == IPPROTO_IP){                 memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));                 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =                     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);                  dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;                 bFindRaw = TRUE;             }         }     }     // 安装我们的分层协议,获取一个dwLayeredCatalogId     // 随便找一个下层协议的结构复制过来即可     WSAPROTOCOL_INFOW LayeredProtocolInfo;     memcpy(&LayeredProtocolInfo, &OriginalProtocolInfo[0], sizeof(WSAPROTOCOL_INFOW));     // 修改协议名称,类型,设置PFL_HIDDEN标志     wcscpy_s(LayeredProtocolInfo.szProtocol, wszLSPName);     LayeredProtocolInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL; // 0;     LayeredProtocolInfo.dwProviderFlags |= PFL_HIDDEN;     // 安装     if(::WSCInstallProvider(&ProviderGuid,         pwszPathName, &LayeredProtocolInfo, 1, &nError) == SOCKET_ERROR){         printf("%d", nError);         return FALSE;     }     // 重新枚举协议,获取分层协议的目录ID号     FreeProvider(pProtoInfo);     pProtoInfo = GetProvider(&nProtocols);     for(int i=0; i<nProtocols; i++){         if(memcmp(&pProtoInfo[i].ProviderId, &ProviderGuid, sizeof(ProviderGuid)) == 0){             dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;             break;         }     }     // 安装协议链     // 修改协议名称,类型     WCHAR wszChainName[WSAPROTOCOL_LEN + 1];     for(int i=0; i<nArrayCount; i++){         swprintf(wszChainName, L"%ws over %ws", wszLSPName, OriginalProtocolInfo[i].szProtocol);         wcscpy_s(OriginalProtocolInfo[i].szProtocol, wszChainName);         if(OriginalProtocolInfo[i].ProtocolChain.ChainLen == 1){             OriginalProtocolInfo[i].ProtocolChain.ChainEntries[1] = dwOrigCatalogId[i];         }         else{             for(int j = OriginalProtocolInfo[i].ProtocolChain.ChainLen; j>0; j--){                 OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j]                 = OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j-1];             }         }         OriginalProtocolInfo[i].ProtocolChain.ChainLen ++;         OriginalProtocolInfo[i].ProtocolChain.ChainEntries[0] = dwLayeredCatalogId;         }     // 获取一个Guid,安装之     GUID ProviderChainGuid;     if(::UuidCreate(&ProviderChainGuid) == RPC_S_OK){         if(::WSCInstallProvider(&ProviderChainGuid,             pwszPathName, OriginalProtocolInfo, nArrayCount, &nError) == SOCKET_ERROR){             return FALSE;             }     }     else         return FALSE;     // 重新排序Winsock目录,将我们的协议链提前     // 重新枚举安装的协议     FreeProvider(pProtoInfo);     pProtoInfo = GetProvider(&nProtocols);     PDWORD dwIds = (PDWORD)malloc(sizeof(DWORD) * nProtocols);     int nIndex = 0;     // 添加我们的协议链     for(int i=0; i<nProtocols; i++){         if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&             (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))             dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;     }     // 添加其它协议     for(int i=0; i<nProtocols; i++){         if((pProtoInfo[i].ProtocolChain.ChainLen <= 1) ||             (pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId))             dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;     }     // 重新排序Winsock目录     if((nError = ::WSCWriteProviderOrder(dwIds, nIndex)) != ERROR_SUCCESS){         return FALSE;     }     FreeProvider(pProtoInfo);     return TRUE; } BOOL RemoveProvider() {     LPWSAPROTOCOL_INFOW pProtoInfo;     int nProtocols;     DWORD dwLayeredCatalogId;     // 根据Guid取得分层协议的目录ID号     pProtoInfo = GetProvider(&nProtocols);     int nError;     int i;     for(i=0; i<nProtocols; i++){         if(memcmp(&ProviderGuid, &pProtoInfo[i].ProviderId, sizeof(ProviderGuid)) == 0){             dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;             break;         }     }     if(i < nProtocols){         // 移除协议链         for(i=0; i<nProtocols; i++){             if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&                 (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId)){                 ::WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError);             }         }          // 移除分层协议         ::WSCDeinstallProvider(&ProviderGuid, &nError);     }     else return FALSE;     return TRUE; }  /**********************************************     以下支持此DLL成为一个Open×××的plugin ***********************************************/ #define MAX_IP_LEN    16 struct plugin_context {     char real_addr[MAX_IP_LEN];     char vitrual_addr[MAX_IP_LEN];     char vitrual_gw[MAX_IP_LEN];     char vitrual_mask[MAX_IP_LEN]; }; //首先要定义一个共享节,因为该DLL作为Open×××的plugin的同时也要作为LSP存在 //需要在两个DLL中共享这个数据 #pragma data_seg (".shared") //数据一定要初始化 struct plugin_context g_context = {0}; TCHAR self[MAX_PATH] = {0}; #pragma   data_seg()        #pragma comment(linker,  " /SECTION:.shared,RWS " )  //这里只是一些个stub,具体以open***的header为准 # define OPEN×××_EXPORT        //需要在.def文件中导出open,func,close函数 typedef void *open***_plugin_handle_t; #define OPEN×××_PLUGIN_MASK(x) (1<<(x)) #define OPEN×××_PLUGIN_UP                    0 #define OPEN×××_PLUGIN_ROUTE_UP              2 #define OPEN×××_PLUGIN_FUNC_SUCCESS  0 #define OPEN×××_PLUGIN_FUNC_ERROR    1 #define OPEN×××_PLUGIN_DOWN                  1  //以下函数摘自open***的plugin的simple实例 static const char * get_env (const char *name, const char *envp[]) {     if (envp){         int i;         size_t namelen = strlen (name);         for (i = 0; envp[i]; ++i){             OutputDebugStringA(envp[i]);             if (!strncmp (envp[i], name, namelen)){                 const char *cp = envp[i] + namelen;                 if (*cp == '=')                     return cp + 1;             }         }     }     return NULL; }  //open***的plugin的关键函数,以下的open***_plugin_open_v1, //open***_plugin_func_v1,open***_plugin_close_v1都是要导出的 OPEN×××_EXPORT open***_plugin_handle_t open***_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) {     struct plugin_context *context;     context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));     //up事件发生时获取环境变量     *type_mask = OPEN×××_PLUGIN_MASK (OPEN×××_PLUGIN_UP);     //route_up事件发生时注册LSP。不能在open的时候加载lsp,因此此时虚拟IP地址还不确定呢     //所以必须在up事件或者route-up事件时加载,再此我选择了route-up而不是up,没有特殊原因     *type_mask |= OPEN×××_PLUGIN_MASK (OPEN×××_PLUGIN_ROUTE_UP);     *type_mask |= OPEN×××_PLUGIN_MASK (OPEN×××_PLUGIN_DOWN);     return (open***_plugin_handle_t) context; } OPEN×××_EXPORT int open***_plugin_func_v1 (open***_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) {     struct plugin_context *context = (struct plugin_context *) handle;     int ret = OPEN×××_PLUGIN_FUNC_SUCCESS;     char buf[512] = {0};     switch (type) {         case OPEN×××_PLUGIN_UP:{             //获取实际IP地址,用于封装源地址             const char *real_addr = get_env ("OPEN×××_clientip", envp);             //获取虚拟IP地址,用于判断路由             const char *vitrual_addr = get_env ("ifconfig_local", envp);             const char *vitrual_gw = get_env ("route_***_gateway", envp);             const char *vitrual_mask = get_env ("ifconfig_netmask", envp);                          real_addr !=NULL?strncpy(context->real_addr, real_addr, strlen(real_addr)):NULL;             vitrual_addr != NULL?strncpy(context->vitrual_addr, vitrual_addr, strlen(vitrual_addr)):NULL;             vitrual_gw != NULL?strncpy(context->vitrual_gw, vitrual_gw, strlen(vitrual_gw)):NULL;             vitrual_mask != NULL?strncpy(context->vitrual_mask, vitrual_mask, strlen(vitrual_mask)):NULL;             memcpy(&g_context, context, sizeof(g_context));             ret = OPEN×××_PLUGIN_FUNC_SUCCESS;             break;         }         case OPEN×××_PLUGIN_ROUTE_UP:{             //服务端推送的路由起来的时候,加载LSP,这个LSP的dll和plugin的dll是同一个             TCHAR szPathName[MAX_PATH];             TCHAR* p;             if(::GetFullPathName(self, MAX_PATH, szPathName, &p) != 0) {                 if(!InstallProvider(szPathName)) {                     ret = OPEN×××_PLUGIN_FUNC_ERROR;                 }             } else {                 ret = OPEN×××_PLUGIN_FUNC_ERROR;             }             break;         }         case OPEN×××_PLUGIN_DOWN:             //虚拟网卡down时,注销LSP             if(!RemoveProvider())                 ret = OPEN×××_PLUGIN_FUNC_ERROR;             break;         default:             ret = OPEN×××_PLUGIN_FUNC_ERROR;             break;     }     return ret; }  OPEN×××_EXPORT void open***_plugin_close_v1 (open***_plugin_handle_t handle) {     struct plugin_context *context = (struct plugin_context *) handle;     free (context); }  /****************************************************************************/ /*                            以下实现LSP                                          */ /*    代码修改自:http://hi.baidu.com/hiliqirun/item/194805be5695c6462bebe3f0    */ /****************************************************************************/  BOOL GetLSP() {     int    errorcode;     ProtoInfo=NULL;     ProtoInfoSize=0;     TotalProtos=0;     if(WSCEnumProtocols(NULL,ProtoInfo,&ProtoInfoSize,&errorcode)==SOCKET_ERROR)         if(errorcode!=WSAENOBUFS)             return FALSE;     if((ProtoInfo=(LPWSAPROTOCOL_INFOW)GlobalAlloc(GPTR,ProtoInfoSize))==NULL)                      return FALSE;     if((TotalProtos=WSCEnumProtocols(NULL,ProtoInfo,&ProtoInfoSize,&errorcode))==SOCKET_ERROR)         return FALSE;     return TRUE; }  void FreeLSP() {     GlobalFree(ProtoInfo); }  unsigned long GetRealAddress() {     //从Open×××的up脚本中获取的用于连接Open×××服务器的本地网卡的真实IP     //更加简便的方式就是将这个DLL既作为LSP,又作为Open×××的plugin,注册up事件     //在up事件发生时,获取真实IP地址,通过全局变量传给GetRealAddress。     //up脚本/plugin中的获取方式:获取环境变量。Linux下的printenv在Windows下的对应物     //这里的地址是在plugin中取到的     char *addr = g_context.real_addr;     //......     return inet_addr(addr); }  BOOL NeedBind(unsigned long addr, sockets *entry) {     //根据目标地址来判断是否需要bind一个本地物理网卡地址     //当且仅当数据包要从Tap-win32发出,也就是路由到这个网卡     //时且本身没有bind任何IP地址时,数据包的源地址才会被被     //设置成虚拟IP,因此需要调用Windows的路由API来查找一下     //目标地址的路由。为了测试方便,一律认为除了广播以及虚拟     //网段本身的所有数据包都从虚拟网卡出去          unsigned long net = inet_addr(g_context.vitrual_mask) & inet_addr(g_context.vitrual_addr);     //GetIpForwardTable ......本来要根据目标地址addr查找系统路由表来决定是不是要     //强制bind一个本地物理网卡的IP的,只因对Windows路由API不熟悉以及人为WinAPI参数     //太繁琐,...留在以后实现     BOOL nd = ((addr & inet_addr(g_context.vitrual_mask)) == net);     BOOL route_res = (!nd) && (addr != 0xffffffff);     BOOL bd = entry->bd;     return route_res & !bd; }  BOOL WINAPI DllMain(HINSTANCE hmodule,                     DWORD     reason,                     LPVOID    lpreserved) {     TCHAR   processname[MAX_PATH];     if(reason==DLL_PROCESS_ATTACH) {         //得到DLL自己的路径         GetModuleFileName(hmodule,processname,MAX_PATH);         wcscpy(self, processname);         OutputDebugString(processname);     }     return TRUE; }  //在原始调用的基础上添加一个链表插入 SOCKET WINAPI WSPSocket(                         __in   int af,                         __in   int type,                         __in   int protocol,                         __in   LPWSAPROTOCOL_INFO lpProtocolInfo,                         __in   GROUP g,                         DWORD dwFlags,                         __out  LPINT lpErrno                         ) {     SOCKET s;     sockets *entry = NULL;     s = NextProcTable.lpWSPSocket(af, type, protocol, lpProtocolInfo, g, dwFlags, lpErrno);     if (s > 0) {         //只要成功创建一个socket,即将其插入list,一起插入的还有它是否已经bind源IP地址         entry = (sockets*)calloc(1, sizeof(sockets));         entry->s = s;         entry->bd = FALSE;         li.push_front(entry);     }     return s; }  //改写原始的connect逻辑,强行绑定 int WSPAPI WSPConnect(                       SOCKET s,                       const struct sockaddr *name,                       int namelen,                       LPWSABUF lpCallerData,                       LPWSABUF lpCalleeData,                       LPQOS lpSQOS,                       LPQOS lpGQOS,                       LPINT lpErrno) {     struct sockaddr_in new_name, *remote_name;     int ret = 0;     sockets *entry = NULL;     list<sockets*>::iterator it;     remote_name = (struct sockaddr_in *)name;     //找到相关的entry,目的是看它是否已经关联了源IP地址     for (it = li.begin(); it != li.end(); it++) {         entry = *it;         if (entry->s == s) {             break;         }     }     //如果还没有bind源IP地址,则为其bind一个本地物理网卡的地址     if (NeedBind(remote_name->sin_addr.s_addr, entry)) {         memset(&new_name, 0, sizeof(struct sockaddr_in));         new_name.sin_family=AF_INET;            new_name.sin_addr.s_addr= GetRealAddress();         new_name.sin_port=htons(0);         ret = NextProcTable.lpWSPBind(s, (struct sockaddr *)&new_name, sizeof(new_name), lpErrno);         if (ret == 0) {             //如果成功bind,则更新该entry的bd字段             entry->bd = TRUE;         }     }     return NextProcTable.lpWSPConnect(s, name, namelen,                         lpCallerData, lpCalleeData, lpSQOS,                         lpGQOS, lpErrno); }  //改写原始的bind逻辑,将绑定0地址的bind行为改为延迟bind,将不帮定源地址的改为严格绑定物理网卡地址 int WINAPI WSPBind(                    __in   SOCKET s,                    __in   const struct sockaddr *name,                    __in   int namelen,                    __out  LPINT lpErrno                    ) {     int ret = 0;     sockets *entry = NULL;     list<sockets*>::iterator it;     //找到对应entry,目的是对其bd字段进行更新     for (it = li.begin(); it != li.end(); it++) {         entry = *it;         if (entry->s == s)             break;     }     //如果一个socket bind了一个非0地址,则直接放过     if (((sockaddr_in*)name)->sin_addr.s_addr == 0) {         //如果一个socket bind了一个0地址,则为其延迟bind一个本地物理网卡的地址         //由于此时还不知道目标地址是什么,无法确定是否需要bind本地物理网卡地址         //故而,更新bd字段后,直接返回0,以下的两行代码不需要         //sockaddr_in *new_name = (sockaddr_in*)name;         //new_name->sin_addr.s_addr = GetRealAddress();         entry->bd = FALSE;         return 0;     }     ret = NextProcTable.lpWSPBind(s, name, namelen, lpErrno);     //如果bind成果,则更新相应entry的bd字段     if (ret == 0)         entry->bd = TRUE;     return ret; }  int WINAPI WSPSendTo(                      __in   SOCKET s,                      __in   LPWSABUF lpBuffers,                      __in   DWORD dwBufferCount,                      __out  LPDWORD lpNumberOfBytesSent,                      __in   DWORD dwFlags,                      __in   const struct sockaddr *lpTo,                      __in   int iTolen,                      __in   LPWSAOVERLAPPED lpOverlapped,                      __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,                      __in   LPWSATHREADID lpThreadId,                      __out  LPINT lpErrno                      ) {     struct sockaddr_in new_name, *remote_name;     int ret = 0;     sockets *entry = NULL;     list<sockets*>::iterator it;     remote_name = (struct sockaddr_in *)lpTo;     //找到对应的entry,目的是处理诸如UDP之类没有bind直接发送的情形     //此类情形下,为了关联一个源IP地址,需要bind一个本地物理网卡地址     for (it = li.begin(); it != li.end(); it++) {         entry = *it;         if (entry->s == s)             break;     }     if (NeedBind(remote_name->sin_addr.s_addr, entry)) {         memset(&new_name, 0, sizeof(struct sockaddr_in));         new_name.sin_family=AF_INET;            new_name.sin_addr.s_addr= GetRealAddress();         new_name.sin_port=htons(0);         ret = NextProcTable.lpWSPBind(s, (struct sockaddr *)&new_name, sizeof(new_name), lpErrno);         if (ret == 0)             entry->bd = TRUE;     }     return NextProcTable.lpWSPSendTo(s, lpBuffers, dwBufferCount,                         lpNumberOfBytesSent, dwFlags, lpTo,                         iTolen, lpOverlapped, lpCompletionRoutine,                         lpThreadId, lpErrno); }  int WINAPI WSPCloseSocket(                           SOCKET s,                           LPINT lpErrno                           ) {     int ret = 0;     sockets *entry = NULL;     list<sockets*>::iterator it;     //找到对应的entry,目的是将其从list删除,并释放数据结构     for (it = li.begin(); it != li.end(); it++) {         entry = *it;         if (entry->s == s)             break;     }     li.remove(entry);     free(entry);     return NextProcTable.lpWSPCloseSocket(s, lpErrno); }  int WSPAPI WSPStartup(                       WORD wversionrequested,                       LPWSPDATA         lpwspdata,                       LPWSAPROTOCOL_INFOW lpProtoInfo,                       WSPUPCALLTABLE upcalltable,                       LPWSPPROC_TABLE lpproctable                       ) {     int           i;     int           errorcode;     int           filterpathlen;     DWORD         layerid=0;     DWORD         nextlayerid=0;     TCHAR         *filterpath;     HINSTANCE     hfilter;     LPWSPSTARTUP  wspstartupfunc=NULL;     if(lpProtoInfo->ProtocolChain.ChainLen<=1)         return FALSE;     GetLSP();     for(i=0;i<TotalProtos;i++){         if(memcmp(&ProtoInfo[i].ProviderId,&filterguid,sizeof(GUID))==0){             layerid=ProtoInfo[i].dwCatalogEntryId;             break;         }     }     for(i=0;i<lpProtoInfo->ProtocolChain.ChainLen;i++){         if(lpProtoInfo->ProtocolChain.ChainEntries[i]==layerid){             nextlayerid=lpProtoInfo->ProtocolChain.ChainEntries[i+1];             break;         }     }     filterpathlen=MAX_PATH;     filterpath=(TCHAR*)GlobalAlloc(GPTR,filterpathlen);       for(i=0;i<TotalProtos;i++) {         if(nextlayerid==ProtoInfo[i].dwCatalogEntryId) {             if(WSCGetProviderPath(&ProtoInfo[i].ProviderId,filterpath,&filterpathlen,&errorcode)  ==SOCKET_ERROR) {                 return WSAEPROVIDERFAILEDINIT;             }             break;         }     }     if(!ExpandEnvironmentStrings(filterpath,filterpath,MAX_PATH))           return WSAEPROVIDERFAILEDINIT;     if((hfilter=LoadLibrary(filterpath))==NULL)         return WSAEPROVIDERFAILEDINIT;     if((wspstartupfunc=(LPWSPSTARTUP)GetProcAddress(hfilter,"WSPStartup"))==NULL)         return WSAEPROVIDERFAILEDINIT;     if((errorcode=wspstartupfunc(wversionrequested,lpwspdata,lpProtoInfo,upcalltable,lpproctable))!=ERROR_SUCCESS)           return errorcode;     NextProcTable=*lpproctable;// 保存原来的入口函数表          //仅下列几个socket函数需要被HOOK,因为这些都涉及到了源地址和目标地址的关系     lpproctable->lpWSPSendTo = WSPSendTo;     lpproctable->lpWSPBind = WSPBind;     lpproctable->lpWSPConnect = WSPConnect;     lpproctable->lpWSPSocket = WSPSocket;     lpproctable->lpWSPCloseSocket = WSPCloseSocket;     FreeLSP();     return 0; }  /**********************************************             以下是本代码的def文件                  EXPORTS         WSPStartup         open***_plugin_open_v1         open***_plugin_func_v1         open***_plugin_close_v1     ***********************************************/  /********************************************** VS2005的编译要点: 1.使用unicode 2.不使用预编译头 3.定义def文件并编译输入 ***********************************************/  /************************************************** 服务端的client-connect脚本中要有以下的代码: #!/bin/bash ...... #获取连接而来的客户端的真实IP地址 real_addr=$(printenv trusted_ip) #将这个真实IP地址推回客户端以供其获取 echo "push setenv-safe clientip $real_addr" >$1 ***************************************************/