C++实现VPN工具之常用API函数

时间:2023-03-09 19:58:05
C++实现VPN工具之常用API函数

RAS是Remote Access Service的缩写,意为:远程访问服务,主要用来配置企业的远程用户对企业内部网络访问,包括拨号访问和vpn方式。微软的所有Windows平台中都有RAS客户机,它允许我们将自己的计算机与另一个地方的远程计算机(其特色是一个远程访问服务器组件)相连,一般情况下,  RAS客户机利用连接了电话线的一个调制解调器,通过拨号的方式呼叫远程计算机。服务器这方面,必须有一项等候DUN连接的服务,RAS客户机和服务器之间的连接建立之后,网络协议堆栈(与所用的分帧协议有关)就通过这个RAS连接,与远程计算机通信,就象通过LAN连接的一样。如今,许多调制解调器的数据通信速率明显比直接的LAN连接慢。RAS连接通过电话簿条目中可用的选项,经过验证后,RAS便可自动进入一台机器,登录到一个域。

VPN开发经常使用的API

RAS有四个函数,允许通过程序对电话簿RASENTRY结构进行管理。它们是:RasSet Entry、RasGetEntryProperties、RasRenameEntry和RasDeleteEntry。如要建立一个新条目或对一个现成的条目进行修改,可使用RasSetEntryProperties函数

RasGetEntryProperties  此函数检索并返回一个电话簿条目的属性

RasSetEntryProperties  修改或者创建一个电话本中的链接条目信息,通俗的说就是修改或创建一个VPN链接属性。

DWORD RasGetEntryProperties(
  LPWSTR lpszPhoneBook,
  LPWSTR szEntry,
  LPRASENTRY lpbEntry,
  LPDWORD lpdwEntrySize,
  LPBYTE lpb,
  LPDWORD lpdwSize
);

lpszPhoneBook:常被忽略并被设置为空值,电话薄条目存储在注册表中,而不是在电话本中。

szEntry:一个包含条目名称,空字符结尾字符串的指针。如果指定一个空字符串,该函数返回的默认值在lpbEntry 和 lpb 参数指向的缓冲区。

lpbEntry:指定新的连接数据,要与由 lpszEntry 参数指示的电话簿条目关联的 RASENTRY 结构的指针。

RasDeleteEntry

删除电话薄中条目,一般用来删除VPN连接

RasDial:拨号API

DWORD RasDial(

__in         
LPRASDIALEXTENSIONS lpRasDialExtensions,

__in         
LPCTSTR lpszPhonebook,

__in         
LPRASDIALPARAMS lpRasDialParams,

__in         
DWORD dwNotifierType,

__in         
LPVOID lpvNotifier,

__in         
LPHRASCONN lphRasConn

);

lpRasDialExtensions:参数是一个可选指针,指向一个RASDIALEXTENSIONS结构,有了

这个结构,你的应用程序便可使用RasDial函数的扩展特性了,
可忽略,设为NULL

pszPhonebook:用于识别到一个电话簿文件的路径。设为NULL,表示使用当前默认电话簿文件

lpRasDialParams:定义了拨号和用户身份验证参数

dwNotifierType,
lpvNotifier:可同步调用还是异步调用。

lphRasConn:

第一个参数lpRasDialExtensions

typedef struct
tagRASDIALEXTENSIONS {

DWORD    
dwSize;

DWORD    
dwfOptions;

HWND     
hwndParent;

ULONG_PTR reserved;

#if (WINVER >=
0x500)

ULONG_PTR reserved1;

RASEAPINFO RasEapInfo;

#endif

} RASDIALEXTENSIONS

第三个参数lpRasDialParams

typedef struct
_RASDIALPARAMS {

DWORD    
dwSize;

TCHAR    
szEntryName[RAS_MaxEntryName + 1];

TCHAR    
szPhoneNumber[RAS_MaxPhoneNumber + 1];

TCHAR    
szCallbackNumber[RAS_MaxCallbackNumber + 1];

TCHAR    
szUserName[UNLEN + 1];

TCHAR    
szPassword[PWLEN + 1];

TCHAR    
szDomain[DNLEN + 1] ;

#if (WINVER >=
0x401)

