当前位置:操作系统 > Unix/Linux >>

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;

  }

  编译运
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,