如何在HTTP代理中查看TCP,IP标头?

时间:2016-08-29 16:58:17

标签: c sockets networking http-proxy packets

我在我的Ubuntu 14.04 x86_64上实现了一个分叉的HTTP代理,具有以下方案(我报告基本代码和伪代码只是为了显示概念):

  1. socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2. bind(socketClient,(struct sockaddr*)&addr, sizeof(addr));
  3. listen(socketClient, 50);
  4. newSocket = accept(socketClient, (struct sockaddr*)&cliAddr, sizeof(cliAddr));
  5. 从客户端获取请求,解析它以解析IP地址中请求的主机名;
  6. fork(),打开与远程服务器的连接并处理请求;
  7. 子进程:如果是GET请求,则向服务器发送原始请求,当服务器正在发送数据时,将数据从服务器发送到客户端;
  8. 子进程:如果是CONNECT请求,则将字符串200 ok发送到客户端,并使用select()轮询客户端套接字描述符和服务器套接字描述符;如果我从服务器套接字读取数据,请将此数据发送给客户端;否则,如果我从客户端套接字读取数据,请将此数据发送到服务器。
  9. 好处是这个代理工作,坏的是现在我必须收集统计数据;这很糟糕,因为我工作的水平我无法获得我感兴趣的数据。我不关心有效载荷,我只需要检查IP和TCP标头我关心的标志。

    例如,我对:

    感兴趣
    • 连接跟踪;
    • 发送和接收的数据包数。

    首先,我会在TCP标头中检查SYN标志,SYN / ACK然后是最后一个ACK;至于第二个,当我char buffer[1500]send()一个完整的数据包时,每当recv()填充数据时,我只会向我的一个计数器+1。

    我意识到这是不对的:SOCK_STREAM没有包的概念,它只是一个连续的字节流!我在第7和第8点使用的char buffer[1500]具有有用的统计数据,我可以将其容量设置为4096字节,但我无法跟踪发送或接收的TCP数据包,因为TCP已经,而非数据包

    我无法解析char buffer[]在TCP标头中寻找SYN标志,因为IP和TCP标头从标题中被剥离(因为I' m工作,指定为IPPROTO_TCP标志)如果我理解的话,char buffer[]只包含有效载荷,对我来说没用。

    所以,如果我的工作水平太高,我应该更低一些:一旦我看到一个简单的raw套接字嗅探器,其中unsigned char buffer[65535]被强制转换为struct ethhdr, iphdt, tcphdr并且它可以看到所有 所有标题的标志,我感兴趣的所有统计数据!

    欢乐之后,令人失望的是:由于raw套接字在低级别上工作,他们没有一些对我的代理人至关重要的概念; raw个套接字不能bindlistenaccept;我的代理正在侦听固定端口,但是raw套接字不知道端口是什么,它属于TCP级别,而是bindsetsockopt的指定接口

    所以,如果我socket(PF_INET, SOCK_RAW, ntohs(ETH_P_ALL))我应该能够在{7}和.8处解析我recv()send()的缓冲区,但我应该使用{{ 1}}和recvfrom() ...但所有这些听起来都很混乱,它包含了对我的代码进行了很好的重构。

    如何保持代理结构(sendto()到固定端口和接口)的完整性并增加我对IP和TCP标头的视线?

1 个答案:

答案 0 :(得分:2)

我的建议是在例如应用程序的另一个线程中打开一个原始套接字。嗅探所有流量并按地址和端口号过滤掉相关数据包。基本上你想要实现自己的数据包嗅探器:

int sniff()
{
    int sockfd;
    int len;
    int saddr_size;
    struct sockaddr saddr;
    unsigned char buffer[65536];

    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }
    while (1) {
        saddr_size = sizeof(saddr);
        len = recvfrom(sockfd, buffer, sizeof(buffer), 0, &saddr, &saddr_size);
        if (len < 0) {
            perror("recvfrom");
            close(sockfd);
            return -1;
        }

        // ... do the things you want to do with the packet received here ...
    }
    close(sockfd);
    return 0;
}

如果您知道哪个接口将用于代理的流量,您还可以将该原始套接字绑定到特定接口。例如,要绑定到“eth0”:

setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);

使用getpeername()getsockname()函数调用查找TCP连接的本地和远程地址和端口号。您需要按这些过滤数据包。