C++ 无法使用原始套接字打印TCP响应

C++ 无法使用原始套接字打印TCP响应,c++,c,sockets,network-programming,raw-sockets,C++,C,Sockets,Network Programming,Raw Sockets,我使用原始套接字发送教程(第三节)中给出的数据报。但是,我无法正确打印响应(显示垃圾字符)。不确定这个程序出了什么问题。 有人能识别错误吗 #define P 80 /* TCP port 80 - HTTP */ unsigned short /* this function generates header checksums */ csum(unsigned short *buf, int nwords) { //checksum code here, omitti

我使用原始套接字发送教程(第三节)中给出的数据报。但是,我无法正确打印响应(显示垃圾字符)。不确定这个程序出了什么问题。 有人能识别错误吗

#define P 80        /* TCP port 80 - HTTP */

unsigned short /* this function generates header checksums */
csum(unsigned short *buf, int nwords)
{
    //checksum code here, omitting for stackoverflow question
}

int main()
{
    int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); /* open raw socket */
    char datagram[4096]; /* this buffer will contain ip header, tcp header,
     and payload. we'll point an ip header structure
     at its beginning, and a tcp header structure after
     that to write the header values into it */
    struct ip *iph = (struct ip *) datagram;
    struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof(struct ip);
    struct sockaddr_in sin;
    /* the sockaddr_in containing the dest. address is used
     in sendto() to determine the datagrams path */

    sin.sin_family = AF_INET;
    sin.sin_port = htons(P);/* you byte-order >1byte header values to network
     byte order (not needed on big endian machines) */
    sin.sin_addr.s_addr = inet_addr("74.125.224.72"); //google's ip address

    memset(datagram, 0, 4096); /* zero out the buffer */

    /* we'll now fill in the ip/tcp header values, see above for explanations */
    iph->ip_hl = 5;
    iph->ip_v = 4;
    iph->ip_tos = 0;
    iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr); /* no payload */
    iph->ip_id = htonl(54321); /* the value doesn't matter here */
    iph->ip_off = 0;
    iph->ip_ttl = 255;
    iph->ip_p = 6;
    iph->ip_sum = 0; /* set it to 0 before computing the actual checksum later */
    iph->ip_src.s_addr = inet_addr("1.2.3.4");/* SYN's can be blindly spoofed */
    iph->ip_dst.s_addr = sin.sin_addr.s_addr;
    tcph->th_sport = htons(1234); /* arbitrary port */
    tcph->th_dport = htons(P);
    tcph->th_seq = random();/* in a SYN packet, the sequence is a random */
    tcph->th_ack = 0;/* number, and the ack sequence is 0 in the 1st packet */
    tcph->th_x2 = 0;
    tcph->th_off = 0; /* first and only tcp segment */
    tcph->th_flags = TH_SYN; /* initial connection request */
    tcph->th_win = htonl(65535); /* maximum allowed window size */
    tcph->th_sum = 0;/* if you set a checksum to zero, your kernel's IP stack
     should fill in the correct checksum during transmission */
    tcph->th_urp = 0;

    iph->ip_sum = csum((unsigned short *) datagram, iph->ip_len >> 1);

    /* finally, it is very advisable to do a IP_HDRINCL call, to make sure
     that the kernel knows the header is included in the data, and doesn't
     insert its own header into the packet before our data */
    int one = 1;
    const int *val = &one;
    if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
        printf("Warning: Cannot set HDRINCL!\n");

    if (sendto(s,datagram,iph->ip_len,0,(struct sockaddr *) &sin, sizeof(sin)) < 0) 
        printf("error\n");
    else
        printf(".\n\n");

    sleep(2); // giving enough time to receive response

    char buffer[8192]; /* single packets are usually not bigger than 8192 bytes */
    memset(buffer, 0, 8192); /* zero out the buffer */
    while (recv(s, buffer, 8192, 0) > 0)
        printf("Caught tcp packet: %s\n", buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));

    close(s);
    return 0;
}
#定义P80/*TCP端口80-HTTP*/
unsigned short/*此函数生成标头校验和*/
csum(无符号短*buf,整数nwords)
{
//这里的校验和代码,对于stackoverflow问题省略
}
int main()
{
int s=套接字(PF_INET、SOCK_RAW、IPPROTO_TCP);/*打开原始套接字*/
字符数据报[4096];/*此缓冲区将包含ip头、tcp头、,
和有效载荷。我们将指向ip头结构
在它的开始,和tcp头结构之后
这样就可以将标题值写入其中*/
结构ip*iph=(结构ip*)数据报;
结构tcphdr*tcph=(结构tcphdr*)数据报+sizeof(结构ip);
sin中的结构sockaddr_;
/*使用包含目标地址的sockaddr_
在sendto()中确定数据报路径*/
sin.sin_family=AF_INET;
sin.sin_port=htons(P);/*将字节顺序>1字节头值发送到网络
字节顺序(在big-endian机器上不需要)*/
sin.sin_addr.s_addr=inet_addr(“74.125.224.72”);//谷歌的ip地址
memset(数据报,04096);/*将缓冲区归零*/
/*我们现在将填写ip/tcp头值,请参见上面的解释*/
iph->ip_hl=5;
iph->ip_v=4;
iph->ip_tos=0;
iph->ip_len=sizeof(struct ip)+sizeof(struct tcphdr);/*无有效负载*/
iph->ip_id=htonl(54321);/*此处的值无关紧要*/
iph->ip_off=0;
iph->ip_ttl=255;
iph->ip_p=6;
iph->ip_sum=0;/*在以后计算实际校验和之前将其设置为0*/
iph->ip_src.s_addr=inet_addr(“1.2.3.4”);/*可以盲目欺骗SYN*/
iph->ip_dst.s_addr=sin.sin_addr.s_addr;
tcph->th_sport=htons(1234);/*任意端口*/
tcph->th_dport=htons(P);
tcph->th_seq=random();/*在SYN数据包中,序列是随机的*/
tcph->th_ack=0;/*编号,且第一个数据包中的ack序列为0*/
tcph->th_x2=0;
tcph->THU off=0;/*第一个也是唯一的tcp段*/
tcph->th_flags=th_SYN;/*初始连接请求*/
tcph->THU win=htonl(65535);/*允许的最大窗口大小*/
tcph->th_sum=0;/*如果将校验和设置为零,则内核的IP堆栈
在传输过程中应填写正确的校验和*/
tcph->th_urp=0;
iph->ip_sum=csum((无符号短*)数据报,iph->ip_len>>1);
/*最后,最好打一个IP_HDRINCL电话,以确保
内核知道头包含在数据中,而不知道
在我们的数据之前将它自己的头插入数据包*/
int-one=1;
常量int*val=&one;
如果(设置锁定选项(s、IPPROTO_IP、IP_HDRINCL、val、尺寸(一个))<0)
printf(“警告:无法设置HDRINCL!\n”);
if(发送到(s,数据报,iph->ip_len,0,(struct sockaddr*)和sin,sizeof(sin))<0)
printf(“错误\n”);
其他的
printf(“.\n\n”);
睡眠(2);//给足够的时间接收响应
字符缓冲区[8192];/*单个数据包通常不大于8192字节*/
memset(缓冲区,08192);/*将缓冲区归零*/
而(recv(s,buffer,8192,0)>0)
printf(“捕获的tcp数据包:%s\n”,缓冲区+sizeof(struct iphdr)+sizeof(struct tcphdr));
关闭(s);;
返回0;
}
我得到的输出如下图所示:

