Windows上的OpenVPN如何封装真实IP作为源地址
OpenVPN引入了一个虚拟的网段,进而每个节点都有一个虚拟的IP地址。为了在加入OpenVPN之后保证原有的网络拓扑不更改,必要的做法就是不让虚拟IP跑出VPN节点之外,也就是说虚拟IP地址不能在任何非VPN网段出现,因为在加入OpenVPN之前,根本就没有这些IP地址,现在加入了,也不应该有。或许很多的防火墙会把这种奇怪的地址直接屏蔽掉。
然而当OpenVPN用于Windows平台的时候,问题就来了。由于Windows在发出数据包的时候,其源地址完全是根据路由选择而来的,人们无法干预源地址的选择,一旦数据包需要经由虚拟网卡发出,那么其源地址自然也就成了虚拟IP地址,这样为了保证网络拓扑的无改动,需要在服务端数据包从真实网卡路由出去的口子上作一个SNAT将源地址改回Windows的真实IP地址。这一次的SNAT导致了通信变成了单向的,也就是说只允许Windows客户端通过VPN发起访问,而其它网段无法通过VPN对Windows进行访问。要想将通信变为双向的,就需要再做一次DNAT,一个Windows客户端接入导致了服务端两条NAT规则的添加,这明显是一种修补策略,太撇脚了。
如果能让通信从Windows客户端发起的时候就使用真实IP地址作为源地址,任何问题就解决了。也就是说,如果Windows的路由命令可以支持Linux的iproute2的src参数就好了:
ip route add 1.2.3.4/32 via $VPN服务端虚拟地址 dev tap0 src $客户端真实地址
遗憾的是,Windows不支持这个,即便是Windows 2008也不支持用户手工指定源IP地址。搞技术的碰到这个问题,最显然的想法就是解决它而不是避开它,目标只有一个,那就是不让虚拟IP地址流到VPN网络之外,并且不通过地址转换来做,因为我知道Linux的地址转换是基于流的,要配置就要配两条,很不爽,非Linux的也没有实验环境,最关键的,我就是Linux程序员,术业有专攻。解决这个问题的方法最直接的就是编写NDIS驱动,然而那样动作太大了,并且它只和OpenVPN(目前)有关,因此就想到使用OpenVPN的plugin,注册up事件来做,这是合理的。然而问题是怎么做,由于不熟悉Windows开发环境,也不能恋战,不知怎么突然就想到了LSP,那就顺着这个路子走下去吧,幸亏能走通,否则就又和去年折腾虚拟文件系统时那样一场悲剧了。我只知道LSP可以在标准的Winsock上添加自己的逻辑,就是个钩子链,具体的代码还是需要请教公司的Windows高手以及互联网上查找现有的代码,虽然有时我也浏览MSDN,还是没有百度来的快,对于这种代码,百度比google快。
大概花了5个小时时间,代码调通了,并且很好的和OpenVPN联动,也就是实现了OpenVPN的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成为一个OpenVPN的plugin***********************************************/#define MAX_IP_LEN 16struct 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作为OpenVPN的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,具体以openvpn的header为准# define OPENVPN_EXPORT //需要在.def文件中导出open,func,close函数typedef void *openvpn_plugin_handle_t;#define OPENVPN_PLUGIN_MASK(x) (1<<(x))#define OPENVPN_PLUGIN_UP 0#define OPENVPN_PLUGIN_ROUTE_UP 2#define OPENVPN_PLUGIN_FUNC_SUCCESS 0#define OPENVPN_PLUGIN_FUNC_ERROR 1#define OPENVPN_PLUGIN_DOWN 1//以下函数摘自openvpn的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;}//openvpn的plugin的关键函数,以下的openvpn_plugin_open_v1,//openvpn_plugin_func_v1,openvpn_plugin_close_v1都是要导出的OPENVPN_EXPORT openvpn_plugin_handle_topenvpn_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 = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP); //route_up事件发生时注册LSP。不能在open的时候加载lsp,因此此时虚拟IP地址还不确定呢 //所以必须在up事件或者route-up事件时加载,再此我选择了route-up而不是up,没有特殊原因 *type_mask |= OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP); *type_mask |= OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN); return (openvpn_plugin_handle_t) context;}OPENVPN_EXPORT intopenvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]){ struct plugin_context *context = (struct plugin_context *) handle; int ret = OPENVPN_PLUGIN_FUNC_SUCCESS; char buf[512] = {0}; switch (type) { case OPENVPN_PLUGIN_UP:{ //获取实际IP地址,用于封装源地址 const char *real_addr = get_env ("OPENVPN_clientip", envp); //获取虚拟IP地址,用于判断路由 const char *vitrual_addr = get_env ("ifconfig_local", envp); const char *vitrual_gw = get_env ("route_vpn_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 = OPENVPN_PLUGIN_FUNC_SUCCESS; break; } case OPENVPN_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 = OPENVPN_PLUGIN_FUNC_ERROR; } } else { ret = OPENVPN_PLUGIN_FUNC_ERROR; } break; } case OPENVPN_PLUGIN_DOWN: //虚拟网卡down时,注销LSP if(!RemoveProvider()) ret = OPENVPN_PLUGIN_FUNC_ERROR; break; default: ret = OPENVPN_PLUGIN_FUNC_ERROR; break; } return ret;}OPENVPN_EXPORT voidopenvpn_plugin_close_v1 (openvpn_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(){ //从OpenVPN的up脚本中获取的用于连接OpenVPN服务器的本地网卡的真实IP //更加简便的方式就是将这个DLL既作为LSP,又作为OpenVPN的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 openvpn_plugin_open_v1 openvpn_plugin_func_v1 openvpn_plugin_close_v1 ***********************************************//**********************************************VS2005的编译要点:1.使用unicode2.不使用预编译头3.定义def文件并编译输入***********************************************//**************************************************服务端的client-connect脚本中要有以下的代码:#!/bin/bash......#获取连接而来的客户端的真实IP地址real_addr=$(printenv trusted_ip)#将这个真实IP地址推回客户端以供其获取echo "push setenv-safe clientip $real_addr" >$1***************************************************/