IE代理自动配置

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 代理配置分类

        现代的浏览器实现了几个级别的代理自动化;用户可以选择最适合他们需要的级别。下面的这些方法被普遍的实现:

  • 手动代理配置:为所有URLS规定一个主机名和端口作为代理。大多数浏览器允许用户规定一个域名的列表(例如localhost),访问这个列表里面的域名的时候不通过代理服务器。如图:

IE代理自动配置_第1张图片

 IE代理自动配置_第2张图片

  • 代理自动配置(PAC):规定一个指向PAC文件的URL,这个文件中包含一个JavaSript函数来确定访问每个URL时所选用的合适代理。这个方法更加适合需要几个不同代理配置的笔记本用户,或者有很多不同代理服务器的复杂的企业级设置。如下图中已勾选框下所填写的地址。

 IE代理自动配置_第3张图片

  • 网络代理自发发现协议(WPAD):浏览器通过DHCP和DNS的查询来搜索PAC文件的位置。如图中所勾选的方式。

 IE代理自动配置_第4张图片

1.2 代理自动配置

    代理自动配置(Proxy auto-config,简称PAC)是一种网页浏览器技术,用于定义浏览器该如何自动选择适当的代理服务器来访问一个网址。在IE配置的tools->Internet Options->Connections->LAN Setting下设置,界面如下图。

IE代理自动配置_第5张图片

这篇文章主要介绍代理自动配置,如何获取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文件经常用到的函数,主要如下列表:

  •  isPlainHostName(host):判断host中是否有网域名,如果没有返回True
  •  dnsDomainls(host,domain):判断host的域名与domain是否匹配,如果匹配返回True
  •  localHostOrDomainls(host,hostdom):判断hosthostdom是否匹配,如果匹配返回True
  • isResolvable(host):判断host是否来自URL
  • isInNet(host,pattern,mask):判断host掩码计算后,是否与pattern匹配,如果匹配返回True
  • dnsResolve(host):返回host对应的ipAddress
  • myIpAddress():返回客户端的ip地址。
  • dnsDomainLevels(host):返回host的域名级别。
  • shExpMatch(str,shexp)str是否与shexp表达式匹配,如果匹配返回True
  • weekdayRange()
  • dateRange()
  •  timeRange()

以上所列具体的信息和使用用例可以查看参考中链接地址。

4 关于Autoproxy的Windows编程

4.1 对AutoProxy的支持

       在Windows API中,主要有两个库对Autoproxy支持,一个是WinInet.lib,一个是WinHttp.lib。但是这两个库针对的方向不同,一个主要支持客户端,一个服务器端。WinINet.lib主要是客户端,而WinHttp.lib则是服务器端。

       本文主要针对客户端进行描述,所以主要介绍WinINet.lib中对Autoproxy支持函数集。对于WinINet对Autoproxy的支持主要依赖于下面四个函数:

  • DectectAutoProxyUrl
  •  InternetInitializeAutoProxyDll
  • InternetGetProxyInfo
  • InternateDeInitializeAutoProxyDll

        其中以“Internet”开头的三个函数,主要来自“JSProxy.dll”,必须在运行时动态链接。而这个三个函数在WinINet的定义与在“JSProxy.dll”的定义不同,所以为了能够使用上述三个函数,必须结合“WinINet.h”中关于这三个函数的定义的函数指针和“JSProxy.dll”动态库进行绑定。具体代码入图。

IE代理自动配置_第6张图片

        DectectAutoProxyForURL这个函数主要使用了前面网络代理自发发现协议(WPAD)自动的获取wpad.dat或者proxy.pac文件的获取地址。对于代理自动配置(规定一个指向PAC文件的URL时)则可以不使用该函数,直接通过URL下载PAC文件。后面的案例就是针对这种情况而言。

     对这四个函数的具体含义,参考MSDN。

4 (PAC)帮助函数

        WinINet提供四个对auto-config script脚本使用的帮助函数。这个四个函数定义在AutoProxyHelperVtbl structure,其结构如下图。

IE代理自动配置_第7张图片

        如上图,主要需要重新定义的函数如下表所列:

  •  IsResolvable
  • GetIpAddress
  • ResovleHostName
  • IsInNet

        在编程中你需要重新编写这些函数,然后再把重新定义的函数地址传入AutoProxyHelperVtbl结构体中。如下图所示。

IE代理自动配置_第8张图片

        将定义好的帮助函数结构体地址和通过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);

}

6 案例测试

      为了达到测试的效果,我自己的D盘上存了一个proxy.pac文件。

      IE代理自动配置_第9张图片

      具体内容如下图。

IE代理自动配置_第10张图片

         然后将proxy.pac文件的地址,设置到如下图中。

IE代理自动配置_第11张图片

       注意:IE中address可能与其他浏览器有点不同,IE中的地址file://D:/proxy.pac, 后面是两个斜杆,在火狐浏览器中可能需要三个斜杆,如file:///D:/proxy.pac。 运行程序后,结果如下图。

IE代理自动配置_第12张图片

7 参考网址

[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



你可能感兴趣的:(Windows编程,浏览器)