Неверный порт при запуске (recvfrom)

Я наткнулся на странное поведение при написании небольшого DNS-сервера и урезал его до минимума. Эта программа должна прослушивать 127.0.0.1:1337 для DNS-запросов и отвечать отказом. Я проверяю его поведение, выдавая dig @localhost -p 1337 foo.bar. Если строка 48 закомментирована //char bout[bufferSize]; // <-- WTF, она работает как шарм.

программа:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

static int SOCKET;


void bindSocket(){
    int s = socket (AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
        fprintf (stderr, "Could not create socket\n");
        exit (EXIT_FAILURE);
     }

    struct sockaddr_in address;
    memset((char *)&address, 0, sizeof(address));

    inet_aton("127.0.0.1", &address.sin_addr);
    address.sin_family  = AF_INET;
    address.sin_port    = htons(1337);

    int rc = bind ( s, (struct sockaddr *) &address, sizeof (address) );
    if (rc < 0) {
         fprintf (stderr, "Could not bind Socket\n %s \n", strerror(errno));
         exit (EXIT_FAILURE);
    }
    SOCKET = s;
}

void decline(uint16_t err, char *bin, struct sockaddr *to){
    char bout[12];
    memset((bout + 4), 0, 8);
    memcpy(bout, bin, 4);
    bout[2] = (bout[2] | 0x80) & 0xFE;
    bout[3] = (bout[3] | err ) & 0x7F;
    sendto( SOCKET, bout, 12, 0, to, sizeof(struct sockaddr) );
}

void hereBeDragons(){
    size_t bufferSize = 512;
    char bin[bufferSize];
    char bout[bufferSize]; // <-- WTF
    struct sockaddr sender;
    socklen_t len;
    while(1){
        memset(bin,  0, bufferSize);
        int n = recvfrom( SOCKET, bin, bufferSize, 0, &sender, &len );
        if (n < 0) continue;
        puts("receved a query");

        /* Strictly decline all invalid queries */
        decline( 2, bin, &sender);
    }
}

int main(){
    bindSocket();
    hereBeDragons();
    return EXIT_FAILURE;
}

вывод программы:

received a query

вывод копать:

; <<>> DiG 9.9.5-3-Ubuntu <<>> @localhost -p 1337 foo.bar.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37520
;; flags: qr ad; QUERY: 0, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; Query time: 1 msec
;; SERVER: 127.0.0.1#1337(127.0.0.1)
;; WHEN: Fri Aug 29 21:37:46 CEST 2014
;; MSG SIZE  rcvd: 12

Это всего лишь урезанный пример, в реальном коде бой используется для построения правильного ответа на входящие запросы. Но когда я оставляю его, появляется следующая проблема:

Захват пакетов Wireshark

программа отправляет ответ не на тот порт, через 5 секунд dig повторяет попытку и программа отправляет на правильный порт.

Что я делаю не так?


person charly    schedule 29.08.2014    source источник
comment
Если вы не используете бой в функции, то зачем его инициализировать??? Или это другой код??   -  person Arpit    schedule 30.08.2014
comment
Код, который является частью вопроса, должен быть включен в вопрос, а не опубликован где-либо еще.   -  person user207421    schedule 30.08.2014


Ответы (1)


arrow_upward
2
arrow_downward

Это очень тонкий баг.

Параметр длины адреса сокета, шестой параметр функции recvfrom(), должен быть инициализирован. Он должен быть установлен, чтобы указать длину адресного буфера, переданного в пятом параметре. Когда recvfrom() возвращает значение, длина обновляется, чтобы отразить фактический размер записанной в нее структуры сетевого адреса.

Перед вызовом recvfrom() добавьте:

len=sizeof(sender);

Это объясняется на справочной странице recvfrom(2):

Аргумент addrlen — это аргумент «значение-результат», который вызывающая сторона должна инициализировать до вызова размера буфера, связанного с src_addr, и изменить его по возвращении, чтобы указать фактический размер исходного адреса.

person Sam Varshavchik    schedule 30.08.2014
comment
Разве srcaddr и addrlen не являются 5-м и 6-м параметрами recvfrom, а не 3-м и 4-м? - person Ross Patterson; 30.08.2014
comment
Да, я ошибся - отредактировал, чтобы внести эту поправку. - person Sam Varshavchik; 30.08.2014
comment
вау, моя справочная страница устарела. Спасибо за ответ, к сожалению, я не могу проголосовать за самолет. - person charly; 31.08.2014