Chinaunix首页 | 论坛 | 博客
  • 博客访问: 21884
  • 博文数量: 10
  • 博客积分: 440
  • 博客等级: 下士
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-10 08:59
文章分类

全部博文(10)

文章存档

2011年(1)

2009年(9)

我的朋友
最近访客

分类:

2009-05-10 10:14:30

ProxyServer源代码分析

目的:
阶段一:
* 了解HTTP ProxyServer使用的基本网络函数。
* 了解HTTP ProxyServer如何处理客户端的GET和POST请求。
* 了解HTTP ProxyServer在有两个网卡的情况下如何转发请求。
* 了解HTTP ProxyServer的Cache的基本原理。

阶段二:
* 了解Socks5 ProxyServer的基本工作模型与实现函数。
* 了解Socks5 ProxyServer是如何支持UDP的。



1.csproxy


过程分析:
分析了一下csproxy的源代码,这是一个中国人写的多线程Proxy,很简单。
* main.c是入口,直接调用start_server。
* server.c中,start_server函数是一个主线程,先调用open_socket启动监听服务(注意open_socket只有一个输入参 数port,那IP如何指定的?跟踪到函数内部,发现IP是INADDR_ANY,即在所有网卡上监听)。遇到新请求启动新的处理线程 read_client_str。
* thread.c中,read_client_str线程先是调用read_from_net(sock.c)获取客户端请求,然后调用 fenxi_http_string(http.c)分析客户端的请求(GET/POST/CONNECT),继而得到str,host,port和 path。然后调用open_connect(sock.c)连接到远程服务器,如成功连接,将得到远程服务器的SOCKET(fd_web)。再调用 write_from_net(sock.c),使用请求参数str向远程服务器提交请求。然后再调用web_server(web.c)。
* web_server有两个参数:fd_s和fd_c。分别表示远程服务器的SOCKET和客户端的SOCKET。该函数通过循环调用 read_from_net(fd_s, &data)和write_from_net(fd_c, data, read_len)将远程服务器返回的页面逐次返回给客户端,完成代理过程。
* 在代码中,并没有看到想像中的将数据包从第一块网卡转发到第二块网卡,从open_socket和open_connect来看,这种转换是自动的。

函数分析:
* server.c中,启动新线程用的是_beginthread。
* sock.c中,open_socket函数打开一个端口,用了四个标准函数:WSAStartup,socket,bind,listen。
* open_connect函数用来连接远程服务器,使用了标准函数WSAStartup,socket,gethostbyname,connect。
* accept_client使用了标准函数accept。
* read_from_net使用了标准函数recv。
* write_from_net使用了标准函数send。
* http.c中,有get,post,conn函数分别对端的GET,POST,CONNECT请求进行解析。
* 另外,还有一个cache.c,基本原理是把cache写入到文件中,但在代码中没有看到应用。

另外,代码中出现了一些FD_xxx的函数,Google了一下,帖:
FD_ZERO,FD_ISSET这些都是套节字结合操作宏  
  看看MSDN上的select函数,  
  这是在select   io   模型中的核心,用来管理套节字IO的,避免出现无辜锁定.  
  int   select(     int   nfds,fd_set   FAR   *readfds,     fd_set   FAR   *writefds,                              
      fd_set   FAR   *exceptfds,                  
      const   struct   timeval   FAR   *timeout      
  );  
  第一个参数不管,是兼容目的,最后的是超时标准,select是阻塞操作  
  当然要设置超时事件.  
  接着的三个类型为fd_set的参数分别是用于检查套节字的可读性,可写性,和列外数据性质.  
   
  我举个例子  
  比如recv(),   在没有数据到来调用它的时候,你的线程将被阻塞  
  如果数据一直不来,你的线程就要阻塞很久.这样显然不好.  
  所以采用select来查看套节字是否可读(也就是是否有数据读了)  
  步骤如下  
  socket   s;  
  .....  
  fd_set   set;  
  while(1)  
  {      
      FD_ZERO(&set);//将你的套节字集合清空  
      FD_SET(s,   &set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字s  
      select(0,&set,NULL,NULL,NULL);//检查套节字是否可读,  
                                                        //很多情况下就是是否有数据(注意,只是说很多情况)  
                                                        //这里select是否出错没有写  
      if(FD_ISSET(s,   &set)   //检查s是否在这个集合里面,  
      {                                           //select将更新这个集合,把其中不可读的套节字去掉  
                                                  //只保留符合条件的套节字在这个集合里面  
                                   
              recv(s,...);  
       
      }  
      //do   something   here  
  }  

了解一下吧。另可参考此文章:http://mawnja.blog.163.com/blog/static/212061982008684928510/


2.proxy


老美的代码看起来味道就是不一样,很专业,东西用得也很玄乎。
这个proxy有简单的filter功能,可以设置简单的accept/deny规则。

过程分析:
* proxy.c:工作模型与csproxy差不多,accept以后起一个线程proxy(proxy.c)。
* 看了一下proxy线程,发现只是简单地read然后write,这应该不是一个HTTP Proxy。
* 注意,这个Readme里面提到两个网卡的组网,因此此例可以解释如何在两个网卡之间转发请求。
* 注意到在main()函数中,已经开始监听了:tcp_listen (srcip, srcport, &addrlen),监听的这个网卡是通过用户输入IP地址指定的,然后在proxy线程里使用tcp_connect (destip, destport)来连接目的主机。看到这里就觉得奇怪了,tcp_listen和tcp_connect如何能根据IP来判断使用哪个网卡通信?
* 跳到tcp_connect中去看就明白了,使用了遍历的办法,如果遍历到某一块网卡,成功建立连接了,就表示找对网卡了,退出循环:
  /*
   * Loop through, calling socket and connect for each IP, creating
   * a linked list of addrinfo structures, once a successful connection
   * has been made, break out of loop. We then free memory and return
   * a socket descriptor.
   */
  do {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0)
      continue;

    if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
      break;    /* Break on success */

   close(sockfd);
  } while ( (res = res->ai_next) != NULL); /* Build linked list */

tcp_listen的代码类似。

注意,代码中的关键是如何枚举网卡的。注意网卡资源信息是使用res交互的,查看res的定义,发现是一个addrinfo的struct。
参考MSDN:


3.transproxy


这个proxy已经在运行脚本tproxyrun中明确表示支持多网卡。
待来看看是如何支持的。

过程分析:
* Take for example the network configuration of a FreeBSD or Linux box acting as
a dialin server (or terminal server), and another FreeBSD or Linux box acting
as a Squid (or any other) proxy cache. Normally users would have to
configure their browser to access the proxy. This transparent proxy
will automatically intercept HTTP accesses and re-direct them to the
Squid (or any other) proxy server. The users need not even know that
a proxy is being used, it's that transparent.
也就是说这不是一个代理,而只是一个透明网关。


4.tinyproxy.c


这个应该是一个比较典型的HTTP Proxy了,有一定的应用价值,因此应该仔细研究一下。

过程分析:
* 入口tinyproxy.c。


5.squid
见《ProxyServer:squid》


阅读(1092) | 评论(0) | 转发(0) |
0

上一篇:搭建C/C++开发环境

下一篇:调整VG大小

给主人留下些什么吧!~~