一、簡介
代理服務器大多被用來連接 INTERNET (國際互聯網)和 INTRANET(企業內部網)。在多個局域網中需設置不同的代理服務器參數來使浏覽器訪問網絡。在微軟 Internet Explorer ( IE )5.0 以上版本中的功能中已經具備了自動切換代理服務器的功能。網絡管理員需要事先部署代理服務器配置文件,然而用戶方的設置卻很簡單。在這一功能中使用了被稱為“WPAD”(Web Proxy Auto-Discovery protocol)的協議。
網絡代理自發現協議(Web Proxy Auto-Discovery Protocol,WPAD),通過讓浏覽器自動發現代理服務器,使代理服務器對用戶來說是透明的,進而輕松訪問互聯網。
參考:http://www.ibm.com/developerworks/cn/linux/1309_quwei_wpad/
二、實現
WPAD實現源碼
/*************************************************************************
*
* wpad.cpp
*
* CREATE ON: 2013-03-14
* BY : Wayne Qu (qwquwei@cn.ibm.com)
*
* This demo is for Proxy auto defection(WPAD) feature development & UT.
*
*************************************************************************/
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <stdlib.h>
#include <strings.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <sys/select.h>
/* Useful definitions */
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define MAGIC_COOKIE 0x63825363
#define DHCP_INFORM 8
/* Sizes for DHCP options */
#define MTU_MAX 1500
#define DHCP_CHADDR_LEN 16
#define SERVERNAME_LEN 64
#define BOOTFILE_LEN 128
#define DHCP_UDP_LEN (14 + 20 + 8)
#define DHCP_FIXED_LEN (DHCP_UDP_LEN + 226)
#define DHCP_OPTION_LEN (MTU_MAX - DHCP_FIXED_LEN)
/* DHCP options */
enum DHO {
DHO_MESSAGETYPE = 53,
DHO_PARAMETERREQUESTLIST = 55,
DHO_PACFILELOCATION = 252,
DHO_END = 255
};
typedef struct dhcp_message_ {
uint8_t op; /* message type */
uint8_t hwtype; /* hardware address type */
uint8_t hwlen; /* hardware address length */
uint8_t hwopcount; /* should be zero in client message */
uint32_t xid; /* transaction id */
uint16_t secs; /* elapsed time in sec. from boot */
uint16_t flags;
uint32_t ciaddr; /* (previously allocated) client IP */
uint32_t yiaddr; /* 'your' client IP address */
uint32_t siaddr; /* should be zero in client's messages */
uint32_t giaddr; /* should be zero in client's messages */
uint8_t chaddr[DHCP_CHADDR_LEN]; /* client's hardware address */
uint8_t servername[SERVERNAME_LEN]; /* server host name */
uint8_t bootfile[BOOTFILE_LEN]; /* boot file name */
uint32_t cookie;
uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
}dhcp_message;
void * xzalloc(size_t s)
{
void *value = malloc(s);
if (value != NULL)
{
bzero(value,s);
}
return value;
}
uint32_t GetIPV4Ciaddr(const std::string& ip_addr)
{
if( 3!=std::count(ip_addr.begin(), ip_addr.end(), '.') )
{
std::cout << "invalid IP Format : " << ip_addr;
return 0;
}
std::string::size_type beg = 0;
std::string::size_type end = 0;
std::string tmp_str[4];
uint32_t tmp_int[4];
for(int i=0; i<4; i++)
{
end = ip_addr.find(".", beg);
tmp_str[i] = ip_addr.substr(beg,end-beg);
tmp_int[i] = atoi(tmp_str[i].c_str());
if( tmp_int[i] > 255)
{
std::cout << "invalid IP Format : " << ip_addr;
return 0;
}
//std::cout<< tmp_int[i] << " ";
beg = end+1;
}
return (uint8_t)tmp_int[0] | ((uint8_t)tmp_int[1]<<8) | ((uint8_t)tmp_int[2]<<16) | ((uint8_t)tmp_int[3]<<24);
}
uint32_t GetCiaddr(const std::string& ip_addr)
{
//Just Ipv4 Now
return GetIPV4Ciaddr(ip_addr);
}
uint32_t GetChaddr(const std::string& mac_addr, uint8_t* chaddr)
{
if( 5!=std::count(mac_addr.begin(), mac_addr.end(), ':') )
{
std::cout << "invalid MAC Format : " << mac_addr;
return 0;
}
std::string::size_type beg = 0;
std::string::size_type end = 0;
std::string tmp_str[6];
uint32_t tmp_int[6];
for(int i=0; i<6; i++)
{
end = mac_addr.find(":", beg);
tmp_str[i] = mac_addr.substr(beg,end-beg);
std::stringstream tmp_stream;
tmp_stream << "0x" << tmp_str[i];
tmp_stream >> std::hex >> tmp_int[i];
if( tmp_int[i] > 255)
{
std::cout << "invalid MAC Format : " << mac_addr;
return 0;
}
chaddr[i] = tmp_int[i];
//std::cout<< std::hex << (int)chaddr[i] << " ";
beg = end+1;
}
}
// Format: ipaddr - 9.125.90.117 macaddr E4:1F:13:DA:12:7E
void make_message(const std::string& ip_addr, const std::string& mac_addr, dhcp_message **message)
{
dhcp_message* dhcp;
dhcp = (dhcp_message*)xzalloc(sizeof (*dhcp));
bzero(dhcp, sizeof(dhcp_message));
dhcp->op = 1;
dhcp->hwtype = 1; //ARPHRD_ETHER
dhcp->hwlen = 6; //MAC ADDR LENGTH
dhcp->xid = 1983; //random
dhcp->ciaddr = GetCiaddr(ip_addr);
GetChaddr(mac_addr, dhcp->chaddr);
dhcp->cookie = htonl(MAGIC_COOKIE);
uint8_t *p = dhcp->options;
//option 53
*p++ = DHO_MESSAGETYPE;
*p++ = 1;
*p++ = DHCP_INFORM;
//option 55
*p++ = DHO_PARAMETERREQUESTLIST;
*p++ = 1;
*p++ = DHO_PACFILELOCATION;
*p++ = DHO_END;
*message = dhcp;
}
int open_send_socket()
{
int sockfd;
struct sockaddr_in addr;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))<0)
{
//TBI
}
int bBroadcast=1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &bBroadcast, sizeof(bBroadcast));
return sockfd;
}
int open_recv_socket(char* ipaddr)
{
int sockfd;
struct sockaddr_in addr;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))<0)
{
std::cout << "setup socket failed" << std::endl;//TBI
}
bzero(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(DHCP_CLIENT_PORT);
addr.sin_addr.s_addr = inet_addr(ipaddr);
const int retry_cnt = 10;
int retry_num = retry_cnt;
while(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr))<0 && (retry_num > 0))
{
std::cout << "bind to " << ipaddr << ":" << DHCP_CLIENT_PORT<< " failed!"<<std::endl;//TBI
sleep(3);
--retry_num;
}
return sockfd;
}
int send_packet(int udp_fd, const uint8_t *data, ssize_t len)
{
struct sockaddr_in sin;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=inet_addr("255.255.255.255");
sin.sin_port = htons(DHCP_SERVER_PORT);
return sendto(udp_fd, data, len, 0, (struct sockaddr *)&sin, sizeof(sin));
}
std::string dhcp_parser(uint8_t* options)
{
uint8_t* p = options;
int len;
std::string url;
while( (p-options) < DHCP_OPTION_LEN )
{
if( 252 == *p )
{
++p;
len = *p++;
for( int i=0; i<len; ++i,++p)
{
url.push_back(*p);
}
std::cout << "find option 252 with length " << url.size() << std::endl;
break;
}
else if( 255 == *p )
{
break;
}
else
{
++p;
len = *p++;
p+=len;
}
}
return url;
}
int readable_timeo(int sock_fd, int sec)
{
struct timeval tv_out;
tv_out.tv_sec = sec;
tv_out.tv_usec = 0;
setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&tv_out, sizeof(tv_out));
}
int main(int argc, char **argv)
{
dhcp_message *dhcp_send;
dhcp_message *dhcp_recv;
struct in_addr to;
struct timeval tv;
int send_socket_fd = open_send_socket();
int recv_socket_fd = open_recv_socket("9.125.90.177");
std::cout << "making DHCP message" << std::endl;
make_message("9.125.90.177", "E4:1F:13:DA:12:7E", &dhcp_send);
dhcp_recv = (dhcp_message*)xzalloc(sizeof (*dhcp_recv));
for(int i=0; i<3; ++i)
{
std::cout << "sending DHCP message" << std::endl;
if( -1 == send_packet(send_socket_fd, (uint8_t *)dhcp_send, sizeof(*dhcp_send)))
{
std::cout << "sendto() error" << std::endl;
continue;
}
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
int addr_len =sizeof(struct sockaddr_in);
std::cout << "receiving DHCP message" << std::endl;
readable_timeo(recv_socket_fd, 6);
if( -1 == recvfrom(recv_socket_fd, (uint8_t *)dhcp_recv, sizeof(*dhcp_recv), 0, (struct sockaddr*)&addr,(socklen_t*)&addr_len))
{
std::cout << "recvfrom() error" << std::endl;
continue;
}
break;
}
/*
for(int i=0;i<100;i++)
{
std::cout << std::hex << int(dhcp_recv->options[i]) << " ";
}
*/
std::cout << "dhcp->xid = " << dhcp_recv->xid << std::endl;
std::cout << "wpad url = " << dhcp_parser(dhcp_recv->options) << std::endl;
close(send_socket_fd);
close(recv_socket_fd);
}