wangchao.org
设为首页 | 添加收藏
 
购物视频论坛博客自然风光美女图片王朝网络小游戏BT下载生活百科编程设计软件下载小说
 
化妆 | 音乐 | 影视 | 图书 | 英语 | 宠物 | 美食 | 旅游 | 养生 | 手机 | 数码 | 汽车 | 珠宝 | 美容 | 装修 | 厨房 | 科普 | 动物 | 植物 | 影音 | 百科 | 知道 | 词典
  
 
 您好! 您现在位于: 王朝网络 → 编程设计 → 《Linux下的透明代理技术返回上一页 
 
1楼 

Linux下的透明代理技术

 
 
  网上购物、在线购物、购物搜索 欢迎光临本站购买图书、影视、音乐、数码、百货,手机等商品。

  想像这么一个场景你想截获(hijack)一个本地的出口连接(LOCALOUT)或者转发的连接(PREROUTING),对这个连接的两个方向的内容
  做修改,比如:1、将这个连接连接到远程socks代理(在通讯头部加上socks通信协议部分)2、对这个连接进行记录(用于协议分析)3、任何你能想
  到的折腾方式,比如我们叫他tcpgrep,:)。
  那么我们该如何做呢?
  一、获得连接
  首先,我们要能获得这个连接,很容易想到的是用iptables连接REDIRECT方法(和DNAT类似),如果你做过squid的透明代理,应该会觉得很熟悉。
  #iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 8010
  当然,你应该忽略一些不感兴趣的连接,可以单独建立一个chains,然后配和-j RETURN就可以做到。如果你熟悉iptables,这个应该很容易做到,我就
  不多说了。
  现在呢?
  nc -l -p 8010 (注:BSD style 的nc,如果你是GNU nc,用nc -l 8888)
  然后nc www.baidu.com 80
  很好,连接已经被第一个nc hijack了。但是,问题来了,他要去哪里呢?
  二、获得ORGINAL DESTINALTION
  这里就是透明代理技术的关键所在了。
  0、socks代理方式的
   这个方式并不是我们所需要的东西,因为这个需要涉及到用户程序的修改。因为用户需要将目的地址发送给服务器(按照socks协议),如果用户程序本身没有考虑
  支持socks协议那么就没有直接的办法了。
  1、方法一:做一个专用内核模块
   我曾经做过一个,在http://s5snake.gro.clinux.org有相关信息,是专门用于socks[45]的东西。不过在kernel 2.6.9以后因为设计上的问题,对于LOCALOUT
  的连接就失效了,现在我已经不维护这个老的模块了。
   这种方法太吃力,而且调试困难,需要同时维护netfilter/iptables的内核态模块和用户态模块,所以不推荐再使用了。
  2、方法二: PRELOAD方式
   通过PRELOAD一个库,修改socks,connect函数的调用来达到目的。
   tsocks就是这么做的(http://tsocks.sourceforge.net/)
   这样的方式非常像windows下的sockscap32程序,但是有个最大的问题是,只能在本机使用,对转发的连接就不适用了。
  3、方式三:SO_ORIGINAL_DST
   SO_ORIGINAL_DST是一个socket参数(SOL_IP层的)。
   调用方式如下:
   getsockopt (clifd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &sin_size);
   clifd是hijack到的客户socket,orig_addr是sockaddr_in结构的参数,sin_size=sizeof(sockaddr_in).
   返回0如果成功,-1失败。
   如果成功orig_addr将是客户真正需要去的方向(恩……撒个小谎,后面你会看到)。先给段代码吧:
  /*
  * Simple "Hello, World!" server
  * patch: demonstrate SO_ORIGINAL_DST
  */
  #include <stdio.h> /* */
  #include <stdlib.h> /* exit() */
  #include <string.h> /* memset(), memcpy() */
  #include <sys/utsname.h> /* uname() */
  #include <sys/types.h>
  #include <sys/socket.h> /* socket(), bind(),listen(), accept(),getsockopt() */
  #include <linux/netfilter_ipv4.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <unistd.h> /* fork(), write(), close() */
  char message[BUFSIZ];
  const int BACK_LOG = 5;
  int main(int argc, char *argv[])
  {
   int serverSocket = 0,
   on = 0,
   port = 0,
   status = 0,
   childPid = 0;
   struct hostent *hostPtr = NULL;
   char hostname[80] = "";
   struct sockaddr_in serverName = { 0 };
   struct sockaddr_in originDst = { 0 };
   socklen_t sin_size = sizeof(originDst);
   if (2 != argc)
   {
   fprintf(stderr, "Usage: %s portnum\n",
   argv[0]);
   exit(1);
   }
   port = atoi(argv[1]);
   serverSocket = socket(PF_INET, SOCK_STREAM,
   IPPROTO_TCP);
   if (-1 == serverSocket)
   {
   perror("socket()");
   exit(1);
   }
   on = 1;
   status = setsockopt(serverSocket, SOL_SOCKET,
   SO_REUSEADDR,
   (const char *) &on, sizeof(on));
   if (-1 == status)
   {
   perror("setsockopt(...,SO_REUSEADDR,...)");
   }
   {
   struct linger linger = { 0 };
   linger.l_onoff = 1;
   linger.l_linger = 30;
   status = setsockopt(serverSocket,
   SOL_SOCKET, SO_LINGER,
   (const char *) &linger,
   sizeof(linger));
   if (-1 == status)
   {
   perror("setsockopt(...,SO_LINGER,...)");
   }
   }
   /*
   * find out who I am
   */
   status = gethostname(hostname,
   sizeof(hostname));
   if (-1 == status)
   {
   perror("gethostname()");
   exit(1);
   }
   hostPtr = gethostbyname(hostname);
   if (NULL == hostPtr)
   {
   perror("gethostbyname()");
   exit(1);
   }
   (void) memset(&serverName, 0,
   sizeof(serverName));
   (void) memcpy(&serverName.sin_addr,
   hostPtr->h_addr,
   hostPtr->h_length);
   serverName.sin_addr.s_addr=htonl(INADDR_ANY);
   serverName.sin_family = AF_INET;
   serverName.sin_port = htons(port);
   status = bind(serverSocket,
   (struct sockaddr *) &serverName,
   sizeof(serverName));
   if (-1 == status)
   {
   perror("bind()");
   exit(1);
   }
   status = listen(serverSocket, BACK_LOG);
   if (-1 == status)
   {
   perror("listen()");
   exit(1);
   }
   for (;;)
   {
   struct sockaddr_in clientName = { 0 };
   int slaveSocket;
   socklen_t clientLength =
   sizeof(clientName);
   (void) memset(&clientName, 0,
   sizeof(clientName));
   slaveSocket = accept(serverSocket,
   (struct sockaddr *) &clientName,
   &clientLength);
   if (-1 == slaveSocket)
   {
   perror("accept()");
   exit(1);
   }
   childPid = fork();
   switch (childPid)
   {
   case -1: /* ERROR */
   perror("fork()");
   exit(1);
   case 0: /* child process */
   close(serverSocket);
   if (-1 == getpeername(slaveSocket,
   (struct sockaddr *) &clientName,
   &clientLength))
   {
   perror("getpeername()");
   }
   else
   {
   if(getsockopt( slaveSocket, SOL_IP, SO_ORIGINAL_DST, &originDst, &sin_size) == 0){
   printf("new connection:%s,%u",inet_ntoa(clientName.sin_addr),ntohs(clientName.sin_port));
   printf("->%s,%u\n",inet_ntoa(originDst.sin_addr),ntohs(originDst.sin_port));
   }else{
   perror("getsockopt SO_ORIGINAL_DST:");
   }
   }
   do{
   read(slaveSocket,message,BUFSIZ);
   write(1,message,strlen(message));
   write(slaveSocket, message,strlen(message));
   }while(message[0]);
   close(slaveSocket);
   exit(0);
   default:
   }
   }
   return 0;
  }
  编译运行前,记得
  #iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports [端口号]
  运行后,如果一切正常,那么你是很幸运,如果得到的是服务器运行的地址和端口,那么你很不幸,你很可能用的是2.6.9-2.6.12之间的
  内核,很明显这是一个BUG,见:http://patchwork.netfilter.org/netfilter-devel/patch.pl?id=2676
  那么怎么办呢?
  1、升级到2.6.13的内核,2.6.13已经合并了上面的那个patch。
  2、降级到低版本的内核,前提是有SO_ORIGINAL_DST选项,并测试是否正常
  3、手动分析/proc/net/ip_conntrack文件,个人分析过可行性,并认为这种方法是一种可行的补救措施,不过一直没有动手写_=_!
  三、恩……随你怎么玩吧
   用poll或者select做中间人,做tcp-grep游戏(s/microsft/gnu/g,哈哈),分析QQ协议,等等等等。写个插件式的框架会更好的:)

