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_types.
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_type1 - question type to match against or -1 to match answers.
78 * If question_type1 is -1, we won't check question_type2.
79 * @param question_type2 - question type to match against or -1 to match answers.
80 *
81 * @return 1 if matched, 0 if not matched, -1 if error in packet, -2 if error in program.
82 */
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_type1,const int question_type2))83 FUNC(match_result_type match_names(const u8* needles,
84 const u8* const needle_bound,
85 const u8* const udp,
86 const u32 udp_len,
87 const int question_type1,
88 const int question_type2)) {
89 u32 num_questions, num_answers;
90 if (udp_len < 12) return error_packet; /* lack of dns header */
91
92 /* dns header: be16 tid, flags, num_{questions,answers,authority,additional} */
93 num_questions = read_be16(udp + 4);
94 num_answers = read_be16(udp + 6) + read_be16(udp + 8) + read_be16(udp + 10);
95
96 /* loop until we hit final needle, which is a null byte */
97 while (true) {
98 u32 i, ofs = 12; /* dns header is 12 bytes */
99 if (needles >= needle_bound) return error_program;
100 if (!*needles) return nomatch; /* we've run out of needles without finding a match */
101 /* match questions */
102 for (i = 0; i < num_questions; ++i) {
103 match_result_type m = match_single_name(needles, needle_bound, udp, udp_len, &ofs);
104 int qtype;
105 if (m < nomatch) return m;
106 if (ofs + 2 > udp_len) return error_packet;
107 qtype = (int)read_be16(udp + ofs);
108 ofs += 4; /* skip be16 qtype & qclass */
109 if (question_type1 == -1) continue;
110 if (m == nomatch) continue;
111 if (qtype == 0xFF /* QTYPE_ANY */ || qtype == question_type1 || qtype == question_type2)
112 return match;
113 }
114 /* match answers */
115 if (question_type1 == -1) for (i = 0; i < num_answers; ++i) {
116 match_result_type m = match_single_name(needles, needle_bound, udp, udp_len, &ofs);
117 if (m < nomatch) return m;
118 ofs += 8; /* skip be16 type, class & be32 ttl */
119 if (ofs + 2 > udp_len) return error_packet;
120 ofs += 2 + read_be16(udp + ofs); /* skip be16 rdata length field, plus length bytes */
121 if (m == match) return match;
122 }
123 /* move needles pointer to the next needle. */
124 do {
125 u8 len = *needles++;
126 if (len == 0xFF) continue;
127 if (len > 63) return error_program;
128 needles += len;
129 if (needles >= needle_bound) return error_program;
130 } while (*needles);
131 needles++; /* skip the NULL byte at the end of *a* DNS name */
132 }
133 }
134