raw socket模拟TCP三次握手

今天花了一天时间,研究了一下如何利用原始套接字模拟TCP的三次握手。因为前几天一直在关于linux下c语言的socket编程,也看的差不多了,今天就敲了一天的代码。但是遇到了一个问题,当我利用raw socket发送SYN报文请求连接后,也能收到对方发来的SYN+ACK报文,当我发送ACK之前,本机系统会自动发送一个RST报文。然后,我在网上搜了一下,也有人遇到我这样的情况,有人给出如下解决方法:

一、反编译内核,提取connect的信息,进行分析,看怎么记录连接信息的,自己加连接信息,我晕,不太好办。
二、做个IP报文拦截器,直接过滤掉rst报文。NND太暴了:),咱是文化人,不能用这招。

三、不让TCP层收到ACK响应,那么我们就可以拦截这个报文并且把它过滤掉,这种方案也是可行的。

四、用winpcap拦截过滤,有兴趣去官方网站下载,比较简单。


也不知道这些法子到底可不可以,这两天再看看查阅别的资料试试,或者问问老师看看。


下面贴出我的实验代码供大家分享:

#include
#include
#include
#include
#include
#include
#include
#include

#define DST "202.196.64.206"  //目的IP
#define SRC "192.168.230.135" //源IP
#define SPORT 6666  //源端口号
#define DPORT 80    //目的端口

struct iphead{            //IP首部
    unsigned char ip_hl:4, ip_version:4;
    unsigned char ip_tos;
    unsigned short int ip_len;
    unsigned short int ip_id;
    unsigned short int ip_off;
    unsigned char ip_ttl;
    unsigned char ip_pro;
    unsigned short int ip_sum;
    unsigned int ip_src;
    unsigned int ip_dst;
};

struct tcphead{      //TCP首部
    unsigned short tcp_sport;
    unsigned short tcp_dport;
    unsigned int tcp_seq;
    unsigned int tcp_ack;
    unsigned char tcp_off:4, tcp_len:4;
    unsigned char tcp_flag;
    unsigned short tcp_win;
    unsigned short tcp_sum;
    unsigned short tcp_urp;
};

struct psdhead{ //TCP伪首部
    unsigned int saddr; //源地址
    unsigned int daddr; //目的地址
    unsigned char mbz;//置空
    unsigned char ptcl; //协议类型
    unsigned short tcpl; //TCP长度
};

unsigned short cksum(unsigned char packet[], int len){   //校验函数
    unsigned long sum = 0;
    unsigned short * temp;
    unsigned short answer;
    temp = (unsigned short *)packet;
    for( ; temp < packet+len; temp += 1)
        sum += *temp;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
    /*
    长度可能奇数,此处需完善
    */
}

