在C中实现TCP握手

时间:2014-10-12 08:26:03

标签: c linux tcp raw-sockets

我已经使用TCP原始套接字来实现简单的TCP握手,以便创建一个简单的端口扫描程序。在这个端口扫描程序中,我想要遵循方法半开。正如我们在这种方法中所知,我们首先将SYN数据包发送到远程主机,然后:

  • 如果主机以SYN / ACK响应,则该端口处于打开状态,我们应该重置连接。
  • 如果主机使用RST响应,则该端口已关闭,我们已完成。
  • 如果主机没有响应,那么我们可以考虑过滤端口。

我已经完成了这种方法的实施。但我觉得我的代码有问题。例如,当我将SYN发送到Web服务器的端口80时,它会重置与RST数据包的连接

任何人都可以请指出我的代码中的错误是什么?提前致谢

请注意,我使用ubuntu 14.04作为操作系统,eclipse使用IDE

int scan(char *hostip, int port){
    // file descriptor for the socket
    int fd;
    // tcp header length
    int tcphdr_len = sizeof(struct tcphdr);
    // buffers containing raw packets
    char send_buffer[PACKET_LENGTH], recv_buffer[PACKET_LENGTH];
    // tcp header segment of raw packet
    struct tcphdr *tcp = (struct tcphdr *) send_buffer;
    // address of the destination entry point
    struct sockaddr_in addr;
    // size of received packet
    int recvd_size;
    int one = 1, result;

    // clearing whole of the packet
    memset(send_buffer, 0, PACKET_LENGTH);
    memset(recv_buffer, 0, PACKET_LENGTH);
    // creating raw socket
    fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if(fd < 0){
       die("failed to create socket");
    }
    // address family
    addr.sin_family = AF_INET;
    // destination port
    addr.sin_port = htons(port);
    // destination ip
    addr.sin_addr.s_addr = inet_addr(hostip);

    // the tcp structure
    // source port
    tcp->source = htons(12345);
    // destination port
    tcp->dest = addr.sin_port;
    tcp->seq = htonl(1);
    tcp->doff = 5;
    tcp->syn = 1;
    tcp->window = htons(65535);

    if(sendto(fd, send_buffer, tcphdr_len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0){
       die("sendto failed");
    }

    recvd_size = recvfrom(fd, recv_buffer, PACKET_LENGTH, 0, 0, 0);
    if(recvd_size < 0){ // error occurred
        close(fd);
        die("failed to receive packet");
    }
    if(recvd_size == 0){ // timed out
        close(fd);
        return ST_FILTERED;
    }
    result = process_packet(recv_buffer, recvd_size);
    if(result == PCK_UNKNOWN){
        close(fd);
        return ST_UNKNOWN;
    }
    if(result == PCK_RST){
        close(fd);
        return ST_CLOSED;
    }

    close(fd);
    return ST_OPEN;
}

这是函数process_packet

int process_packet(char *buffer, int size){
    struct tcphdr *tcp = (struct tcphdr *) buffer;
    fprintf(stdout, "\nsport=%i\ndport=%i\nsyn=%i\nack=%i\nrst=%i\nrecv size=%i\n", ntohs(tcp->source),
            ntohs(tcp->dest),
            tcp->syn,
            tcp->ack,
            tcp->rst,
            size);
    if(tcp->syn & tcp->ack){
        return PCK_SYN_ACK;
    }
    if(tcp->rst){
        return PCK_RST;
    }
    return PCK_UNKNOWN;
}

这是我的./myprg -t 81.31.160.39

程序的输出
target=81.31.160.39 *** port=80 *** status=
sport=17664
dport=1400
syn=0
ack=0
rst=1
recv size=1024
closed

此外,目标端口应为12345,但不是。

1 个答案:

答案 0 :(得分:0)

您最好在wireshark中查看数据包,并尝试确定哪些字段有误。

然而documentation说:

  

用于接收IP标头始终包含在数据包中。

这意味着收到的数据以IP头开头,你的process_packet()必须在你到达tcp头之前解析它。