• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Compares a (Q)NAME starting at udp[*ofs] with the target name.
3  *
4  * @param needle - non-NULL - pointer to DNS encoded target name to match against.
5  *   example: [11]_googlecast[4]_tcp[5]local[0]  (where [11] is a byte with value 11)
6  * @param needle_bound - non-NULL - points at first invalid byte past needle.
7  * @param udp - non-NULL - pointer to the start of the UDP payload (DNS header).
8  * @param udp_len - length of the UDP payload.
9  * @param ofs - non-NULL - pointer to the offset of the beginning of the (Q)NAME.
10  *   On non-error return will be updated to point to the first unread offset,
11  *   ie. the next position after the (Q)NAME.
12  *
13  * @return 1 if matched, 0 if not matched, -1 if error in packet, -2 if error in program.
14  */
FUNC(match_result_type match_single_name (const u8 * needle,const u8 * const needle_bound,const u8 * const udp,const u32 udp_len,u32 * const ofs))15 FUNC(match_result_type match_single_name(const u8* needle,
16                                     const u8* const needle_bound,
17                                     const u8* const udp,
18                                     const u32 udp_len,
19                                     u32* const ofs)) {
20     u32 first_unread_offset = *ofs;
21     bool is_qname_match = true;
22     int lvl;
23 
24     /* DNS names are <= 255 characters including terminating 0, since >= 1 char + '.' per level => max. 127 levels */
25     for (lvl = 1; lvl <= 127; ++lvl) {
26         u8 v;
27         if (*ofs >= udp_len) return error_packet;
28         v = udp[(*ofs)++];
29         if (v >= 0xC0) { /* RFC 1035 4.1.4 - handle message compression */
30             u8 w;
31             u32 new_ofs;
32             if (*ofs >= udp_len) return error_packet;
33             w = udp[(*ofs)++];
34             if (*ofs > first_unread_offset) first_unread_offset = *ofs;
35             new_ofs = (v - 0xC0) * 256u + w;
36             if (new_ofs >= *ofs) return error_packet;  /* RFC 1035 4.1.4 allows only backward pointers */
37             *ofs = new_ofs;
38         } else if (v > 63) {
39             return error_packet;  /* RFC 1035 2.3.4 - label size is 1..63. */
40         } else if (v) {
41             u8 label_size = v;
42             if (*ofs + label_size > udp_len) return error_packet;
43             if (needle >= needle_bound) return error_program;
44             if (is_qname_match) {
45                 u8 len = *needle++;
46                 if (len == label_size) {
47                     if (needle + label_size > needle_bound) return error_program;
48                     while (label_size--) {
49                         u8 w = udp[(*ofs)++];
50                         is_qname_match &= (uppercase(w) == *needle++);
51                     }
52                 } else {
53                     if (len != 0xFF) is_qname_match = false;
54                     *ofs += label_size;
55                 }
56             } else {
57                 is_qname_match = false;
58                 *ofs += label_size;
59             }
60         } else { /* reached the end of the name */
61             if (first_unread_offset > *ofs) *ofs = first_unread_offset;
62             return (is_qname_match && *needle == 0) ? match : nomatch;
63         }
64     }
65     return error_packet;  /* too many dns domain name levels */
66 }
67 
68 /**
69  * Check if DNS packet contains any of the target names with the provided
70  * question_type.
71  *
72  * @param needles - non-NULL - pointer to DNS encoded target nameS to match against.
73  *   example: [3]foo[3]com[0][3]bar[3]net[0][0]  -- note ends with an extra NULL byte.
74  * @param needle_bound - non-NULL - points at first invalid byte past needles.
75  * @param udp - non-NULL - pointer to the start of the UDP payload (DNS header).
76  * @param udp_len - length of the UDP payload.
77  * @param question_type - question type to match against or -1 to match answers.
78  *
79  * @return 1 if matched, 0 if not matched, -1 if error in packet, -2 if error in program.
80  */
FUNC(match_result_type match_names (const u8 * needles,const u8 * const needle_bound,const u8 * const udp,const u32 udp_len,const int question_type))81 FUNC(match_result_type match_names(const u8* needles,
82                               const u8* const needle_bound,
83                               const u8* const udp,
84                               const u32 udp_len,
85                               const int question_type)) {
86     u32 num_questions, num_answers;
87     if (udp_len < 12) return error_packet;  /* lack of dns header */
88 
89     /* dns header: be16 tid, flags, num_{questions,answers,authority,additional} */
90     num_questions = read_be16(udp + 4);
91     num_answers = read_be16(udp + 6) + read_be16(udp + 8) + read_be16(udp + 10);
92 
93     /* loop until we hit final needle, which is a null byte */
94     while (true) {
95         u32 i, ofs = 12;  /* dns header is 12 bytes */
96         if (needles >= needle_bound) return error_program;
97         if (!*needles) return nomatch;  /* we've run out of needles without finding a match */
98         /* match questions */
99         for (i = 0; i < num_questions; ++i) {
100             match_result_type m = match_single_name(needles, needle_bound, udp, udp_len, &ofs);
101             int qtype;
102             if (m < nomatch) return m;
103             if (ofs + 2 > udp_len) return error_packet;
104             qtype = (int)read_be16(udp + ofs);
105             ofs += 4; /* skip be16 qtype & qclass */
106             if (question_type == -1) continue;
107             if (m == nomatch) continue;
108             if (qtype == 0xFF /* QTYPE_ANY */ || qtype == question_type) return match;
109         }
110         /* match answers */
111         if (question_type == -1) for (i = 0; i < num_answers; ++i) {
112             match_result_type m = match_single_name(needles, needle_bound, udp, udp_len, &ofs);
113             if (m < nomatch) return m;
114             ofs += 8; /* skip be16 type, class & be32 ttl */
115             if (ofs + 2 > udp_len) return error_packet;
116             ofs += 2 + read_be16(udp + ofs);  /* skip be16 rdata length field, plus length bytes */
117             if (m == match) return match;
118         }
119         /* move needles pointer to the next needle. */
120         do {
121             u8 len = *needles++;
122             if (len == 0xFF) continue;
123             if (len > 63) return error_program;
124             needles += len;
125             if (needles >= needle_bound) return error_program;
126         } while (*needles);
127         needles++;  /* skip the NULL byte at the end of *a* DNS name */
128     }
129 }
130