Search code examples
clinuxtcpraw-sockets

Implement TCP hand shake in C


I've used TCP raw sockets to implement a simple TCP handshake in order create a simple port scanner, In this port scanner I want to follow method half-open. As we know in this method, we first send a SYN packet to the remote host and then:

  • If host responds with SYN/ACK then that port is open and we should reset the connection.
  • If host responds with RST then that port is closed and we'r done.
  • If host does not respond then we can consider that port is filtered.

I've done implementation of such method. But I think something is wrong with my code. For example when I send a SYN to port 80 of a web server it reset the connection with RST packet

Can anyone please point me out what's my mistake in my codes? Thanks in advance

Note that I'm using ubuntu 14.04 as OS and eclipse as 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;
}

And here's the function 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;
}

And here's the output of my program for ./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

Also destination port should be 12345 but it's not.


Solution

  • You're better off looking at your packets in wireshark, and try to figure out which fields are wrong.

    However the documentation says:

    For receiving the IP header is always included in the packet.

    That means the received data starts with the IP header, and your process_packet() will have to parse that before you get to the tcp header.