int conn(int sendsockfd, int recsockfd, struct sockaddr_in seraddr){  // 三次握手
    unsigned char packet[sizeof(struct iphead) + sizeof(struct tcphead)];
    struct iphead* ip;
    struct tcphead* tcp;
    ip = (struct iphead*)packet;
    tcp = (struct tcphead*)(packet+sizeof(struct iphead));
    memset(packet, 0, sizeof(packet));
    /*以下分别设置IP,和TCP的首部,然后发送SYN报文段*/
    /*设置IP首部*/
    ip->ip_hl = 5;
    ip->ip_version = 4;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(struct iphead) + sizeof(struct tcphead));
    ip->ip_id = htons(13542); // random()
    ip->ip_off = htons(0x4000);
    ip->ip_ttl = 64;
    ip->ip_pro = IPPROTO_TCP;
    ip->ip_src = inet_addr(SRC);
    ip->ip_dst = inet_addr(DST);
    ip->ip_sum = cksum(packet, 20);  //计算IP首部的校验和,必须在其他字段都赋值后再赋值该字段,赋值前为0

    /*设置TCP首部*/
    int my_seq = 0; //TCP序号
    tcp->tcp_sport = htons(SPORT);
    tcp->tcp_dport = htons(DPORT);
    tcp->tcp_seq = htonl(my_seq);
    tcp->tcp_ack = htons(0);
    tcp->tcp_len = 5;  //发送SYN报文段时,设置TCP首部长度为20字节
    tcp->tcp_off = 0;
    tcp->tcp_flag = 0x02;  //SYN置位
    tcp->tcp_win = htons(29200);
    tcp->tcp_urp = htons(0);
    /*设置tcp伪首部,用于计算TCP报文段校验和*/
    struct psdhead psd;
    psd.saddr = inet_addr(SRC); //源IP地址
    psd.daddr = inet_addr(DST); //目的IP地址
    psd.mbz = 0;
    psd.ptcl = 6;  
    psd.tcpl = htons(20);

    unsigned char buffer[1000]; //用于存储TCP伪首部和TCP报文,计算校验码
    memcpy(buffer, &psd, sizeof(psd));
    memcpy(buffer+sizeof(psd), tcp, sizeof(struct tcphead));
    tcp->tcp_sum = cksum(buffer, sizeof(psd) + sizeof(struct tcphead));  //计算检验码

    /*发送SYN报文段*/
    int send = sendto(sendsockfd, packet, htons(ip->ip_len), 0,(struct sockaddr *)&seraddr, sizeof(seraddr));
    if(send < 0){
      printf("send failed   sendcode=%d\n", send);
      return -1;
    }
    unsigned char rec[1024];
    int n = recvfrom(recsockfd, rec, 1024, 0, NULL, NULL);  //接收SYN和ACK报文
    printf("receive %d bytes:\n", n);  //将接受的IP数据报输出
    for(int i=0; i> 4;  //因为TCP报文长度只占该字节的高四位,需要取出该四位的值
    tcpheadlen *= 4;   //以四个字节为单位
    printf("tcpheadlen:%d\n", tcpheadlen);
    buffer[11] = tcpheadlen;  //将TCP长度存入
    for(int i=0; iip_hl = 5;
            ip->ip_version = 4;
            ip->ip_tos = 0;
            ip->ip_len = htons(sizeof(struct iphead) + sizeof(struct tcphead));
            ip->ip_id = htons(13543); // random()
            ip->ip_off = htons(0x4000);
            ip->ip_ttl = 64;
            ip->ip_pro = IPPROTO_TCP;
            ip->ip_src = inet_addr(SRC);
            ip->ip_dst = inet_addr(DST);
            ip->ip_sum = cksum(packet, 20);  //计算IP首部的校验和,必须在其他字段都赋值后再赋值该字段,赋值前为0
            
            /*设置TCP首部*/
            my_seq ++;
            tcp->tcp_sport = htons(SPORT);
            tcp->tcp_dport = htons(DPORT);
            tcp->tcp_seq = htonl(my_seq);
            printf("op_seq:%d\n", op_seq);
            tcp->tcp_ack = ntohl(op_seq+1);
            tcp->tcp_len = 5;  //发送SYN报文段时,设置TCP首部长度为20字节
            tcp->tcp_off = 0;
            tcp->tcp_flag = 0x10;  //SYN置位
            tcp->tcp_win = htons(1000);
            tcp->tcp_urp = htons(0);
            
            /*设置tcp伪首部,用于计算TCP报文段校验和*/
    //        struct psdhead psd;
            psd.saddr = inet_addr(SRC); //源IP地址
            psd.daddr = inet_addr(DST); //目的IP地址
            psd.mbz = 0;
            psd.ptcl = 6;  
            psd.tcpl = htons(20);
            unsigned char buffer[1000]; //用于存储TCP伪首部和TCP报文,计算校验码
            memcpy(buffer, &psd, sizeof(psd));
            memcpy(buffer+sizeof(psd), tcp, sizeof(struct tcphead));
            tcp->tcp_sum = cksum(buffer, sizeof(psd) + sizeof(struct tcphead));  //计算检验码

            /*发送SYN报文段*/
            int send = sendto(sendsockfd, packet, htons(ip->ip_len), 0,(struct sockaddr *)&seraddr, sizeof(seraddr));
            if(send < 0){
                printf("send failed   sendcode=%d\n", send);
                return -1;
            }
            printf("已发送ACK报文,已创建TCP连接\n");
            
            n = recvfrom(recsockfd, rec, 1024, 0, NULL, NULL);  //接收IP数据报
            printf("receive %d bytes:\n", n);  //将接受的IP数据报输出
            for(int i=0; i


你可能感兴趣的:(网络编程)