Sendto работает только на локальном хосте

Я пытался это исправить, но думаю, мне нужна ваша помощь. Я новичок и не могу найти проблему.

Код работает, когда для источника и назначения задано значение 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, не уверен, нормально ли это. Это нормально?

Как я могу заставить компилятор выдавать больше информации об ошибке, когда он ее обнаруживает?

Когда он вернет -1, прочитайте errno, чтобы понять, что пошло не так. Обычно форматируйте это чем-то вроде strerror.

Jerry Coffin 24.07.2024 03:36

Есть ли причина, по которой вы используете необработанные сокеты и создаете свои собственные датаграммы для отправки вместо использования простых стандартных функций TCP?

Some programmer dude 24.07.2024 03:41

@JerryCoffin Я попробовал errno, как показано ниже, но ничего не показывает: char errBuffer[256]; strerror_r(errno, errbuffer, 256); std:cout << "Ошибка: " << errbuffer << std:endl;

X-Maki 24.07.2024 04:07

Измените fprintf(stderr, "sendto() error!!!.\n"); на fprintf(stderr, "sendto() error: %s\n", strerror(errno));. Когда вы получаете ошибку sendto, каков точный результат? Пожалуйста, отредактируйте свой вопрос, чтобы сообщить нам (скопируйте и вставьте результат).

Some programmer dude 24.07.2024 05:48

Еще пара замечаний: в коде, который вы показываете, есть некоторые вещи... странные. Например, sizeof "255.255.255.255" как размер массива вместо макроса. Или const int *val = &tmp; вместо использования &tmp непосредственно в вызове setsockopt. Или используйте fprintf в программе на C++. На самом деле в показанном коде вообще нет кода, специфичного для C++, все это простой C. И использование устаревшей функции random, и использование ее для порядкового номера TCP. Использование глобальных переменных вместо аргументов функции. Небольшие несоответствия, которые просто вызывают странное ощущение.

Some programmer dude 24.07.2024 05:57

@Someprogrammerdude Вот вывод: ошибка sendto(): сообщение слишком длинное

X-Maki 24.07.2024 06:00

Вместо того, чтобы использовать fprintf() с strerror() вручную, вы можете вместо этого использовать perror(), который обрабатывает печать errno в stderr за вас, например: perror("sendto() error");

Remy Lebeau 24.07.2024 06:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В этом комментарии вы сказали:

Это вывод: ошибка 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.

X-Maki 24.07.2024 06:28

@Xmaki «[Неопределенное поведение] — это путь ко многим способностям, которые некоторые считают неестественными».

Remy Lebeau 24.07.2024 06:32

Огромное спасибо, Реми. Снимаю перед вами шляпу. Мне пришлось прочитать ваш комментарий 3 раза и буквально вытащить свой научный калькулятор. Вы решили проблему! Вы только что дали мне понять, почему HTONS и тому подобное необходимо использовать для TCP. Большое спасибо ! Теперь я могу спать. :п

X-Maki 24.07.2024 14:48

Другие вопросы по теме