summaryrefslogtreecommitdiffstats
path: root/falgproto/falgproto-protocol-dns.c
blob: 2207922fa2756756da182a6babbe02c98b6728a5 (plain) (blame)
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/* vim: set sw=4 ts=4 sts=4 et: */

#include "config.h"
#include "falgproto.h"

#include <arpa/inet.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static inline int get_question_count (
    const char *pkt, size_t len, uint16_t *result) {

    if (len < 12) {
        return -1;
    }

    uint16_t result_ne;
    memcpy (&result_ne, pkt + 4, 2);
    *result = ntohs (result_ne);

    return 0;
}

/* XXX: We only inspect the first question in the packet, as we
 *      don't know which question should we use to decide the
 *      destination. */
static inline ssize_t get_question_name (
    const char *pkt_s, size_t len, char *out_s) {

    /* String length are always unsigned. */
    const unsigned char *pkt = (const unsigned char*)pkt_s;
    unsigned char *out = (unsigned char*)out_s;

    /* We assume get_question_count are called before this function, so
     * we don't get a malformed or truncated packet */
    ssize_t i = 12, j = 0;
    for (; i < len && pkt[i] != 0; j++) {
        unsigned int label_len = pkt[i];
        for (i++; i < len && label_len > 0; i++, j++, label_len--) {
            if (out != NULL) {
                out[j] = pkt[i];
            }
        }
        if (i >= len) {
            return -1;
        }
        if (out != NULL) {
            if (pkt[i] != 0) {
                out[j] = '.';
            } else {
                out[j] = '\0';
            }
        }
    }

    if (pkt[i] != 0) {
        return -1;
    }

    return i - 12 - 1;
}

FALGPROTO_PARAM_GETTER_DECL (dns) {

    uint16_t question_count;
    if (get_question_count (pkt, len, &question_count) < 0) {
        return (FalgprotoParam) { .result = FALGPROTO_PARAM_RESULT_TRUNCATED };
    }
    if (question_count == 0) {
        return (FalgprotoParam) { .result = FALGPROTO_PARAM_RESULT_NOT_FOUND };
    }

    ssize_t question_name_len = get_question_name (pkt, len, NULL);
    if (question_name_len < 0) {
        return (FalgprotoParam) { .result = FALGPROTO_PARAM_RESULT_TRUNCATED };
    }

    char *question_name = malloc (question_name_len + 1);
    if (question_name == NULL) {
        return (FalgprotoParam) { .result = FALGPROTO_PARAM_RESULT_ERROR };
    }

    get_question_name (pkt, len, question_name);
    return (FalgprotoParam) {
        .param   = question_name,
        .len     = question_name_len,
        .dup     = true,
        .result  = FALGPROTO_PARAM_RESULT_OK };
}

FALGPROTO_PRINTER_DECL (dns) {

    uint16_t question_count;
    if (get_question_count (pkt, len, &question_count) < 0) {
        fputs ("DNS: Cannot get question count\n", fp);
        return;
    }

    fprintf (fp, "DNS: Question count: %" PRIu16 "\n", question_count);
    if (question_count == 0) {
        fputs ("DNS: Why the question count is zero?\n", fp);
        return;
    }

    ssize_t question_name_len = get_question_name (pkt, len, NULL);
    if (question_name_len < 0) {
        fputs ("DNS: Malformed question name\n", fp);
        return;
    }

    char question_name[question_name_len + 1];
    get_question_name (pkt, len, question_name);
    fputs ("DNS: Question name: ", fp);
    fputs (question_name, fp);
    fputc ('\n', fp);
    fprintf (fp, "DNS: Question name length: %zd\n", question_name_len);
}