DWORD    
dwSubEntry;

ULONG_PTR dwCallbackId;

#endif

}
RASDIALPARAMS;

第四个第五个参数:

如果lpvNotifier参数设为NULL,RasDial就会置入同步模式。dwNotifierType参数就会被忽略。同步调用RasDial是使用该函数的最简单的作用;美中不足的是,同步模式下不能对连接进行监视,如果lpvNotifier

参数不为NULL,就会进入异步模式,
意味着进行连接的同时,函数调用会立即返回。可以对连接进程进行监视

dwNotifierType:

0: lpvNotifier参数使用RasDialFunc函数指针管理连接事件

1: lpvNotifier参数利用RasDialFunc1函数指针管理连接事件

2: lpvNotifier参数利用RasDialFunc2函数指针管理连接事件

0xFFFFFFF: lpvNotifier参数令RasDial在连接事件期间发送一个窗口消息

RasHangUp挂断VPN

DWORD RasHangUp(

__in         
HRASCONN hrasconn

);

hrasconn:RasDial返回的一个连接句柄

注意:连接在利用一个调制解调器端口时,如果连接关闭,这个端口需要花时间重新设置这个连接。因此,你应该一直等下去,直到端口连接完全关闭为止。要做到这一点,在重新设置自己的连接时,可调用

RasGetConnectionStatus:获取VPN链接状态来判断连接是否完全关闭。

DWORD
RasGetConnectStatus(

__in         
HRASCONN hrasconn,

__in_out     
LPRASCONNSTATUS lprasconnstatus

);

hrasconn:RasDial返回的一个连接句柄

lprasconnstatus:取得当前的连接状态

typedef struct
_RASCONNSTATUS {

DWORD        
dwSize;

RASCONNSTATE 
rasconnstate;

DWORD        
dwError;

TCHAR        
szDeviceType[RAS_MaxDeviceType + 1];

TCHAR        
szDeviceName[RAS_MaxDeviceName + 1];

#if (WINVER >=
0x401)

TCHAR       
szPhoneNumber[ RAS_MaxPhoneNumber + 1 ];

#endif // (WINVER
>= 0x401)

} RASCONNSTATUS;

dwSize:必须设为RASCONNSTATUS结构的长度(按字节算)。

rasconnstate:RAS连接活动状态

dwError:若RasGetConnectStatus没有返回0,就取得一个具体的R
A S错误代码

szDeviceType:取得一个字串,该字串代表连接所用的设备类型

szDeviceName:取得当前的设备名

szPhoneNumber:电话号码,如*99#之类的字符串

RasEnumDevices:获得所有具有RAS能力的设备名及类型

DWORD RasEnumDevices(

__in         
LPRASDEVINFO lpRasDevInfo,

__in_out     
LPDWORD lpcb,

__out        
LPDWORD lpcDevices

);

RasValidateEntryName:判断名字的格式是否正确,是否已包含在电话簿中

DWORD
RasValidateEntryName(

__in         
LPCTSTR lpszPhonebook,

__in         
LPCTSTR lpszEntry

);

lpszPhonebook:用于识别到一个电话簿文件的路径。设为NULL,表示使用当前默认电话簿文件

lpszEntry:电话簿中没有这个名字,但该名字格式无误时,便返回ERROR_SUCCESS。若名字格式有错,这个函数就会失败,返回ERROR_INVALIDNAME;

如果电话簿中有这个名字,就会返回ERROR_ALREAD_YEXIST

可以用RasGetEntryDialParams和RasSetEntryDialParams函数来管理与具体电话簿条目相关的用户安全凭据

通过对RasDial的成功调用,本函数调用后返回连接信息被保存为电话簿入口。

RasEnumConnections:返回一个RASCONN结构数组的缓存的长指针,指向每一个RAS连接。

在调用本函数之前,必须设置缓存中RASCONN结构的第一个成员dwSize的值,即RASCONN的大小,为了在不同系统版本中通过,请用sizeof(RASCONN)取得大小。