想像这么一个场景你想截获(hijack)一个本地的出口连接(LOCALOUT)或者转发的连接(PREROUTING),对这个连接的两个方向的内容 做修改,比如:1、将这个连接连接到远程socks代理(在通讯头部加上socks通信协议部分)2、对这个连接进行记录(用于协议分析)3、任何你能想 到的折腾方式,比如我们叫他tcpgrep,:)。 那么我们该如何做呢? 一、获得连接 首先,我们要能获得这个连接,很容易想到的是用iptables连接REDIRECT方法(和DNAT类似),如果你做过squid的透明代理,应该会觉得很熟悉。 #iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 8010 当然,你应该忽略一些不感兴趣的连接,可以单独建立一个chains,然后配和-j RETURN就可以做到。如果你熟悉iptables,这个应该很容易做到,我就 不多说了。 现在呢? nc -l -p 8010 (注:BSD style 的nc,如果你是GNU nc,用nc -l 8888) 然后nc www.baidu.com 80 很好,连接已经被第一个nc hijack了。但是,问题来了,他要去哪里呢? 二、获得ORGINAL DESTINALTION 这里就是透明代理技术的关键所在了。 0、socks代理方式的 这个方式并不是我们所需要的东西,因为这个需要涉及到用户程序的修改。因为用户需要将目的地址发送给服务器(按照socks协议),如果用户程序本身没有考虑 支持socks协议那么就没有直接的办法了。 1、方法一:做一个专用内核模块 我曾经做过一个,在http://s5snake.gro.clinux.org有相关信息,是专门用于socks[45]的东西。不过在kernel 2.6.9以后因为设计上的问题,对于LOCALOUT 的连接就失效了,现在我已经不维护这个老的模块了。 这种方法太吃力,而且调试困难,需要同时维护netfilter/iptables的内核态模块和用户态模块,所以不推荐再使用了。 2、方法二: PRELOAD方式 通过PRELOAD一个库,修改socks,connect函数的调用来达到目的。 tsocks就是这么做的(http://tsocks.sourceforge.net/) 这样的方式非常像windows下的sockscap32程序,但是有个最大的问题是,只能在本机使用,对转发的连接就不适用了。 3、方式三:SO_ORIGINAL_DST SO_ORIGINAL_DST是一个socket参数(SOL_IP层的)。 调用方式如下: getsockopt (clifd, SOL_IP, SO_ORIGINAL_DST, &orig_addr, &sin_size); clifd是hijack到的客户socket,orig_addr是sockaddr_in结构的参数,sin_size=sizeof(sockaddr_in). 返回0如果成功,-1失败。 如果成功orig_addr将是客户真正需要去的方向(恩……撒个小谎,后面你会看到)。先给段代码吧: /* * Simple "Hello, World!" server * patch: demonstrate SO_ORIGINAL_DST */ #include <stdio.h> /* */ #include <stdlib.h> /* exit() */ #include <string.h> /* memset(), memcpy() */ #include <sys/utsname.h> /* uname() */ #include <sys/types.h> #include <sys/socket.h> /* socket(), bind(),listen(), accept(),getsockopt() */ #include <linux/netfilter_ipv4.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> /* fork(), write(), close() */ char message[BUFSIZ]; const int BACK_LOG = 5; int main(int argc, char *argv[]) { int serverSocket = 0, on = 0, port = 0, status = 0, childPid = 0; struct hostent *hostPtr = NULL; char hostname[80] = ""; struct sockaddr_in serverName = { 0 }; struct sockaddr_in originDst = { 0 }; socklen_t sin_size = sizeof(originDst); if (2 != argc) { fprintf(stderr, "Usage: %s portnum\n", argv[0]); exit(1); } port = atoi(argv[1]); serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (-1 == serverSocket) { perror("socket()"); exit(1); } on = 1; status = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)); if (-1 == status) { perror("setsockopt(...,SO_REUSEADDR,...)"); } { struct linger linger = { 0 }; linger.l_onoff = 1; linger.l_linger = 30; status = setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, (const char *) &linger, sizeof(linger)); if (-1 == status) { perror("setsockopt(...,SO_LINGER,...)"); } } /* * find out who I am */ status = gethostname(hostname, sizeof(hostname)); if (-1 == status) { perror("gethostname()"); exit(1); } hostPtr = gethostbyname(hostname); if (NULL == hostPtr) { perror("gethostbyname()"); exit(1); } (void) memset(&serverName, 0, sizeof(serverName)); (void) memcpy(&serverName.sin_addr, hostPtr->h_addr, hostPtr->h_length); serverName.sin_addr.s_addr=htonl(INADDR_ANY); serverName.sin_family = AF_INET; serverName.sin_port = htons(port); status = bind(serverSocket, (struct sockaddr *) &serverName, sizeof(serverName)); if (-1 == status) { perror("bind()"); exit(1); } status = listen(serverSocket, BACK_LOG); if (-1 == status) { perror("listen()"); exit(1); } for (;;) { struct sockaddr_in clientName = { 0 }; int slaveSocket; socklen_t clientLength = sizeof(clientName); (void) memset(&clientName, 0, sizeof(clientName)); slaveSocket = accept(serverSocket, (struct sockaddr *) &clientName, &clientLength); if (-1 == slaveSocket) { perror("accept()"); exit(1); } childPid = fork(); switch (childPid) { case -1: /* ERROR */ perror("fork()"); exit(1); case 0: /* child process */ close(serverSocket); if (-1 == getpeername(slaveSocket, (struct sockaddr *) &clientName, &clientLength)) { perror("getpeername()"); } else { if(getsockopt( slaveSocket, SOL_IP, SO_ORIGINAL_DST, &originDst, &sin_size) == 0){ printf("new connection:%s,%u",inet_ntoa(clientName.sin_addr),ntohs(clientName.sin_port)); printf("->%s,%u\n",inet_ntoa(originDst.sin_addr),ntohs(originDst.sin_port)); }else{ perror("getsockopt SO_ORIGINAL_DST:"); } } do{ read(slaveSocket,message,BUFSIZ); write(1,message,strlen(message)); write(slaveSocket, message,strlen(message)); }while(message[0]); close(slaveSocket); exit(0); default: } } return 0; } 编译运行前,记得 #iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports [端口号] 运行后,如果一切正常,那么你是很幸运,如果得到的是服务器运行的地址和端口,那么你很不幸,你很可能用的是2.6.9-2.6.12之间的 内核,很明显这是一个BUG,见:http://patchwork.netfilter.org/netfilter-devel/patch.pl?id=2676 那么怎么办呢? 1、升级到2.6.13的内核,2.6.13已经合并了上面的那个patch。 2、降级到低版本的内核,前提是有SO_ORIGINAL_DST选项,并测试是否正常 3、手动分析/proc/net/ip_conntrack文件,个人分析过可行性,并认为这种方法是一种可行的补救措施,不过一直没有动手写_=_! 三、恩……随你怎么玩吧 用poll或者select做中间人,做tcp-grep游戏(s/microsft/gnu/g,哈哈),分析QQ协议,等等等等。写个插件式的框架会更好的:)

 
标签: Linux  下的  代理  技术  透明  
 