尝试更改:

printf("Caught tcp packet: %s\n", buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
致:

不将
recv()
的结果存储在变量中永远是不正确的。您需要测试它是否为零和-1,或者使用它作为接收数据的长度。试试这个:

int count;
while ((count = recv(s, buffer, sizeof buffer, 0)) > 0)
    printf("Caught tcp packet: %.*s\n", count - sizeof(struct iphdr) - sizeof(struct tcphdr, buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
if (count < 0)
    perror("recv");
int计数;
而((count=recv(s,buffer,sizeof buffer,0))>0)
printf(“捕获的tcp数据包:%.*s\n”,count-sizeof(struct iphdr)-sizeof(struct tcphdr,buffer+sizeof(struct iphdr)+sizeof(struct tcphdr));
如果(计数<0)
perror(“recv”);

编辑您似乎试图打印SYN-ACK数据包的内容。没有任何内容。

您需要使用libpcap库从链接层检索数据包。

谢谢EJP,但我仍然收到类似的“垃圾字符”这是因为
count
是接收到的原始字节数,而不是要打印的字符数。您从来没有编写过任何代码来合理地接收数据并安排打印。您希望它能神奇地工作。@sam请参阅edit。但所有这些都遵循您的假设,即数据实际上是可打印的。首先LH,使用诸如查看网络上发生的事情之类的工具。其次,看起来您正在尝试这样做,在这种情况下,您将不会收到任何数据的回复。而且您甚至不检查是否收到数据的回复,您只需打印“数据”。我想程序只会在使用
recv()接收到某些数据时打印
函数。由于它是一个SYN数据包,我认为服务器应该发送一个ACK。因为您使用的是原始套接字,所以您收到的数据包将包含IP和TCP头,这些头将计入返回的大小。您的意思是:服务器的响应中不会有任何有效负载?如果您发送
SYN
数据包,您应该会收到
ACK+SYN
(或a
RST
)返回,该数据包将不会有任何有效负载。感谢Davider,但收到的响应将仅在iPhoneder和tcpheader之后给出清晰的输出。对。我在发布响应后意识到。
while (recv(s, buffer, 8192, 0) > 0)
    printf("Caught tcp packet: %s\n", buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
int count;
while ((count = recv(s, buffer, sizeof buffer, 0)) > 0)
    printf("Caught tcp packet: %.*s\n", count - sizeof(struct iphdr) - sizeof(struct tcphdr, buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
if (count < 0)
    perror("recv");