[C] Allereerste recvfrom heeft als source altijd 0.0.0.0

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • zeroxcool
  • Registratie: Januari 2001
  • Laatst online: 09:59
Ik ben al een tijdje bezig met een op UDP gebaseerde netwerk daemon. Er gaat echter wat mis bij het eerste UDP packet wat arriveert...

Ik bind m'n daemon op UDP poort 53 (DNS inderdaad), en wil genotified worden wanneer er een packet arriveert. Dat werkt allemaal prima, zowel met select(2) als met poll(2). Als ik na deze notify echter het eerste UDP packet ophaal, dan is het source adres daarvan altijd 0.0.0.0. Dat is dus het source adres wat recvfrom(2) teruggeeft.

Ik heb het voorbeeld proberen te beperken tot de kern, met onderstaand fragment, wat nog redelijk groot is, maar mijn inziens wel de essentie van het probleem vat.

Het vage is nou, dat volgens mij de optimizer van de compiler (gcc in mijn geval) een rol speelt. Compile en run ik onderstaande code zonder een optimizer flag:
code:
1
2
cc -o udpserver -Wall udpserver.c
./udpserver 0.0.0.0 53


En ik stuur een DNS request:
code:
1
dig example.org @127.0.0.1

Dan zie je dus dat de 1e request niet terug komt (de request komt volgens recvfrom(2) van 0.0.0.0, waar ook de response heen gaat). Alle hierop volgende packets hebben wel het juiste source adres. En aangezien dig na een seconde of 3 een nieuw packet stuurt, komt er uiteindelijk wel een (ja, ik weet: malformed) antwoord.

Echter, compile ik vervolgens de code als volgt:
code:
1
cc -o udpserver -Wall -O2 udpserver.c

Dan komt wel het juiste source adres binnen, bij het eerste packet.


Iemand een idee wat er mis zou kunnen zijn?

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>

int ip_parse(uint32_t *out, const char *in) {
    struct in_addr addr;
    if (!inet_aton(in, &addr)) {
        return 0;
    } else {
        *out = addr.s_addr;
        return 1;
    }
}

static int usage(const char *argv0) {
    fprintf(stderr, "usage: %s <IP address> <UDP port number>\n", argv0);
    return 1;
}

int main(int argc, char *argv[]) {
    unsigned long port;
    int nrready, sock, n;
    char buf[4096], *endptr;
    uint32_t bind_ip;
    struct sockaddr_in sock_out, sock_in;
    socklen_t sock_outlen;
    struct pollfd packet;
    ssize_t recv;
    uint16_t dns_id;

    memset(&sock_in, 0, sizeof(sock_in));
    memset(&sock_out, 0, sizeof(sock_out));

    if (argc < 3)
        return usage(argv[0]);

    if (!ip_parse(&bind_ip, argv[1]))
        return usage(argv[0]);

    port = strtoul(argv[2], &endptr, 10);
    if (*endptr)
        return usage(argv[0]);

    if ((port == 0) || (port > 65535)) {
        fprintf(stderr, "Port number out of range (1..65535)\n");
        return 1;
    }

    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) {
        perror("open socket failed");
        return 1;
    }

    sock_in.sin_family = AF_INET;
    sock_in.sin_addr.s_addr = bind_ip;
    sock_in.sin_port = htons(port);

    do {
        n = bind(sock, (struct sockaddr *) &sock_in, sizeof(sock_in));
        if (n) {
            if (errno == EADDRINUSE || errno == ENOMEM) {
                fprintf(stderr, "address in use, sleeping and trying again automatically\n");
                sleep(1);
                continue;
            }
            perror("bind failed");
            return 1;
        }
    } while (n);

    packet.fd = sock;
    packet.events = POLLIN;

    for ( ; ; ) {
        nrready = poll(&packet, 1, -1);

        if (packet.revents & POLLIN) {
            do {
                recv = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &sock_out, &sock_outlen);
            } while ((recv == -1) && (errno == EINTR));

            if (recv < 12)
                continue;

            dns_id  = buf[0] << 8;
            dns_id |= buf[1];

            printf("received DNS query (id = %d, from %s:%d)\n", dns_id, inet_ntoa(sock_out.sin_addr), sock_out.sin_port);

            buf[2] = 0x84;
            buf[3] = 0;

            sendto(sock, buf, recv, MSG_DONTWAIT, (struct sockaddr *) &sock_out, sock_outlen);
        }
    }

    return 0;
}

zeroxcool.net - curity.eu


Acties:
  • 0 Henk 'm!

  • jschot
  • Registratie: Oktober 2002
  • Laatst online: 09-07 09:46
Het probleem is dat je de variabele sock_outlen niet initialiseert. Hierdoor geef je de allereerste recvfrom() op regel 87 een adreslengte van 0 mee.

Acties:
  • 0 Henk 'm!

  • zeroxcool
  • Registratie: Januari 2001
  • Laatst online: 09:59
Aaargh onderhand, dat ik daar overheen heb gekeken. Waarschijnlijk met de gedachte dat dit enkel een output variabele is. Niet dus. Heel hartelijk bedankt!

zeroxcool.net - curity.eu