[APFv6 2023-01] Add helper function to match names in DNS packets
Introduces a new helper function called match_name. This function
checks if any names in a given list match the content of a DNS packet.
Bug: 293811969
Test: TH
Change-Id: I55ebe5141477a6b831bd1ecc20bf3a8ec458ad96
diff --git a/v5/apf_interpreter.c b/v5/apf_interpreter.c
index 73eb794..86b3f79 100644
--- a/v5/apf_interpreter.c
+++ b/v5/apf_interpreter.c
@@ -23,6 +23,14 @@
#define TO_UPPER(a) ((a) >= 'a' && (a) <= 'z' ? ((a) - ('a' - 'A')) : (a))
#define ASSERT_POINTER_IN_BOUND(p, l, r) if ((p) < (l) || (p) >= (r)) return -1
+#define DECODE_BYTES(value, len, p, l, r) \
+ do { \
+ value = 0; \
+ ASSERT_POINTER_IN_BOUND(((p) + (len) - 1), (l), (r)); \
+ uint32_t i; \
+ for (i = 0; i < (len); i++) \
+ value = (value << 8) | *p++; \
+ } while (0)
#ifdef __cplusplus
extern "C" {
@@ -106,6 +114,100 @@
return -1;
}
+
+
+ /**
+ * Checks if a DNS packet contains any of the target names with the provided
+ * question type.
+ *
+ * @param target_names - TLV encoded names to match against.
+ * @param remain_program_len - Remaining program length.
+ * @param udp_payload - Pointer to the start of the UDP payload (DNS header).
+ * @param udp_payload_len - Length of the UDP payload.
+ * @param question_type - Question type to match against. Use -1 to match answers.
+ *
+ *
+ * @return 1 if matched, 0 if not matched, -1 if an error occurs.
+ */
+int match_name(const uint8_t* const target_names,
+ const uint32_t remain_program_len,
+ const uint8_t* const udp_payload,
+ const uint32_t udp_payload_len,
+ const int question_type) {
+ const uint8_t* src = udp_payload;
+ uint32_t value;
+ // skip tid and flags
+ DECODE_BYTES(value, 4, src, udp_payload, udp_payload + udp_payload_len);
+ uint32_t num_questions;
+ DECODE_BYTES(num_questions, 2, src, udp_payload, udp_payload + udp_payload_len);
+
+ uint32_t num_answers = 0;
+ DECODE_BYTES(value, 2, src, udp_payload, udp_payload + udp_payload_len);
+ num_answers += value;
+ DECODE_BYTES(value, 2, src, udp_payload, udp_payload + udp_payload_len);
+ num_answers += value;
+ DECODE_BYTES(value, 2, src, udp_payload, udp_payload + udp_payload_len);
+ num_answers += value;
+
+ const uint8_t* q = target_names;
+ while (*q != 0) {
+ src = udp_payload + 12;
+ ASSERT_POINTER_IN_BOUND(src, udp_payload, udp_payload + udp_payload_len);
+ uint32_t j;
+ const uint32_t checked_label_size = (uint32_t) (q - target_names);
+ // match questions
+ for (j = 0; j < num_questions; ++j) {
+ int rst = match_labels(q, remain_program_len - checked_label_size,
+ udp_payload, udp_payload_len, &src);
+ if (rst == -1) {
+ return -1;
+ }
+ int qtype;
+ // read qtype
+ DECODE_BYTES(qtype, 2, src, udp_payload, udp_payload + udp_payload_len);
+ // skip qclass
+ DECODE_BYTES(value, 2, src, udp_payload, udp_payload + udp_payload_len);
+ if (rst != 1) {
+ continue;
+ }
+ if ((question_type != -1)
+ && (qtype == 0xff /* TYPE_ANY */ || qtype == question_type)) {
+ return 1;
+ }
+ }
+ // match answers
+ for (j = 0; j < num_answers; ++j) {
+ int rst = match_labels(q, remain_program_len - checked_label_size,
+ udp_payload, udp_payload_len, &src);
+ if (rst == -1) {
+ return -1;
+ }
+ // skip type, class, ttl
+ DECODE_BYTES(value, 8, src, udp_payload, udp_payload + udp_payload_len);
+ // decode rdata length
+ uint32_t len;
+ DECODE_BYTES(len, 2, src, udp_payload, udp_payload + udp_payload_len);
+ // skip rdata
+ src += len;
+ if (rst == 1) {
+ return rst;
+ }
+ }
+ // move the pointer to the next name.
+ while (*q != 0) {
+ uint32_t len = *q++;
+ if (len < 1 || len > 63) {
+ return -1;
+ }
+ q += len;
+ ASSERT_POINTER_IN_BOUND(q, target_names, target_names + remain_program_len);
+ }
+ q++;
+ ASSERT_POINTER_IN_BOUND(q, target_names, target_names + remain_program_len);
+ }
+ return 0;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/v5/apf_interpreter_test.cc b/v5/apf_interpreter_test.cc
index 329d724..39eee52 100644
--- a/v5/apf_interpreter_test.cc
+++ b/v5/apf_interpreter_test.cc
@@ -8,6 +8,9 @@
int match_labels(const uint8_t* const target_name,
const int target_name_max_len, const uint8_t* const udp_payload,
const int udp_payload_len, const uint8_t** src);
+int match_name(const uint8_t* const target_names, const int remain_program_len,
+ const uint8_t* const udp_payload, const int udp_payload_len,
+ const int question_type);
#ifdef __cplusplus
}
@@ -97,4 +100,79 @@
EXPECT_EQ(match_labels(matched_target_name, matched_target_name_len, udp_payload, udp_payload_len, &src), -1);
}
+TEST(ApfInterpreterTest, MatchQuetions) {
+ // target names = { A.B.LOCAL }
+ const uint8_t target_names1[] = {0x01, 0x41, 0x01, 0x42, 0x05, 0x4c,
+ 0x4f, 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len1 = sizeof(target_names1);
+ const uint8_t udp_payload[] = {
+ 0x00, 0x00, 0x00, 0x00,// tid = 0x00, flags = 0x00,
+ 0x00, 0x02, // qdcount = 2
+ 0x00, 0x00, // ancount = 0
+ 0x00, 0x00, // nscount = 0
+ 0x00, 0x00, // arcount = 0
+ 0x01, 0x61, 0x01, 0x62,// qname1 = a.b.local
+ 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
+ 0x00, 0x01, 0x00, 0x01,// type=A, class=0x0001
+ 0xc0, 0x0e, // qname2 = b.local (name compression)
+ 0x00, 0x01, 0x00, 0x01}; // type=A, class=0x0001
+ const int udp_payload_len = sizeof(udp_payload);
+ EXPECT_EQ(match_name(target_names1, target_names_len1, udp_payload,
+ udp_payload_len, 0x01), 1);
+
+ // target names = { A, B.LOCAL }
+ const uint8_t target_names2[] = {0x01, 0x41, 0x00, 0x01, 0x42, 0x05, 0x4c,
+ 0x4f, 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len2 = sizeof(target_names2);
+ EXPECT_EQ(match_name(target_names2, target_names_len2, udp_payload,
+ udp_payload_len, 0x01), 1);
+
+ // target names = { C.LOCAL }
+ const uint8_t target_names3[] = {0x01, 0x43, 0x05, 0x4c, 0x4f,
+ 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len3 = sizeof(target_names2);
+ EXPECT_EQ(match_name(target_names3, target_names_len3, udp_payload,
+ udp_payload_len, 0x01), 0);
+}
+
+TEST(ApfInterpreterTest, MatchAnswers) {
+ // target names = { A.B.LOCAL }
+ const uint8_t target_names1[] = {0x01, 0x41, 0x01, 0x42, 0x05, 0x4c,
+ 0x4f, 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len1 = sizeof(target_names1);
+ const uint8_t udp_payload[] = {
+ 0x00, 0x00, 0x84, 0x00, // tid = 0x00, flags = 0x8400,
+ 0x00, 0x00, // qdcount = 0
+ 0x00, 0x02, // ancount = 2
+ 0x00, 0x00, // nscount = 0
+ 0x00, 0x00, // arcount = 0
+ 0x01, 0x61, 0x01, 0x62, // qname1 = a.b.local
+ 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
+ 0x00, 0x01, 0x80, 0x01, // type=A, class=0x8001
+ 0x00, 0x00, 0x00, 0x78, // ttl = 120
+ 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09, // rdlengh = 4, rdata=192.168.1.9
+ 0xc0, 0x0e, // qname2 = b.local (name compression)
+ 0x00, 0x01, 0x80, 0x01, // type=A, class=0x8001
+ 0x00, 0x00, 0x00, 0x78, // ttl = 120
+ 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09 // rdlengh = 4, rdata=192.168.1.9
+ };
+ const int udp_payload_len = sizeof(udp_payload);
+ EXPECT_EQ(match_name(target_names1, target_names_len1, udp_payload,
+ udp_payload_len, -1), 1);
+
+ // target names = { A, B.LOCAL }
+ const uint8_t target_names2[] = {0x01, 0x41, 0x00, 0x01, 0x42, 0x05, 0x4c,
+ 0x4f, 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len2 = sizeof(target_names2);
+ EXPECT_EQ(match_name(target_names2, target_names_len2, udp_payload,
+ udp_payload_len, -1), 1);
+
+ // target names = { C.LOCAL }
+ const uint8_t target_names3[] = {0x01, 0x43, 0x05, 0x4c, 0x4f,
+ 0x43, 0x41, 0x4c, 0x00, 0x00};
+ const int target_names_len3 = sizeof(target_names2);
+ EXPECT_EQ(match_name(target_names3, target_names_len3, udp_payload,
+ udp_payload_len, -1), 0);
+}
+
} // namespace apf