如何编写重发数据包的代理程序?

时间:2015-03-22 18:23:49

标签: c linux sockets tcp proxy

我需要在两台主机之间的代理服务器上使用c语言的原始套接字编写程序 我为它编写了一些代码(并为iptable设置了一些规则,以便将数据包的目标地址更改为代理接口),我接收数据包,在此数据包中打印数据,然后将数据包发送到接收方

它在原始套接字上处理我的简单客户端/服务器程序,但是当我尝试通过代理建立连接时 - 它不起作用。

对于如何在不使用内核的情况下编写此程序,您有什么想法吗?

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

#define PCKT_LEN 8192

int main(void){
  int s;
  char buffer[PCKT_LEN];
  struct sockaddr saddr;
  struct sockaddr_in daddr;
  memset(buffer, 0, PCKT_LEN);

  s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
  if(s < 0){
     printf("socket() error");
     return -1;
  }

  int saddr_size = sizeof(saddr);
  int header_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
  unsigned int count;

  daddr.sin_family = AF_INET;
  daddr.sin_port = htons(1234);
  daddr.sin_addr.s_addr = inet_addr ("2.2.2.1");

  while(1){
    if(recvfrom(s, buffer, PCKT_LEN , 0, &saddr, &saddr_size) < 0){
       printf("recvfrom() error");
       return -1;
    }
    else{
        int i = header_size;
        for(; i < PCKT_LEN; i++)
            printf("%c", buffer[i]);

      if (sendto (s, buffer, PCKT_LEN, 0, &daddr, &saddr_size) < 0)
        printf("sendto() error");
        return -1;
      }
    }
  }
  close(s);
  return 0;
}

2 个答案:

答案 0 :(得分:0)

(你的代码有严重的错误。例如,sendto(2)的最后一个参数不应该是指针。我会假设它不是真正的代码,真正的代码在没有警告的情况下编译。)

随着唠叨的出现,我认为一个问题是你不小心在你发送的数据包中加了一个额外的IP头。 raw(7)有以下内容:

  

除非在套接字上启用IP_HDRINCL套接字选项,否则IPv4层在发送数据包时会生成IP标头。启用后,数据包必须包含IP标头。对于接收IP报头,始终包含在数据包中。

默认情况下,

IP_HDRINCL未启用,除非protocolIPPROTO_RAW(请参阅raw(7)中的更深一点),这意味着它已在您的情况下被停用。 (我还查看了getsockopt(2)。)

您必须使用IP_HDRINCL启用setsockopt(2)告诉内核您自己提供标头,或者不在sendto()中添加标头。

最好是查看IP标头中的IHL字段,而不是假设它具有固定大小。 IP标头可以包含选项。

根据您的尝试,可能还有其他问题,IPv6的细节可能会有所不同。

答案 1 :(得分:0)

无论你在做什么,我都不认为使用原始插座就是这样。它们仅用于网络调试。

首先要注意的是,基本上你要从现有的,稳定的连接中复制内容,而不是将其隧道化。你不是在做什么建议。

如果要捕获与给定服务器的连接:端口(例如2.2.2.1:1234)到应用程序中,以便可以通过代理进行隧道传输,则可以使用iptables

iptables -t nat -A OUTPUT -p tcp -d 2.2.2.1 --dport 1234 -j REDIRECT

创建绑定到ip 0.0.0.0的应用程序,侦听TCP端口1234,并且每次2.2.2.1:1234的连接尝试都将连接到您的应用程序,您可以随意使用它。