本函数列出所有活动RAS连接,返回每一个连接句柄和电话簿入口名

函数原型:

DWORD
RasEnumConnections(

LPRASCONN
lprasconn,

LPDWORD
lpcb,

LPDWORD
lpcConnections

);

下面我们来了解一个很重要的数据结构RASENTRY

typedef_struct_RASENTRY
{
  DWORD dwSize;
  DWORD dwfOptions;

DWORD dwCountryID;
  DWORD dwCountryCode;
  TCHAR szAreaCode[ RAS_MaxAreaCode + 1
];
  TCHAR szLocalPhoneNumber[
RAS_MaxPhoneNumber + 1 ];
  DWORD dwAlternatesOffset;
  RASIPADDR ipaddr;
  RASIPADDR ipaddrDns;
  RASIPADDR ipaddrDnsAlt;
  RASIPADDR ipaddrWins;
  RASIPADDR ipaddrWinsAlt;
  DWORD dwFrameSize;
  DWORD dwfNetProtocols;
  DWORD dwFramingProtocol;
  TCHAR szScript[ MAX_PATH ];
  TCHAR szAutoDialDll[ MAX_PATH
];
  TCHAR szAutoDialFunc[ MAX_PATH
];
  TCHAR szDeviceType[ RAS_MaxDeviceType +
1 ];
  TCHAR szDeviceName[ RAS_MaxDeviceName +
1 ];
  TCHAR szX25PadType[ RAS_MaxPadType + 1
];
  TCHAR szX25Address[ RAS_MaxX25Address +
1 ];
  TCHAR szX25Facilities[
RAS_MaxFacilities + 1 ];
  TCHAR szX25UserData[ RAS_MaxUserData +
1 ];
  DWORD dwChannels;
  DWORD dwReserved1;
  DWORD dwReserved2;
  DWORD dwCustomAuthKey;
} RASENTRY;

RASENTRY 赋值:

RasOptions   =  
RASEO_SpecificNameServers   |   RASEO_RemoteDefaultGateway;

//RASEO_UseCountryAndAreaCodes   (是否使用区号与拨号属性)

//RASEO_SpecificIpAddr   (服务类型-> TCP/IP设置-> 是否指定IP地址项)

//RASEO_SpecificNameServers   (设置DNS是否选用)

//RASEO_IpHeaderCompression   (是否选用IP头指针压缩)

//RASEO_RemoteDefaultGateway   (是否选用远程网上默认网关)

//RASEO_DisableLcpExtensions   (是否决定在PPP里不使用LCP,一般不使用这个选项)

//RASEO_TerminalAfterDial   (是否拨号后出现终端窗口 <常规-> 连接方式-> 设置-> 选项-> 连接控制> )

//RASEO_ModemLights   (此选项只对WIN2K有效,选用后在任务栏出现一个状态监测器)

//RASEO_SwCompression   (是否选用软件压缩 <服务器类型-> 高级选项> )

//RASEO_RequireEncryptedPw   (是否选用需要加密的密码 <作用是PPP使用PAP明文> )

//RASEO_RequireMsEncryptedPw   (是否选用需要微软加密的密码)

//RASEO_RequireDataEncryption   (是否选用需要数据加密)

//RASEO_NetworkLogon   (此选项对NT/2K没有影响,是否选用登陆网络)

//RASEO_UseLogonCredentials   (是否当前用户采用用户名、密码、域等信息进行拨号连接)

//RASEO_PromoteAlternates   (是否选用交替号码)

VpnStrategy   =   VS_Default;

strcpy(mName   , "GPRSHY ");

//   rasEntry     Num  
values

rasEntry.dwSize =   sizeof  
(RASENTRY);

rasEntry.dwfOptions =   RasOptions;

rasEntry.dwAlternateOffset =   0;

rasEntry.dwCountryID =   86;//china

rasEntry.dwCountryCode =   86;//china

rasEntry.dwFrameSize =   0;

rasEntry.dwfNetProtocols =   RASNP_Ip;
//   TCP/IP

