Lately I have been trying to code a webserver using linux raw sockets in c, for the point of education. If my understanding is correct, this is how the ideal server should act:
| [SYN] |
|<--------------|
| [SYN] [ACK] |
|-------------->|
| [ACK] |
|<--------------|
|GET / HTTP/1.1 |
|<--------------|
| [ACK] |
|-------------->|
Server |HTTP/1.1 200 OK| Client
|-------------->|
| [ACK] |
|<--------------|
| [FIN] [ACK] |
|<--------------|
| [FIN] [ACK] |
|-------------->|
| [ACK] |
|<--------------|
| [ACK] |
|-------------->|
However, trying to actually implememt this is a lot harder than it looks. Right now, I have code that can get up to the acknowledgement to the HTTP request without any problems. However, I cannot find anywhere on the internet that explains how to send the actual response. I have tried so many things, but nothing I can find will work. Here is how I am filling out the packet:
//allocate a packet to send:
uint8_t* httppack=(uint8_t*)malloc(IP_MAXPACKET*sizeof(uint8_t));
//allocate and fill out the payload:
char* payload=malloc(100*sizeof(char));
payload="HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n<!doctype html><html><head><title>Evan's Webpage</title></head><body><h1>Evan's Webpage</h1></body></html>\r\n";
int payloadlen=strlen(payload);
//IP header
struct ip* iph=(struct ip*)httppack;
iph->ip_hl = 20/sizeof(uint32_t);
iph->ip_v = 4;
iph->ip_tos = 0;
//Flags: none set
iph->ip_off = htons ((0 << 15) + (0 << 14) + (0 << 13) + 0);
iph->ip_ttl = 255;
iph->ip_p = IPPROTO_TCP;
if (inet_pton (AF_INET, "192.168.1.141", &(iph->ip_src)) != 1) {
printf("Failed to convert source IP to network form\n");
exit (EXIT_FAILURE);
}
if (inet_pton (AF_INET, "192.168.1.114", &(iph->ip_dst)) != 1) {
printf("Failed to convert target IP to network form\n");
exit (EXIT_FAILURE);
}
//Since we don't know the checksum yet, make it zero:
iph->ip_sum = 0;
//Length is IP header (20) + TCP header (no options, 20) and payload
iph->ip_len = htons (20+20+payloadlen);
//ID is 2 because it is the third packet being sent (first was 0)
iph->ip_id = htons (2);
//IP Checksum function (I did not include it in this example, just assume that it works)
iph->ip_sum = chksm ((uint16_t *)iph, 20);
//TCP header
struct tcphdr* tcph=(struct tcphdr*)(httppack+sizeof(struct ip));
//source: http
tcph->source = htons (80);
//reserved: 0
tcph->res1 = 0;
//TCP header size: 20 (no options)
tcph->doff = 20/4;
tcph->rst = 0;
tcph->urg = 0;
tcph->res2 = 0;
//Window: max size
tcph->window = htons (65535);
tcph->urg_ptr = htons (0);
//We dont know what port the client will be requesting from at first. receivetcph is the TCP header of the last packet we received (HTTP request) so the dest port will be wherever that packet was sent from
tcph->dest = receivetcph->source;
// The sequence will simply be whatever the last packet's ack sequence was (Which will be 1)
tcph->seq = receivetcph->ack_seq;
//The acknowledgement sequence is the HTTP request packet's sequence (htseq) + the amount of data we received - ethernet header - IP header - TCP header (rsize). I am fairly confident this is not the problem with my code, so assume that it works
tcph->ack_seq = htonl(htseq+rsize);
//TCP Flags: ACK and PSH
tcph->syn = 0;
tcph->fin = 0;
tcph->ack = 1;
tcph->psh = 1;
//Another Checksum function, again, just assume it works
tcph->check = tcpchksm (*cip, *ctcp, (uint8_t*)payload, payloadlen);
//Copy payload to buffer before we send
memcpy((httppack+20+20), payload, payloadlen*sizeof(uint8_t));
But obviously this is wrong because the client does not acknowledge this packet when I send it, in fact it acts exactly as if I did not send the packet at all.
Questions:
-What is the correct way to fill out the HTTP response packet?
-What were the errors to the way I was filling it out?
-Finally, any advice about my code is appreciated (EXCEPT ADVICE TELLING ME TO STAY AWAY FROM RAW SOCKETS!!)
EDIT: One last detail: when you use wire shark to sniff an http response being sent between a server and a client, it identifies the packet with an HTTP protocol, instead of a TCP protocol. I'd does not identify my packet as an HTTP protocol, and calls it a segment of a reassembled PDU. hopefully this will help you with question 2.
Thanks in advance