]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: tests for dns_packet_extract(); parsing OPT records
authorJames Coglan <james@neighbourhood.ie>
Tue, 11 Jun 2024 10:57:36 +0000 (11:57 +0100)
committerJames Coglan <james@neighbourhood.ie>
Mon, 22 Jul 2024 09:11:51 +0000 (10:11 +0100)
These tests identify a couple of problems with OPT pseudo-RR parsing.

First, any TTL value with the high bit set is replaced with zero before
checking the record type. For most types this is correct, since TTLs
have the range of signed int32. But for OPT records where the TTL is
repurposed to hold the extended RCODE, EDNS version and flags, it means
that the high bit cannot be used in extended RCODEs. Any RCODE with the
high bit set will be read as zero.

Second, the DNS_PACKET_RCODE() function bit-shifts the extended RCODE by
24 places instead of 20, so that it ends up forming the lower 8 bits of
a 12-bit RCODE, instead of the upper 8 bits as intended.

We intend to fix these issues in other pull requests.

src/resolve/test-dns-packet-extract.c

index e9cec06469ee506cc261a67d5851541948ab3ae5..65cb08f2bc756d7f36f4474081dd74045f37172f 100644 (file)
@@ -2207,6 +2207,409 @@ TEST(packet_reply_naptr_compressed_replace) {
         ASSERT_EQ(dns_answer_size(packet->answer), 0u);
 }
 
