Неверный порт при запуске (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)


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

Параметр длины адреса сокета, шестой параметр функции 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