rasEntry.dwFramingProtocol =  
RASFP_Ppp; //PPP

rasEntry.dwChannels =   0;

rasEntry.dwReserved1 =   0;

rasEntry.dwReserved2 =   0;

if(WINVER   > =   0X400)//
  WIN98系统

{

rasEntry.dwDialMode   =  
RASEDM_DialAsNeeded;

rasEntry.dwIdleDisconnectSeconds=0;

rasEntry.dwSubEntries=0;

rasEntry.dwDialExtraPercent=0;

rasEntry.dwDialExtraSampleSeconds=0;

rasEntry.dwHangUpExtraPercent=0;

rasEntry.dwHangUpExtraSampleSeconds=0;

if(WINVER   > =   0x500)//
  WIN2K系统

{

rasEntry.dwType        
    =   RASET_Vpn;

rasEntry.dwVpnStrategy=VpnStrategy;

rasEntry.dwCustomAuthKey=600;

GUID   guid;

CoCreateGuid(&guid);

rasEntry.guidId=guid;

rasEntry.dwEncryptionType=0;

strcpy(rasEntry.szCustomDialDll,
" ");

//
rasEntry.dwfOptions2=0;

//
rasEntry.dwfOptions3=0;

if(WINVER   > =   0x501)

{

//
rasEntry.dwfOptions2=0;

//
rasEntry.dwfOptions3=0;

}

}

}

//   Strings   values

char   strPhoneNumber[20];

char   strDevType[50];

char   strDevName[50];

strcpy   (strPhoneNumber,  
"*99***1# ");

strcpy   (strDevType,  
"RASDT_Modem ");

strcpy   (strDevName,  
"Hawsna   Wireless   Modem ");

strcpy   (rasEntry.szAreaCode,  
"   ");

strcpy   (rasEntry.szLocalPhoneNumber,
  strPhoneNumber);

strcpy   (rasEntry.szScript,  
" ");

strcpy   (rasEntry.szAutodialDll,
  " ");

strcpy   (rasEntry.szAutodialFunc,
  " ");

strcpy   (rasEntry.szX25PadType,  
" ");

strcpy   (rasEntry.szX25Address,  
" ");

strcpy   (rasEntry.szX25Facilities,
  " ");

strcpy   (rasEntry.szX25UserData,
  " ");

strcpy   (rasEntry.szDeviceType,  
strDevType);

strcpy   (rasEntry.szDeviceName,  
strDevName);

//   IP   addresses

RASIPADDR  
struipaddrDns,struipaddrDnsAlt,struipaddrWins,struipaddrWinsAlt;

struipaddrDns.a   =   211;

struipaddrDns.b   =   136;

struipaddrDns.c   =   20;

struipaddrDns.d   =   203;

struipaddrDnsAlt.a   =   202;

struipaddrDnsAlt.b   =   96;

struipaddrDnsAlt.c   =   128;

struipaddrDnsAlt.d   =   68;

struipaddrWins.a   =   0;

struipaddrWins.b   =   0;

struipaddrWins.c   =   0;

struipaddrWins.d   =   0;

struipaddrWinsAlt.a   =   0;

struipaddrWinsAlt.b   =   0;

struipaddrWinsAlt.c   =   0;

struipaddrWinsAlt.d   =   0;

rasEntry.ipaddrDns   =  
struipaddrDns;

rasEntry.ipaddrDnsAlt   =  
struipaddrDnsAlt;

rasEntry.ipaddrWins   =  
struipaddrWins;

rasEntry.ipaddrWinsAlt   =  
struipaddrWinsAlt;

一个简单的示例:

//   Call   RAS

DWORD   dwRet=RasSetEntryProperties   (NULL,   mName,   &rasEntry,   sizeof(RASENTRY),   NULL,   0);

if   (dwRet)//ERROR_INVALID_PARAMETER   ==   dwRet)  

{

CString   str;

str.Format( "RasSetEntryProperties   failed   %s\n   The   error   code:   %d\n ",   mName,dwRet);

AfxMessageBox(str);

// return   FALSE;

}