+/* ================================================================
+ * reply: OPT
+ * ================================================================ */
+
+TEST(packet_reply_opt_no_do_empty) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        DnsResourceRecord *rr = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_NXDOMAIN,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x9a,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+
+        rr = dns_resource_record_new_full(513, DNS_TYPE_OPT, "");
+        ASSERT_NOT_NULL(rr);
+        rr->ttl = 2046820352;
+
+        ASSERT_TRUE(dns_resource_record_equal(packet->opt, rr));
+        dns_resource_record_unref(rr);
+
+        ASSERT_EQ(DNS_PACKET_PAYLOAD_SIZE_MAX(packet), 513u);
+        ASSERT_EQ(DNS_PACKET_RCODE(packet), 2467u);
+        ASSERT_EQ(DNS_PACKET_DO(packet), 0u);
+}
+
+TEST(packet_reply_opt_multiple) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x02,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x7a,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x7a,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
+TEST(packet_reply_opt_not_root) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x03, 'c', 'o', 'm',
+                        0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
+TEST(packet_reply_opt_wrong_section) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x01,     0x00, 0x00,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
+TEST(packet_query_opt_version_ok) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        DnsResourceRecord *rr = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     0x00, 0x00,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x01,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+
+        rr = dns_resource_record_new_full(513, DNS_TYPE_OPT, "");
+        ASSERT_NOT_NULL(rr);
+        rr->ttl = 65536;
+
+        ASSERT_TRUE(dns_resource_record_equal(packet->opt, rr));
+        dns_resource_record_unref(rr);
+
+        ASSERT_EQ(DNS_PACKET_PAYLOAD_SIZE_MAX(packet), 513u);
+        ASSERT_EQ(DNS_PACKET_DO(packet), 0u);
+}
+
+TEST(packet_reply_opt_version_bad) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x01,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_ERROR(dns_packet_extract(packet), EBADMSG);
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
+TEST(packet_reply_opt_with_do) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        DnsResourceRecord *rr = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x10, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x80, 0x00,
+        /* rdata */     0x00, 0x00
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+
+        rr = dns_resource_record_new_full(4097, DNS_TYPE_OPT, "");
+        ASSERT_NOT_NULL(rr);
+        rr->ttl = 32768;
+
+        ASSERT_TRUE(dns_resource_record_equal(packet->opt, rr));
+        dns_resource_record_unref(rr);
+
+        ASSERT_EQ(DNS_PACKET_PAYLOAD_SIZE_MAX(packet), 4097u);
+        ASSERT_EQ(DNS_PACKET_DO(packet), 1u);
+}
+
+TEST(packet_reply_opt_with_data) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        DnsResourceRecord *rr = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x12,
+                        0x00, 0x01,
+                        0x00, 0x05,
+                        'h', 'e', 'l', 'l', 'o',
+                        0x00, 0x02,
+                        0x00, 0x05,
+                        'w', 'o', 'r', 'l', 'd'
+        };
+
+        const uint8_t opt_data[] = {
+                        0x00, 0x01,
+                        0x00, 0x05,
+                        'h', 'e', 'l', 'l', 'o',
+                        0x00, 0x02,
+                        0x00, 0x05,
+                        'w', 'o', 'r', 'l', 'd'
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+
+        rr = dns_resource_record_new_full(513, DNS_TYPE_OPT, "");
+        ASSERT_NOT_NULL(rr);
+        rr->ttl = 0;
+        rr->opt.data_size = sizeof(opt_data);
+        rr->opt.data = calloc(rr->opt.data_size, sizeof(uint8_t));
+        ASSERT_NOT_NULL(rr->opt.data);
+        memcpy(rr->opt.data, opt_data, sizeof(opt_data));
+
+        ASSERT_TRUE(dns_resource_record_equal(packet->opt, rr));
+        dns_resource_record_unref(rr);
+}
+
+TEST(packet_reply_opt_bad_data_size) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x13,
+                        0x00, 0x01,
+                        0x00, 0x05,
+                        'h', 'e', 'l', 'l', 'o',
+                        0x00, 0x02,
+                        0x00, 0x05,
+                        'w', 'o', 'r', 'l', 'd'
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_ERROR(dns_packet_extract(packet), EBADMSG);
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
+TEST(packet_query_opt_with_rfc6975_data) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+        DnsResourceRecord *rr = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     0x00, 0x00,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x05,
+                        0x00, 0x05,     /* RFC 6975 defines codes 5, 6, 7 */
+                        0x00, 0x01,
+                        0xff
+        };
+
+        const uint8_t opt_data[] = { 0x00, 0x05, 0x00, 0x01, 0xff };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+
+        rr = dns_resource_record_new_full(513, DNS_TYPE_OPT, "");
+        ASSERT_NOT_NULL(rr);
+        rr->ttl = 0;
+        rr->opt.data_size = sizeof(opt_data);
+        rr->opt.data = calloc(rr->opt.data_size, sizeof(uint8_t));
+        ASSERT_NOT_NULL(rr->opt.data);
+        memcpy(rr->opt.data, opt_data, sizeof(opt_data));
+
+        ASSERT_TRUE(dns_resource_record_equal(packet->opt, rr));
+        dns_resource_record_unref(rr);
+}
+
+TEST(packet_reply_opt_with_rfc6975_data) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+        ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+        ASSERT_NOT_NULL(packet);
+        dns_packet_truncate(packet, 0);
+
+        const uint8_t data[] = {
+                        0x00, 0x42,     BIT_QR | BIT_AA, DNS_RCODE_SUCCESS,
+                        0x00, 0x00,     0x00, 0x00,     0x00, 0x00,     0x00, 0x01,
+
+        /* name */      0x00,
+        /* OPT */       0x00, 0x29,
+        /* udp max */   0x02, 0x01,
+        /* rcode */     0x00,
+        /* version */   0x00,
+        /* flags */     0x00, 0x00,
+        /* rdata */     0x00, 0x05,
+                        0x00, 0x05,     /* RFC 6975 defines codes 5, 6, 7 */
+                        0x00, 0x01,
+                        0xff
+        };
+
+        ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+
+        ASSERT_OK(dns_packet_extract(packet));
+        ASSERT_EQ(dns_question_size(packet->question), 0u);
+        ASSERT_EQ(dns_answer_size(packet->answer), 0u);
+        ASSERT_NULL(packet->opt);
+}
+
 /* ================================================================
  * reply: RRSIG
  * ================================================================ */