Contents
1代理配置... 2
1.1代理配置分类... 2
1.2 代理自动配置... 3
2 PAC文件... 3
3 PAC函数... 3
3.1 PAC主函数... 4
3.2 其他函数... 4
4 关于Autoproxy的Windows编程... 5
4.1 WinINet对AutoProxy的支持... 5
4.2 Proxy Auto-Config(PAC)帮助函数... 6
5 案例代码... 7
6 案例测试... 12
7 参考网址... 13
1 代理配置
1.1 代理配置分类
现代的浏览器实现了几个级别的代理自动化;用户可以选择最适合他们需要的级别。下面的这些方法被普遍的实现:
1.2 代理自动配置
代理自动配置(Proxy auto-config,简称PAC)是一种网页浏览器技术,用于定义浏览器该如何自动选择适当的代理服务器来访问一个网址。在IE配置的tools->Internet Options->Connections->LAN Setting下设置,界面如下图。
这篇文章主要介绍代理自动配置,如何获取PAC文件,以及如何解析PAC文件。
2 PAC文件
PAC文件是一个至少定义了一个JavaScript函数的文本文件,这个函数叫做 FindProxyForURL(url,host)。其中URL指的是我们要访问的目的地址,而host则是从URL中提取的主机名,这个函数的返回值为字符串。后面我们将在PAC函数一节介绍它和与之相关的一些PAC函数。
要使用PAC,我们应该在一个网页服务器上发布一个PAC文件,并且通过在浏览器的代理连接设置页面输入这个PAC文件的URL或者通过使用WPAD协议告知用户代理去使用这个文件。按照惯例,这个文件名字一般是proxy.pac,WPAD标准使用的是wpad.dat。虽然大多数客户端无论从HTTP请求返回的MIME类型是什么都能够正确的处理,但是为了完整性和最佳的兼容性,我们应该设置网页服务器将这个文件的MIME类型声明为application/x-ns-proxy/autoconfig或者application/x-javascript-config。前一种声明相对一后一种更为客户端所理解。
3 PAC函数
3.1 PAC主函数
PAC主函数,FindProxyForURL(url,host),这个是我自己对它的称谓,因为它有点像C中的main函数。因为PAC文件中主要就是包含了这个函数的定义。所以本节从它讲起。
Ret=FindProxyForURL(url,host){
Return “PROXY www.example.com:80”;
}
url:所访问的网络地址。
host:从url中获取的主机名。
Ret:返回字符串,里面包含对应的代理服务器信息,返回值主要包含一下四种:
· Null:没有代理服务器可用。
· DIRECT:不用任何代理服务器,可以直接连接。
· PROXY host:port:指定代理服务器和端口。
· SOCKS host:port:指定SOCKS服务器和端口。
3 其他函数
除了FindProxyForURL外还有一些PAC文件经常用到的函数,主要如下列表:
以上所列具体的信息和使用用例可以查看参考中链接地址。
4 关于Autoproxy的Windows编程
4.1 对AutoProxy的支持
在Windows API中,主要有两个库对Autoproxy支持,一个是WinInet.lib,一个是WinHttp.lib。但是这两个库针对的方向不同,一个主要支持客户端,一个服务器端。WinINet.lib主要是客户端,而WinHttp.lib则是服务器端。
本文主要针对客户端进行描述,所以主要介绍WinINet.lib中对Autoproxy支持函数集。对于WinINet对Autoproxy的支持主要依赖于下面四个函数:
其中以“Internet”开头的三个函数,主要来自“JSProxy.dll”,必须在运行时动态链接。而这个三个函数在WinINet的定义与在“JSProxy.dll”的定义不同,所以为了能够使用上述三个函数,必须结合“WinINet.h”中关于这三个函数的定义的函数指针和“JSProxy.dll”动态库进行绑定。具体代码入图。
DectectAutoProxyForURL这个函数主要使用了前面网络代理自发发现协议(WPAD)自动的获取wpad.dat或者proxy.pac文件的获取地址。对于代理自动配置(规定一个指向PAC文件的URL时)则可以不使用该函数,直接通过URL下载PAC文件。后面的案例就是针对这种情况而言。
对这四个函数的具体含义,参考MSDN。
4 (PAC)帮助函数
WinINet提供四个对auto-config script脚本使用的帮助函数。这个四个函数定义在AutoProxyHelperVtbl structure,其结构如下图。
如上图,主要需要重新定义的函数如下表所列:
在编程中你需要重新编写这些函数,然后再把重新定义的函数地址传入AutoProxyHelperVtbl结构体中。如下图所示。
将定义好的帮助函数结构体地址和通过DectectProxyURL获取的URL下载的PAC临时文件,传入pInternetInitializeAutoProxyDll函数中,由“JSProxy.dll”中与之绑定InternetInitializeAutoProxyDll函数进行解析,就可以在pInternetInitializeAutoProxyDll函数中得到PAC文件中FindProxyForURL所返回的字符串。
5 案例代码
#include "stdafx.h"
#include
#include
#include
#include
#include
#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib, "WinInet.lib")
#pragma comment(lib, "UrlMon.lib")
using namespace std;
/************************************************************************/
/* WinINet autoproxy supports relies on four functions
/* DetectAutoProxyUrl
/* InternetInitializeAutoProxyDll
/* InternetGetProxyInfo
/* InternetDeInitializeAutoProxyDll
/* The three functions mentioned above whose name begins with "Internet:reside
/* in "JSProxy.dll", and must be linked to dynamically at runtime.To do so, use the
/* corresponding function-pointer type definitions in "WinINet..h", together with
/* LoadLibrary and GetProcAddress
/************************************************************************/
/* ==================================================================
HELPER FUNCTIONS
================================================================== */
/////////////////////////////////////////////////////////////////////
// ResolveHostName (a helper function)
/////////////////////////////////////////////////////////////////////
DWORD __stdcall ResolveHostName(LPSTR lpszHostName,
LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
{
DWORD dwIPAddressSize;
struct addrinfo Hints;
LPADDRINFO lpAddrInfo;
LPADDRINFO IPv4Only;
DWORD error;
// Figure out first whether to resolve a name or an address literal.
// If getaddrinfo( ) with the AI_NUMERICHOST flag succeeds, then
// lpszHostName points to a string representation of an IPv4 or IPv6
// address. Otherwise, getaddrinfo( ) should return EAI_NONAME.
ZeroMemory(&Hints, sizeof (struct addrinfo));
Hints.ai_flags = AI_NUMERICHOST; // Only check for address literals.
Hints.ai_family = PF_UNSPEC; // Accept any protocol family.
Hints.ai_socktype = SOCK_STREAM; // Constrain results to stream socket.
Hints.ai_protocol = IPPROTO_TCP; // Constrain results to TCP.
error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
if (error != EAI_NONAME) {
if (error != 0) {
error = (error == EAI_MEMORY) ?
ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
freeaddrinfo(lpAddrInfo);
// An IP address (either v4 or v6) was passed in, so if there is
// room in the lpszIPAddress buffer, copy it back out and return.
dwIPAddressSize = lstrlenA(lpszHostName);
if ((*lpdwIPAddressSize < dwIPAddressSize) || (lpszIPAddress == NULL)) {
*lpdwIPAddressSize = dwIPAddressSize + 1;
error = ERROR_INSUFFICIENT_BUFFER;
goto quit;
}
strcpy_s(lpszIPAddress, *lpdwIPAddressSize, lpszHostName);
goto quit;
}
// Call getaddrinfo( ) again, this time with no flag set.
Hints.ai_flags = 0;
error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
if (error != 0) {
error = (error == EAI_MEMORY) ?
ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
// Convert the IP address in addrinfo into a string.
// (the following code only handles IPv4 addresses)
IPv4Only = lpAddrInfo;
while (IPv4Only->ai_family != AF_INET) {
IPv4Only = IPv4Only->ai_next;
if (IPv4Only == NULL) {
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
}
error = getnameinfo(IPv4Only->ai_addr,
IPv4Only->ai_addrlen,
lpszIPAddress,
*lpdwIPAddressSize, NULL, 0, NI_NUMERICHOST);
if (error != 0)
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
quit:
return (error);
}
/////////////////////////////////////////////////////////////////////
// IsResolvable (a helper function)
/////////////////////////////////////////////////////////////////////
BOOL __stdcall IsResolvable(LPSTR lpszHost)
{
char szDummy[255];
DWORD dwDummySize = sizeof (szDummy) - 1;
if (ResolveHostName(lpszHost, szDummy, &dwDummySize))
return (FALSE);
return TRUE;
}
/////////////////////////////////////////////////////////////////////
// GetIPAddress (a helper function)
/////////////////////////////////////////////////////////////////////
DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
{
char szHostBuffer[255];
if (gethostname(szHostBuffer, sizeof (szHostBuffer) - 1) != ERROR_SUCCESS)
return (ERROR_INTERNET_INTERNAL_ERROR);
return (ResolveHostName(szHostBuffer, lpszIPAddress, lpdwIPAddressSize));
}
/////////////////////////////////////////////////////////////////////
// IsInNet (a helper function)
/////////////////////////////////////////////////////////////////////
BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask)
{
DWORD dwDest;
DWORD dwIpAddr;
DWORD dwMask;
dwIpAddr = inet_addr(lpszIPAddress);
dwDest = inet_addr(lpszDest);
dwMask = inet_addr(lpszMask);
if ((dwDest == INADDR_NONE) ||
(dwIpAddr == INADDR_NONE) || ((dwIpAddr & dwMask) != dwDest))
return (FALSE);
return (TRUE);
}
/////////////////////////////////////////////////////////////////////
// reportFuncErr (simple error reporting)
/////////////////////////////////////////////////////////////////////
void reportFuncErr(TCHAR * funcName)
{
printf("\n ERROR: %s failed with error number %d.\n", funcName,
GetLastError());
exit(1);
}
/* ==================================================================
* * * The main( ) function of the test application * * *
================================================================== */
void _stdcall reportFuncErr(CHAR * funcName)
{
printf("\n ERROR: %s failed with error number %d.\n", funcName,
GetLastError());
//exit(1);
}
int _tmain(int argc, _TCHAR* argv[])
{
char url[1025] = "http:\/\/appengine.google.com";//"http:\/www.microsoft.com\/windows\/default.mspx";
char host[256] = "appengine.google.com";
char WPADLocation[1024] = "file\:\/\/D:\/proxy.pac";
char TempPath[MAX_PATH];
char TempFile[MAX_PATH];
char proxyBuffer[1024];
char *proxy = proxyBuffer;
DWORD dwProxyHostNameLength = 1024;
DWORD returnVal;
HMODULE hModJS;
// Declare and populate an AutoProxyHelperVtbl structure, and then
// place a pointer to it in a containing AutoProxyHelperFunctions
// structure, which will be passed to InternetInitializeAutoProxyDll
AutoProxyHelperVtbl OurVtbl = {
IsResolvable,
GetIPAddress,
ResolveHostName,
IsInNet,
NULL,
NULL,
NULL,
NULL
};
// Declare function pointers for the three autoproxy functions
pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
pfnInternetGetProxyInfo pInternetGetProxyInfo;
AutoProxyHelperFunctions HelperFunctions;
HelperFunctions.lpVtbl = &OurVtbl;
ZeroMemory(proxy, 1024);
printf("\nWinINet Proxy Test Program output:\n");
if (!(hModJS = LoadLibrary(_T("jsproxy.dll"))))
reportFuncErr("LoadLibrary");
if (!(pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)
GetProcAddress(hModJS, "InternetInitializeAutoProxyDll")) ||
!(pInternetDeInitializeAutoProxyDll =
(pfnInternetDeInitializeAutoProxyDll)
GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"))
|| !(pInternetGetProxyInfo = (pfnInternetGetProxyInfo)
GetProcAddress(hModJS, "InternetGetProxyInfo")))
reportFuncErr("GetProcAddress");
/*if (!DetectAutoProxyUrl(WPADLocation, sizeof (WPADLocation),
PROXY_AUTO_DETECT_TYPE_DHCP |
PROXY_AUTO_DETECT_TYPE_DNS_A))
reportFuncErr("DetectAutoProxyUrl");*/
printf("\n WPAD Location is: %s\n", WPADLocation);
GetTempPathA(sizeof (TempPath) / sizeof (TempPath[0]), TempPath);
GetTempFileNameA(TempPath, NULL, 0, TempFile);
URLDownloadToFileA(NULL, WPADLocation, TempFile, 0, NULL);
if (!(returnVal = pInternetInitializeAutoProxyDll(0, TempFile,
NULL,
&HelperFunctions, NULL)))
reportFuncErr("InternetInitializeAutoProxyDll");
printf("\n InternetInitializeAutoProxyDll returned: %d\n", returnVal);
// Delete the temporary file
// (or, to examine the auto-config script, comment out the
// file delete and substitute the following printf call)
// printf( "\n The auto-config script temporary file is:\n %s\n", TempFile);
DeleteFileA(TempFile);
if (!pInternetGetProxyInfo((LPSTR) url, sizeof (url),
(LPSTR) host, sizeof (host),
&proxy, &dwProxyHostNameLength))
reportFuncErr("InternetGetProxyInfo");
printf("\n Proxy is: %s\n", proxy);
if (!pInternetDeInitializeAutoProxyDll(NULL, 0))
reportFuncErr("InternetDeInitializeAutoProxyDll");
printf("\n...end of test program!\n");
system("pause");
return (0);
}
为了达到测试的效果,我自己的D盘上存了一个proxy.pac文件。
具体内容如下图。
然后将proxy.pac文件的地址,设置到如下图中。
注意:IE中address可能与其他浏览器有点不同,IE中的地址file://D:/proxy.pac, 后面是两个斜杆,在火狐浏览器中可能需要三个斜杆,如file:///D:/proxy.pac。 运行程序后,结果如下图。
[1]http://en.wikipedia.org/wiki/Proxy_auto-config
[2]http://findproxyforurl.com/pac-functions/
[3]http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#isPlainHostName
[4] ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/autoproxy_support_in_wininet.htm
(msdn)
[5] ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/detectautoproxyurl.htm
[6]ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/internetinitializeautoproxydll.htm
[7]ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/internetgetproxyinfo.htm
[8]ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/internetdeinitializeautoproxydll.htm
[9] ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/wininet/wininet/autoproxyhelpervtbl.htm
[10] http://baike.baidu.com/view/3321596.htm