如何在我的HTTP代理中查看TCP、IP头?

如何在我的HTTP代理中查看TCP、IP头?,c,sockets,networking,http-proxy,packets,C,Sockets,Networking,Http Proxy,Packets,我在我的Ubuntu 14.04 x86_64上使用以下方案实现了一个分叉HTTP代理(我报告的基本代码和伪代码只是为了展示这个概念): socketClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) bind(socketClient,(struct sockaddr*)&addr,sizeof(addr)) 监听(socketClient,50) newSocket=accept(socketClient,(struct sockaddr*)&cli

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

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

    例如,我感兴趣的是:

    • 连接跟踪
    • 发送和接收的数据包数
    对于第一个,我将在TCP报头中检查SYN标志SYN/ACK,然后是最后一个ACK;至于第二个,当我
    send()
    recv()
    一个完整的数据包时,每当
    char缓冲区[1500]
    中充满数据时,我会对我的计数器执行+1

    我意识到这是不正确的:
    SOCK\u STREAM
    没有数据包的概念,它只是一个连续的字节流!我在第7点使用的
    char缓冲区[1500]
    。八,。有一个有用的统计数据,我可以将其容量设置为4096字节,但我无法跟踪发送或接收的TCP数据包,因为TCP有段,而不是数据包

    我也无法解析
    char buffer[]
    在TCP报头中查找SYN标志,因为IP和TCP报头是从报头中剥离出来的(因为我正在处理的级别是用
    IPPROTO\u TCP
    标志指定的),而且,如果我理解得很好,
    char buffer[]
    只包含有效负载,对我来说是无用的

    因此,如果我的级别太高,我应该降低级别:有一次我看到一个简单的
    raw
    套接字嗅探器,其中
    无符号字符缓冲区[65535]
    被强制转换到
    struct ethhdr、iphdt、tcphdr
    ,它可以看到所有标题的所有标志,以及我感兴趣的所有统计信息

    喜悦过后,失望之后:由于
    raw
    sockets的工作级别较低,它们没有一些对我的代理至关重要的概念<代码>原始套接字不能
    绑定
    侦听
    接受
    ;我的代理正在侦听固定端口,但
    raw
    sockets不知道端口是什么,它属于TCP级别,它们
    bind
    使用
    setsockopt
    将其绑定到指定的接口

    因此,如果我想
    socket(PF_INET,SOCK_RAW,ntohs(ETH_p_ALL))
    我应该能够解析我在.7和.8处
    recv()
    send()
    的缓冲区,但是我应该使用
    recvfrom()
    sendto()
    …但是所有这些听起来都很混乱,它为我的代码提供了一个很好的重构


    如何保持代理的完整结构(
    绑定、侦听、接受固定端口和接口)并增加IP和TCP头的视野?

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

    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;
    }
    

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

    我将编辑问题以了解更多详细信息。您根本不可能获得任何带有HTTP代理的UDP。为什么不使用专用嗅探器,例如wireshark?您可以嗅探特定接口上的所有流量,然后根据协议和端口号进行过滤。您还可以使用原始套接字方法编写自己的嗅探器:您创建的需求似乎与项目的设计有很大的差异。您确定需要了解特定的数据包流量吗?你为什么认为你需要这个?如果您的应用程序是绑定到此接口的唯一通信量,您可以映射到本地接口并查询这些统计信息。或者,您可以破解内核代码以打印或导出所有需要的内容,前提是您有权使用应用程序运行自定义内核(一个可加载的模块也可能足够了,这取决于您到底想做什么)。
    setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);