Как я могу отправить ответ HTTP, используя сырые сокеты Linux в c
В последнее время я пытался закодировать веб-сервер, используя linux raw sockets на c, для образования. Если мое понимание верно, вот как должен действовать идеальный сервер:
| [SYN] |
|<--------------|
| [SYN] [ACK] |
|-------------->|
| [ACK] |
|<--------------|
|GET / HTTP/1.1 |
|<--------------|
| [ACK] |
|-------------->|
Server |HTTP/1.1 200 OK| Client
|-------------->|
| [ACK] |
|<--------------|
| [FIN] [ACK] |
|<--------------|
| [FIN] [ACK] |
|-------------->|
| [ACK] |
|<--------------|
| [ACK] |
|-------------->|
Тем не менее, попытаться реализовать это гораздо сложнее, чем кажется. Прямо сейчас у меня есть код, который может без проблем получить подтверждение запроса HTTP. Тем не менее, я не могу найти в Интернете нигде, который объясняет, как отправить фактический ответ. Я пробовал так много вещей, но ничего, что я могу найти, не сработает. Вот как я заполняю пакет:
//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));
Но очевидно, что это неправильно, потому что клиент не подтверждает этот пакет, когда я отправляю его, фактически он действует точно так же, как если бы я вообще не отправлял пакет.
Вопросы:
-Как правильно заполнить ответный пакет HTTP?
-Какие были ошибки в том, как я его заполнял?
-Наконец, любые советы по поводу моего кода приветствуются (за исключением совета, в котором говорится, чтобы я держался подальше от сырых розеток!!)
РЕДАКТИРОВАТЬ: Одна последняя деталь: когда вы используете wire shark, чтобы прослушать HTTP-ответ, отправляемый между сервером и клиентом, он идентифицирует пакет по протоколу HTTP, а не по протоколу TCP. Я не идентифицирую свой пакет как протокол HTTP, а называю его сегментом повторно собранного блока PDU. надеюсь, это поможет вам с вопросом 2.
заранее спасибо