Hi I'm Frogatto Hi I'm Frogatto - 1 month ago 22
C Question

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.

nos nos
Answer

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.