创建、连接、挂断、删除VPN实现起来并不难,下面给出一套比较完整的代码。该段代码只是示例代码,但是已经通过了编译,对API的使用和VPN操作步骤是没问题的。具体每个API代表的意义可以参看《C++实现VPN工具之常用API函数》。代码中的OutputString只是起查看消息的作用,当成messagebox理解就行。到此篇总结,VPN工具实现的学习基本上就结束了。
#include "ras.h" #pragma comment(lib, "rasapi32.lib") typedef struct VPNPARAMS { WCHAR szDescription[ RAS_MaxEntryName + 1 ]; WCHAR szServer[ RAS_MaxPhoneNumber + 1 ]; WCHAR szUserName[ UNLEN + 1 ]; WCHAR szPassword[ PWLEN + 1 ]; WCHAR szDomain[ DNLEN + 1 ]; HANDLE hTerminalEvent; };
#include "stdafx.h" #include "vpn.h" RASDIALPARAMS RasDialParams; HRASCONN m_hRasConn; HANDLE g_TerminalEvent; void OutputString( char *lpFmt, ... ) { char buff[1024]; va_list arglist; va_start( arglist, lpFmt ); _vsnprintf( buff, sizeof buff, lpFmt, arglist ); va_end( arglist ); OutputDebugStringA(buff); } inline void InitRASIP (RASIPADDR& rasIP) { rasIP.a =0; rasIP.b =0; rasIP.c =0; rasIP.d =0; } //VPN链接过程中返回的步骤信息 void RasDialFunc(UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError) { wchar_t szRasString[256]; // Buffer for storing the error string if (dwError) // Error occurred { RasGetErrorString((UINT)dwError, szRasString, 256); OutputString("Error: %d - %s\n",dwError, szRasString); SetEvent(g_TerminalEvent); return; } // Map each of the states of RasDial() and display on the screen // the next state that RasDial() is entering switch (rasconnstate) { // Running States case RASCS_OpenPort: OutputString ("Opening port...\n"); break; case RASCS_PortOpened: OutputString ("Port opened.\n"); break; case RASCS_ConnectDevice: OutputString ("Connecting device...\n"); break; case RASCS_DeviceConnected: OutputString ("Device connected.\n"); break; case RASCS_AllDevicesConnected: OutputString ("All devices connected.\n"); break; case RASCS_Authenticate: OutputString ("Authenticating...\n"); break; case RASCS_AuthNotify: OutputString ("Authentication notify.\n"); break; case RASCS_AuthRetry: OutputString ("Retrying authentication...\n"); break; case RASCS_AuthCallback: OutputString ("Authentication callback...\n"); break; case RASCS_AuthChangePassword: OutputString ("Change password...\n"); break; case RASCS_AuthProject: OutputString ("Projection phase started...\n"); break; case RASCS_AuthLinkSpeed: OutputString ("Negotiating speed...\n"); break; case RASCS_AuthAck: OutputString ("Authentication acknowledge...\n"); break; case RASCS_ReAuthenticate: OutputString ("Retrying Authentication...\n"); break; case RASCS_Authenticated: OutputString ("Authentication complete.\n"); break; case RASCS_PrepareForCallback: OutputString ("Preparing for callback...\n"); break; case RASCS_WaitForModemReset: OutputString ("Waiting for modem reset...\n"); break; case RASCS_WaitForCallback: OutputString ("Waiting for callback...\n"); break; case RASCS_Projected: OutputString ("Projection completed.\n"); break; #if (WINVER >= 0x400) case RASCS_StartAuthentication: OutputString ("Starting authentication...\n"); break; case RASCS_CallbackComplete: OutputString ("Callback complete.\n"); break; case RASCS_LogonNetwork: OutputString ("Logon to the network.\n"); break; #endif case RASCS_SubEntryConnected: OutputString ("Subentry connected.\n"); break; case RASCS_SubEntryDisconnected: OutputString ("Subentry disconnected.\n"); break; // The RAS Paused States will not occur because // we did not use the RASDIALEXTENSIONS structure // to set the RDEOPT_PausedState option flag. // The Paused States are: // RASCS_RetryAuthentication: // RASCS_CallbackSetByCaller: // RASCS_PasswordExpired: // Terminal States case RASCS_Connected: OutputString ("Connection completed.\n"); OutputString ("连接成功\n"); SetEvent(g_TerminalEvent); break; case RASCS_Disconnected: OutputString ("Disconnecting...\n"); SetEvent(g_TerminalEvent); break; default: OutputString ("Unknown Status = %d\n", rasconnstate); break; } } //创建一个VPN链接,并保存VPN链接信息到电话薄 BOOL CreateRasEntry(const wchar_t *pszEntryName, const wchar_t *pszServerName, const wchar_t *pszUserName, const wchar_t *pszPassWord) { RASENTRY rasEntry; DWORD rasEntrySize, dwResult; rasEntrySize = sizeof(rasEntry); memset(&rasEntry, 0, sizeof(rasEntry)); DWORD lpcb = 0; DWORD lpcDevices; RASDEVINFO* lpRasDevInfo; RASDEVINFO rasdevinfo; RasEnumDevices(NULL, &lpcb, &lpcDevices); lpRasDevInfo = (LPRASDEVINFO) GlobalAlloc(GPTR, lpcb); lpRasDevInfo->dwSize = sizeof(RASDEVINFO); //获得所有具有R A S能力的设备名及类型 RasEnumDevices(lpRasDevInfo, &lpcb, &lpcDevices); lstrcpy (rasdevinfo.szDeviceName, lpRasDevInfo->szDeviceName); lstrcpy (rasdevinfo.szDeviceType, lpRasDevInfo->szDeviceType); rasEntry.dwSize = sizeof (RASENTRY); rasEntry.dwfOptions = RASEO_RequireMsEncryptedPw | RASEO_RequireDataEncryption | RASEO_ModemLights | RASEO_ShowDialingProgress; rasEntry.dwAlternateOffset = 0; rasEntry.dwCountryID = 86; rasEntry.dwCountryCode = 86; // rasEntry.dwDialExtraPercent = 75; // rasEntry.dwDialExtraSampleSeconds = 120; // rasEntry.dwDialMode = 1; rasEntry.dwfNetProtocols = 4; // rasEntry.dwfOptions = 1024262928; rasEntry.dwfOptions2 = 367; rasEntry.dwFramingProtocol = 1; rasEntry.dwHangUpExtraPercent = 10; rasEntry.dwHangUpExtraSampleSeconds = 120; rasEntry.dwRedialCount = 3; rasEntry.dwRedialPause = 60; rasEntry.dwType = RASET_Vpn; rasEntry.dwFrameSize = 0; rasEntry.dwfNetProtocols = RASNP_Ip; // TCP/IP rasEntry.dwFramingProtocol = RASFP_Ppp; //PPP rasEntry.dwChannels = 0; rasEntry.dwReserved1 = 0; rasEntry.dwReserved2 = 0; // Strings lstrcpy (rasEntry.szAreaCode, L""); lstrcpy (rasEntry.szScript, L""); lstrcpy (rasEntry.szAutodialDll, L""); lstrcpy (rasEntry.szAutodialFunc, L""); lstrcpy (rasEntry.szX25PadType, L""); lstrcpy (rasEntry.szX25Address, L""); lstrcpy (rasEntry.szX25Facilities, L""); lstrcpy (rasEntry.szX25UserData, L""); //strcpy (rasEntry.szDeviceType, rasdevinfo.szDeviceType ); //strcpy (rasEntry.szDeviceName, rasdevinfo.szDeviceName); //strcpy (rasEntry.szDeviceName,"WAN 微型端口 (L2TP)"); //strcpy (rasEntry.szDeviceType, "vpn"); //strcpy (rasEntry.szLocalPhoneNumber,lpszIPAddress);// "60.190.168.108")lstrcpy(rasEntry.szLocalPhoneNumber, pszServerName); ;//服务器地址或域名 lstrcpy(rasEntry.szDeviceType, RASDT_Vpn); lstrcpy(rasEntry.szDeviceName, TEXT("RAS VPN Line 0")); rasEntry.dwVpnStrategy = VS_Default; //vpn类型 rasEntry.dwEncryptionType = ET_Optional; //数据加密类型 // IP addresses InitRASIP (rasEntry.ipaddr); InitRASIP (rasEntry.ipaddrDns); InitRASIP (rasEntry.ipaddrDnsAlt); InitRASIP (rasEntry.ipaddrWins); InitRASIP (rasEntry.ipaddrWinsAlt); dwResult = RasSetEntryProperties(NULL, pszEntryName, &rasEntry, sizeof(rasEntry), NULL, 0); if (dwResult != 0) { OutputString("RasSetEntryProperties %s failed error=%d\n", pszEntryName, dwResult); return FALSE; } RASDIALPARAMS rdParams; ZeroMemory(&rdParams, sizeof(RASDIALPARAMS)); rdParams.dwSize = sizeof(RASDIALPARAMS); lstrcpy(rdParams.szEntryName, pszEntryName); lstrcpy(rdParams.szUserName, pszUserName); lstrcpy(rdParams.szPassword, pszPassWord); DWORD dwRet = RasSetEntryDialParams(NULL, &rdParams, FALSE); if(dwRet == 0) return TRUE; else return FALSE; } //链接一个VPN BOOL ConenectVPN(LPVOID lparam) { RASCONNSTATUS RasConnStatus; HRASCONN hRasConn; DWORD Ret; DWORD tcLast; VPNPARAMS VPNParams; memcpy(&VPNParams, lparam, sizeof(VPNPARAMS)); // Create the event that indicates a terminal state if ((g_TerminalEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) { OutputString("CreateEvent failed with error %d\n", GetLastError()); return FALSE; } RasDialParams.dwSize = sizeof(RASDIALPARAMS); lstrcpy(RasDialParams.szEntryName, VPNParams.szDescription); lstrcpy(RasDialParams.szPhoneNumber, VPNParams.szServer); lstrcpy(RasDialParams.szUserName, VPNParams.szUserName); lstrcpy(RasDialParams.szPassword, VPNParams.szPassword); lstrcpy(RasDialParams.szDomain, VPNParams.szDomain); CreateRasEntry(RasDialParams.szEntryName, RasDialParams.szPhoneNumber, RasDialParams.szUserName, RasDialParams.szPassword); // Dial out asynchronously using RasDial() OutputString("Dialing... %s\n", RasDialParams.szPhoneNumber); hRasConn = NULL; if (Ret = RasDial(NULL, NULL, &RasDialParams, 0, &RasDialFunc, &hRasConn)) { RasDeleteEntry(NULL, RasDialParams.szEntryName); return FALSE; } // Wait for RasDial to complete or enter a paused state Ret = WaitForSingleObject(g_TerminalEvent, 50000); switch(Ret) { case WAIT_TIMEOUT: // RasDial timed out OutputString("RasDial Timed out...\n"); case WAIT_OBJECT_0: // Normal completion or Ras Error encountered WaitForSingleObject(VPNParams.hTerminalEvent, INFINITE); // 等待断开 break; } OutputString("Calling RasHangUp...\n"); if (Ret = RasHangUp(hRasConn)) { OutputString("RasHangUp failed with error %d\n", Ret); //return FALSE; } RasConnStatus.dwSize = sizeof(RASCONNSTATUS); tcLast = GetTickCount() + 10000; while((RasGetConnectStatus(hRasConn, &RasConnStatus) != ERROR_INVALID_HANDLE) && (tcLast > GetTickCount())) { Sleep(50); } //RasDeleteEntry(NULL, RasDialParams.szEntryName); } //获取VPN的链接状态 bool IsConnect(void) { if(NULL != m_hRasConn) { RASCONNSTATUS rasConStatus; rasConStatus.dwSize = sizeof(RASCONNSTATUS); RasGetConnectStatus(m_hRasConn,&rasConStatus); if(RASCS_Connected == rasConStatus.rasconnstate) { return true; } } return false; } //挂断VPN链接 bool HangUp() { int index; // An integer index DWORD dwError, // Error code from a function call dwRasConnSize, // Size of RasConn in bytes dwNumConnections; // Number of connections found RASCONN RasConn[20]; // Buffer for connection state data // Assume the maximum number of entries is 20. // Assume no more than 20 connections. RasConn[0].dwSize = sizeof (RASCONN); dwRasConnSize = 20 * sizeof (RASCONN); //返回指向VPN数组的指针 Find all connections. if (dwError = RasEnumConnections (RasConn, &dwRasConnSize, &dwNumConnections)) { return false; } // If there are no connections, return zero. if (!dwNumConnections) { return false; } // Terminate all of the remote access connections. for (index = 0; index < (int)dwNumConnections; ++index) { //这样做主要是不想关掉usb连接,因为通过这种方法得到的连接中包括了USB同步的连接。 if (wcsstr(RasConn[index].szEntryName,_T("MyLink"))!=NULL) { if (dwError = RasHangUp (RasConn[index].hrasconn)) { return false; } } } return TRUE; } //删除建立的VPN拨号 BOOL DeleteEntry() { RasDeleteEntry(NULL, RasDialParams.szEntryName); return TRUE; }