您可以将本页贴到其他网站
UBB代码HTML代码
 
 
 
 

 
 
 更多内容
 ·The Linux filesystem explained ·windows运行命令详解 ·windows下禁止程序运行 ·Linux下的并口编程
 ·MS-DOS autocomplete in Microso ·Windows 2K DDK IRP原文翻译 ·微软证实Windows XP SP3 ·Windows XP操作系统自动关机的实
 ·Win 2003安装过后必须进行的配置 ·Windows 端口汇总 ·推荐一份Linux电子杂志《OurLinu ·WIN32编程必知:__stdcall,__cde
 ·Windows 的多线程程序设计 ·Linux 目录详解! ·感受Windows XP中DOS命令的新增功 ·Win 2000命令行大揭秘
 ·编译优化自己的FreeBSD ·FreeBSD 5.3 与 Apache2.0建立列 ·从windows启动列表里启动Linux的 ·寻找免费的windows平台下的pdf创
 ·王垠:完全用Linux工作及其后续 ·如何系统的学习linux. ·什么是Fedora Linux ? ·Tomcat5.5.X 的安装和配置
 
 
 
最新评论  点此查看所有评论
 
 
 
 
发表评论(支持UBB码)


验证码:  
 
 
 
© 2005- 王朝网络 版权所有