Я пытался это исправить, но думаю, мне нужна ваша помощь. Я новичок и не могу найти проблему.
Код работает, когда для источника и назначения задано значение localhost. Но когда я использую реальные IP-адреса, отличные от localhost, как для источника, так и для назначения, это терпит неудачу. sendto()
возвращает -1.
Я не знаю, как заставить `sendto() выдавать более подробную информацию об ошибке или о том, что заставляет ее возвращать -1.
Я использую Linux, кстати.
Вот код:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define MAX_PACKET_SIZE 4096
u_int32_t src_addr, dst_addr;
u_int16_t src_port, dst_port;
unsigned short csum (unsigned short *buf, int nwords)
{
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
void setup_ip_header(struct iphdr *iph)
{
iph->saddr = src_addr; // SOURCE IP ADDRESS
iph->daddr = dst_addr; // TARGET IP ADDRESS
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr));
iph->id = htonl(54321); /*setup any arbitrary number for id */
iph->frag_off = 0;
iph->ttl = MAXTTL;
iph->protocol = 6; /* upper layer protocol, TCP*/
iph->check = 0;
}
void setup_tcp_header(struct tcphdr *tcph)
{
tcph->source = htons(src_port); // SOURCE PORT
tcph->dest = htons(dst_port); // TARGET PORT
tcph->seq = random();
tcph->ack_seq = 0;
tcph->res2 = 0;
tcph->doff = 5;
tcph->syn = 1;
tcph->window = htonl(65535);
tcph->check = 0;
tcph->urg_ptr = 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
struct sockaddr_in sin;
const char *src_addr_str = "192.168.1.50";
src_addr = inet_addr(src_addr_str);
src_port = 777;
const char *dst_addr_str = "192.168.1.51";
dst_addr = inet_addr(dst_addr_str);
dst_port = 888;
char datagram[MAX_PACKET_SIZE];
struct iphdr *iph = (struct iphdr *)datagram;
struct tcphdr *tcph = (struct tcphdr *)((u_int8_t *)iph + (5 * sizeof(u_int32_t)));
char new_ip[sizeof "255.255.255.255"];
int src_socket = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
if (src_socket < 0){
fprintf(stderr, "Could not open raw socket.\n");
exit(-1);
}
/* a IP_HDRINCL call, to make sure that the kernel knows
* the header is included in the data, and doesn't insert
* its own header into the packet before our data
*/
int tmp = 1;
const int *val = &tmp;
if (setsockopt(src_socket, IPPROTO_IP, IP_HDRINCL, val, sizeof (tmp)) < 0){
fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!\n");
exit(-1);
}
// SETUP DETAILS ABOUT THE TARGET MACHINE
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = dst_addr;
sin.sin_port = htons(dst_port);
// Clear the data
memset(datagram, 0, MAX_PACKET_SIZE);
// Set appropriate fields in headers
setup_ip_header(iph);
setup_tcp_header(tcph);
iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
for(;;){
int ret = sendto(src_socket, datagram, iph->tot_len, 0, (struct sockaddr *) &sin, sizeof(sin));
if (ret == -1)
{
fprintf(stderr, "sendto() error!!!.\n");
}
else
{
fprintf(stdout, "Flooding %s at %u...\n", dst_addr_str , dst_port);
}
sleep(1);
}
return 0;
}
Я также заметил, что sizeof(sin)
возвращает только 16, не уверен, нормально ли это. Это нормально?
Как я могу заставить компилятор выдавать больше информации об ошибке, когда он ее обнаруживает?
Есть ли причина, по которой вы используете необработанные сокеты и создаете свои собственные датаграммы для отправки вместо использования простых стандартных функций TCP?
@JerryCoffin Я попробовал errno, как показано ниже, но ничего не показывает: char errBuffer[256]; strerror_r(errno, errbuffer, 256); std:cout << "Ошибка: " << errbuffer << std:endl;
Измените fprintf(stderr, "sendto() error!!!.\n");
на fprintf(stderr, "sendto() error: %s\n", strerror(errno));
. Когда вы получаете ошибку sendto
, каков точный результат? Пожалуйста, отредактируйте свой вопрос, чтобы сообщить нам (скопируйте и вставьте результат).
Еще пара замечаний: в коде, который вы показываете, есть некоторые вещи... странные. Например, sizeof "255.255.255.255"
как размер массива вместо макроса. Или const int *val = &tmp;
вместо использования &tmp
непосредственно в вызове setsockopt
. Или используйте fprintf
в программе на C++. На самом деле в показанном коде вообще нет кода, специфичного для C++, все это простой C. И использование устаревшей функции random
, и использование ее для порядкового номера TCP. Использование глобальных переменных вместо аргументов функции. Небольшие несоответствия, которые просто вызывают странное ощущение.
@Someprogrammerdude Вот вывод: ошибка sendto(): сообщение слишком длинное
Вместо того, чтобы использовать fprintf()
с strerror()
вручную, вы можете вместо этого использовать perror()
, который обрабатывает печать errno
в stderr
за вас, например: perror("sendto() error");
В этом комментарии вы сказали:
Это вывод: ошибка sendto(): сообщение слишком длинное.
Вы устанавливаете iph->tot_len
в результат htons()
, а затем передаете это значение как есть в sendto()
в качестве размера данных. В системе с прямым порядком байтов в качестве размера данных вам придется передать очень большое число. В вашем случае 40 (0x0028) преобразуется в 10240 (0x2800), что вызывает неопределенное поведение, когда sendto()
пытается отправить больше данных, чем вы предоставили.
Поле iph->tot_len
должно быть в сетевом порядке байтов, но значение, которое вы передаете sendto()
, должно быть в порядке байтов хоста, поэтому вам придется преобразовать значение iph->tot_len
обратно с помощью ntohs()
:
sendto(..., ntohs(iph->tot_len), ...);
Лучше было бы сохранить размер в отдельной переменной, а затем преобразовать его только для iphdr
, например:
u_short len = sizeof(struct iphdr) + sizeof(struct tcphdr);
...
iph->tot_len = htons(len);
...
sendto(..., len, ...);
Я попробую, когда вернусь позже. Я сообщу вам, как только смогу попробовать. Однако я не понимаю, почему это работает, когда я использую «127.0.0.1» (localhost) как для src_addr, так и для dst_addr.
@Xmaki «[Неопределенное поведение] — это путь ко многим способностям, которые некоторые считают неестественными».
Огромное спасибо, Реми. Снимаю перед вами шляпу. Мне пришлось прочитать ваш комментарий 3 раза и буквально вытащить свой научный калькулятор. Вы решили проблему! Вы только что дали мне понять, почему HTONS и тому подобное необходимо использовать для TCP. Большое спасибо ! Теперь я могу спать. :п
Когда он вернет -1, прочитайте
errno
, чтобы понять, что пошло не так. Обычно форматируйте это чем-то вродеstrerror
.