WPAD 通过让浏览器自动发现代理服务器,使代理服务器对用户来说是透明的,进而轻松访问互联网。WPAD 可以借助 DNS 服务器或 DHCP 服务器来查询代理自动配置(PAC)文件的位置。
引言
代理服务器大多被用来连接 INTERNET (国际互联网)和 INTRANET(企业内部网)。在多个局域网中需设置不同的代理服务器参数来使浏览器访问网络。在微软 Internet Explorer ( IE )5.0 以上版本中的功能中已经具备了自动切换代理服务器的功能。网络管理员需要事先部署代理服务器配置文件,然而用户方的设置却很简单。在这一功能中使用了被称为“WPAD”(Web Proxy Auto-Discovery protocol)的协议。
浏览器原本具有读入并解析代理服务器的配置文件,并将其配置信息设置到浏览器中的功能。配置文件是使用 Java Script 描述的,通常具有“.js”,“.jvs”,“.pac”(proxy auto-configuration)等扩展名的文件。
自动代理检测由系统确定,Web 代理服务器代表客户端发送请求。自动代理检测启用时,系统会尝试定位到代理发送请求后返回的代理配置文件位置。若查找到代理配置文件,那么在使用 Web 代理服务器实例得到代理信息、数据请求或响应信息时,在本地计算机上进行下载,编译并运行。
被部署的大多数配置文件的格式是 Proxy auto-config (PAC)。最初,PAC 是由 Netscape 在 1996 年为 Netscape Navigator 2.0 设计的。WPAD 协议草案由 Inktomi、微软、RealNetworks、Sun Microsystems 几个公司共同提出。WPAD 可支持所有主流浏览器,首次存在于 Internet Explorer 5.0 中。
WPAD 对代理服务器的透明处理使得管理员不再需要去每台客户机上设置代理服务器参数了。自动检测受动态主机配置协议 (DHCP) 和域名系统 (DNS) 的支持,浏览器通过 DHCP 和 DNS 的查询来搜索 PAC 文件的位置。
Proxy Auto-Config(PAC)文件
在开始介绍 WPAD 原理之前,有必要首先对代理自动配置(PAC)文件有一个概念上的认识。代理自动配置(PAC)文件定义了浏览器和其他用户代理如何自动选择适当的代理服务器来访问一个 URL。要使用 PAC,我们应当在一个网页服务器上发布一个 PAC 文件,并且通过在浏览器的代理链接设置页面输入这个 PAC 文件的 URL 或者通过使用 WPAD 协议告知用户代理去使用这个文件。
一个 PAC 文件是一个至少定义了一个 JavaScript 函数的文本文件。该函数 FindProxyForURL(url, host)有 2 个参数:
url 是一个对象的 URL;
host 是一个由这个 URL 所衍生的主机名。
按照惯例,这个文件名字一般是 proxy.pac。 WPAD 标准使用 wpad.dat。一个非常简单的 PAC 文件内容如下:
1 function FindProxyForURL(url, host) { 2 if (url== 'http://www.baidu.com/') return 'DIRECT'; 3 if (host== 'twitter.com') return 'SOCKS 127.0.0.10:7070'; 4 if (dnsResolve(host) == '10.0.0.100') return 'PROXY 127.0.0.1:8086;DIRECT'; 5 return 'DIRECT'; 6 }
WPAD 的原理
DHCP 的自动检测
通过 DHCP 服务器,管理员可以集中指定全局 TCP/IP 参数和子网特定的 TCP/IP 参数,并可使用保留地址定义客户端的参数。如果客户端计算机在子网之间发生了移动,则在启动该计算机时,会自动重新配置 TCP/IP。
通过 DHCP 服务器部署 WPAD 的原理如下。首先确保 DHCP 服务器有效,然后为每个包含客户的子网建立了作用域。DHCP 服务器中的 252 选项通常被当作查询或注册用的指针,我们可以通过 252 项发现打印机、时间服务器、WPAD 主机以及其他的网络服务器。在 DHCP 服务器中添加一个用于查找 WPAD 主机的 252 项,252 项是一个字符串值,内容是部署在 WPAD 主机上 PAC 文件的 URL。为适当的作用域配置 252 项,就算只有一个作用域。具体部署 DHCP 服务器的操作,请见参考文献:ISA 防火墙之利用 DHCP 部署 WPAD。
因此 DHCP 客户机便可获得 PAC 文件的 URL,当客户机需要对浏览器或防火墙客户端进行自动配置时,就可以下载该 PAC 文件并得到代理服务器的地址。
图 1.DHCP 的自动检测示意图
在上图示意中,用户访问 laptop01.us.division.company.com 时,web 浏览器发送 DHCP INFORM 包于 DHCP 服务器来请求配置文件的位置,DHCP ACK 为 DHCP 服务器返回的数据包,其中 252 选项即为代理自动配置文件的位置。DHCP 报文格式如下图:
图 2.DHCP 报文格式
OP:消息操作代码,既可以是引导请求(1=BOOTREQUEST)也可以是引导答复(2=BOOTREPLY);
Htype:硬件地址类别,ethernet 为 1;
Hlen:硬件地址长度,ethernet 为 6;
Hops:若数据包需经过 router 传送,每站加 1,若在同一网内,为 0;
Transaction ID:事务 ID,是个随机数,用于客户和服务器之间匹配请求和相应消息;
Seconds:由用户指定的时间,指开始地址获取和更新进行后的时间;
Flags:从 0-15bits,最左一 bit 为 1 时表示 server 将以广播方式传送封包给 client,其余尚未使用;
Ciaddr:用户 IP 地址;
Yiaddr:服务器分配给客户的 IP 地址;
Siaddr:用于 bootstrap 过程中的 IP 地址;(服务器的 IP 地址)
Giaddr:转发代理(网关)IP 地址;
Chaddr:客户机的硬件地址;
Sname:可选 server 的名称,以 0x00 结尾;
File:启动文件名;
Options:厂商标识,可选的参数字段。此参数是 WPAD 实现过程中的关键参数,即相关了 PAC 文件的 URL。
DNS 的自动检测
DNS 是 TCP/IP 网络上的一组协议和服务,通过 DNS,用户可以使用分层的用户友好名称(主机)代替数字 IP 地址来搜索其他计算机。
用 DNS 来实现 WPAD,原理如下:
WPAD 工作原理是客户机向 DNS 服务器发起 WPAD+X 的查询。DNS 返回提供 WPAD 主机 IP 地址,客户机通过该 IP 的 80 端口去 WPAD 主机下载 WPAD.DAT(浏览器配置用文件)和 WSPAD.DAT(防火墙配置用文件)两个文件以实现自动配置。
客户机向 DNS 发起的 WPAD 查询的后缀是根据 WPAD 主机所处的环境决定的,如果客户机是在一个域环境下时,发起的查询便是一个“WPAD.所在域的域名”的标准域名查询,这种情况下配合 DNS 里添加 WPAD 主机的 A 或别名记录便可轻松在域环境中对 WPAD 主机的定位。
但是如果在工作组环境下时,客户机发起的查询可能是一个标准的域名查询(如果计算机名有加域名后缀)也可能只是个没有后缀的 WPAD 查询,这时就要通过创建 DNS 私有根域查询或是通过创建单标签域的方式进行 WPAD 主机查询。
从以上的原理分析,首先 WPAD 主机要在 80 端口提供 wpad.dat 和 wspad.dat,有了这两个文件,客户机上的浏览器或防火墙客户端才能实现自动配置。其次,DNS 服务器要创建相关记录,当客户机来查询时,将解析结果指向 WPAD 主机。
关于 DNS 的自动检测详细的分析,可以参考文献 工作组环境下 WPAD 部署的另类解决 。
图 3.DNS 的自动检测示意图
回页首
WPAD 的实现
通过 DNS 服务器部署 WPAD 在域的环境下比较适宜,但在工作组环境下就需要做一些改变。通过 DHCP 服务器部署 WPAD 还是更加方便,既不限制端口,又不受客户机计算机名影响,无论是工作组还是域都能很好地工作。
以下具体讨论了利用 DHCP 服务器来实现 WPAD 的 C++实例,并已通过 gcc 编译。此实例模拟了浏览器使用 DHCP 服务器实现 WPAD 的过程。同时希望能给有需要的开发人员一些关于如何实现 WPAD 的参考。
由上述通过 DHCP 服务器部署 WPAD 的原理,实现分为两大步骤,即向 DHCP 服务器发送 DHCP INFORM 包和接收 DHCP 服务器返回的 DHCP ACK 包,并解析。首先使用 Socket 实现广播(broadcast)的发送和接收来查找 WPAD 主机。广播是指在一个局域网中向所有的网上节点发送信息,是 UDP 协议的一种。在局域网中,广播 DHCP INFORM 包,当 DHCP 服务器接收到广播信息后,返回 DHCP ACK 包于客户机(本机),报文中的 252 选项即为查找的存储在 WPAD 主机上 PAC 文件的 URL。详细如下:
步骤一.查找 DHCP 服务器
- 在 WPAD 实现过程中,DHCP 报文为主要的数据结构,dhcp_send 代表 DHCP INFORM 包,dhcp_recv 代表 DHCP 服务器返回的 DHCP ACK 包。
清单 1.DHCP 报文结构
1 typedef struct dhcp_message_ { 2 uint8_t op; /* message type */ 3 uint8_t hwtype; /* hardware address type */ 4 uint8_t hwlen; /* hardware address length */ 5 uint8_t hwopcount; /* should be zero in client message */ 6 uint32_t xid; /* transaction id */ 7 uint16_t secs; /* elapsed time in sec. from boot */ 8 uint16_t flags; 9 uint32_t ciaddr; /* (previously allocated) client IP */ 10 uint32_t yiaddr; /* 'your' client IP address */ 11 uint32_t siaddr; /* should be zero in client's messages */ 12 uint32_t giaddr; /* should be zero in client's messages */ 13 uint8_t chaddr[DHCP_CHADDR_LEN]; /* client's hardware address */ 14 uint8_t servername[SERVERNAME_LEN]; /* server host name */ 15 uint8_t bootfile[BOOTFILE_LEN]; /* boot file name */ 16 uint32_t cookie; 17 uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */ 18 }dhcp_message;
- 广播的实现首先创建 UDP 的 socket,将发送端口 socket 设置为广播类型,开启发送广播报文:
清单 2.设置发送端口 socket 的属性为广播
1 int bBroadcast=1; 2 setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &bBroadcast, sizeof(bBroadcast));
- 接收端口绑定地址和这组广播的端口号(DHCP 统一使用两个 IANA 分配的端口作为 BOOTP : 服务器端 使用 67/udp, 客户端 使用 68/udp),接受广播信息:
清单 3.绑定端口号
1 struct sockaddr_in addr; 2 bzero(&addr,sizeof(addr)); 3 addr.sin_family = AF_INET; 4 addr.sin_port = htons(DHCP_CLIENT_PORT);//DHCP_CLIENT_PORT=68 5 addr.sin_addr.s_addr = inet_addr("10.0.2.15");//local IP 6 bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
- 将本机的 IP 地址和 MAC 地址等数据封装成 DHCP INFORM 报文,定义为 dhcp_send:
清单 4.封装 DHCP 报文
1 void make_message(const std::string& ip_addr, const std::string& mac_addr, dhcp_message **message) 2 { 3 dhcp_message* dhcp; 4 dhcp = (dhcp_message*)xzalloc(sizeof (*dhcp)); 5 bzero(dhcp, sizeof(dhcp_message)); 6 dhcp->op = 1; 7 dhcp->hwtype = 1; //ARPHRD_ETHER 8 dhcp->hwlen = 6; //MAC ADDR LENGTH 9 dhcp->xid = 1983; //random 10 dhcp->ciaddr = GetCiaddr(ip_addr); 11 GetChaddr(mac_addr, dhcp->chaddr); 12 dhcp->cookie = htonl(MAGIC_COOKIE); 13 uint8_t *p = dhcp->options; 14 //option 53 15 *p++ = DHO_MESSAGETYPE; 16 *p++ = 1; 17 *p++ = DHCP_INFORM; 18 //option 55 19 *p++ = DHO_PARAMETERREQUESTLIST; 20 *p++ = 1; 21 *p++ = DHO_PACFILELOCATION; 22 *p++ = DHO_END; 23 *message = dhcp; 24 }
- 广播 DHCP INFORM 报文
清单 5.发送端口发送 DHCP INFORM 广播报文:
1 struct sockaddr_in sin; 2 bzero(&sin, sizeof(sin)); 3 sin.sin_family = AF_INET; 4 sin.sin_addr.s_addr=inet_addr("255.255.255.255"); //Broadcast Address 5 sin.sin_port = htons(DHCP_SERVER_PORT);//DHCP_server_PORT=67 6 sendto(send_socket_fd, (uint8_t *)dhcp_send,sizeof(*dhcp_send) 0,\ 7 (struct sockaddr *)&sin,sizeof(sin));
步骤二.接收 DHCP 服务器返回的 DHCP ACK 包,并解析,报文中的 252 项即为查找的存储在 WPAD 主机上 PAC 文件的 URL:
清单 6.将接收端口设置超时选项并接收消息:
1 struct timeval tv_out; 2 tv_out.tv_sec = 6; 3 tv_out.tv_usec = 0; 4 setsockopt(recv_socket_fd,SOL_SOCKET,SO_RCVTIMEO,&tv_out, sizeof(tv_out)); 5 recvfrom(recv_socket_fd, (uint8_t *)dhcp_recv, sizeof(*dhcp_recv), 0,\ 6 (struct sockaddr*)&addr,(socklen_t*)&addr_len);
清单 7.解析 DHCP ACK 包
1 std::string AutoProxyDiscovery::dhcp_parser(uint8_t* options) 2 { 3 uint8_t* p = options; 4 int len; 5 std::string url; 6 while( (p-options) < DHCP_OPTION_LEN ) 7 { 8 if( 252 == *p ) 9 { 10 ++p; 11 len = *p++; 12 for( int i=0; ip) 13 { 14 url.push_back(*p); 15 } 16 break; 17 } 18 else if( 255 == *p ) 19 { 20 break; 21 } 22 else 23 { 24 ++p; 25 len = *p++; 26 p+=len; 27 } 28 } 29 return url; 30 }
步骤三.找到返回的 URL 下/data/wpad.dat 代理配置文件,在本地计算机上下载,编译并运行。即完成了 DHCP 的自动检测。
总结
之前的浏览器通常使用手动代理配置和代理自动配置等,而 WPAD 协议的出现更高级别地实现了自动化。使用 DHCP 来实现 WPAD 的思想较简单,是较好的解决方案。