1 /* $NetBSD: message_test.c,v 1.1.1.1 2026/09/17 17:45:09 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #include <inttypes.h> 17 #include <sched.h> /* IWYU pragma: keep */ 18 #include <setjmp.h> 19 #include <stdarg.h> 20 #include <stdbool.h> 21 #include <stddef.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #define UNIT_TESTING 27 #include <cmocka.h> 28 29 #include <isc/buffer.h> 30 #include <isc/region.h> 31 #include <isc/result.h> 32 #include <isc/util.h> 33 34 #include <dns/fixedname.h> 35 #include <dns/message.h> 36 #include <dns/name.h> 37 #include <dns/rdata.h> 38 #include <dns/rdataclass.h> 39 #include <dns/rdataset.h> 40 #include <dns/rdatatype.h> 41 42 #include <tests/dns.h> 43 44 #define TTL 300 45 46 /* 47 * Helpers for assembling a DNS message in wire format by hand, so that 48 * it can contain things dns_message_rendersection() would never produce, 49 * such as the same RR listed several times. 50 */ 51 52 static void 53 put_name(isc_buffer_t *wire, const char *namestr) { 54 dns_fixedname_t fname; 55 isc_region_t r; 56 57 dns_test_namefromstring(namestr, &fname); 58 dns_name_toregion(dns_fixedname_name(&fname), &r); 59 isc_buffer_putmem(wire, r.base, r.length); 60 } 61 62 static void 63 put_header(isc_buffer_t *wire, unsigned int ancount, unsigned int nscount) { 64 isc_buffer_putuint16(wire, 0x1234); /* ID */ 65 isc_buffer_putuint16(wire, DNS_MESSAGEFLAG_QR); 66 isc_buffer_putuint16(wire, 1); /* QDCOUNT */ 67 isc_buffer_putuint16(wire, ancount); 68 isc_buffer_putuint16(wire, nscount); 69 isc_buffer_putuint16(wire, 0); /* ARCOUNT */ 70 } 71 72 static void 73 put_question(isc_buffer_t *wire, const char *qname, dns_rdatatype_t qtype) { 74 put_name(wire, qname); 75 isc_buffer_putuint16(wire, qtype); 76 isc_buffer_putuint16(wire, dns_rdataclass_in); 77 } 78 79 static void 80 put_rr_with_ttl(isc_buffer_t *wire, const char *owner, dns_rdatatype_t type, 81 dns_ttl_t ttl, const char *rdatatext) { 82 unsigned char rdatabuf[512]; 83 dns_rdata_t rdata = DNS_RDATA_INIT; 84 isc_result_t result; 85 86 result = dns_test_rdatafromstring(&rdata, dns_rdataclass_in, type, 87 rdatabuf, sizeof(rdatabuf), rdatatext, 88 false); 89 assert_int_equal(result, ISC_R_SUCCESS); 90 91 put_name(wire, owner); 92 isc_buffer_putuint16(wire, type); 93 isc_buffer_putuint16(wire, dns_rdataclass_in); 94 isc_buffer_putuint32(wire, ttl); 95 isc_buffer_putuint16(wire, rdata.length); 96 isc_buffer_putmem(wire, rdata.data, rdata.length); 97 } 98 99 static void 100 put_rr(isc_buffer_t *wire, const char *owner, dns_rdatatype_t type, 101 const char *rdatatext) { 102 put_rr_with_ttl(wire, owner, type, TTL, rdatatext); 103 } 104 105 static isc_result_t 106 parse(isc_buffer_t *wire, unsigned int options, dns_message_t **msgp) { 107 dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, msgp); 108 return dns_message_parse(*msgp, wire, options); 109 } 110 111 /* 112 * Get the rdataset of the given type at 'owner' in 'section'. 113 */ 114 static dns_rdataset_t * 115 get_rdataset(dns_message_t *msg, dns_section_t section, const char *owner, 116 dns_rdatatype_t type) { 117 dns_fixedname_t fname; 118 dns_rdataset_t *rdataset = NULL; 119 isc_result_t result; 120 121 dns_test_namefromstring(owner, &fname); 122 result = dns_message_findname(msg, section, dns_fixedname_name(&fname), 123 type, 0, NULL, &rdataset); 124 assert_int_equal(result, ISC_R_SUCCESS); 125 126 return rdataset; 127 } 128 129 /* 130 * A record of a singleton type repeated with identical RDATA is kept once; 131 * repeats of other types are all retained, as before. 132 */ 133 ISC_RUN_TEST_IMPL(parse_duplicate_singleton) { 134 unsigned char wirebuf[1024]; 135 isc_buffer_t wire; 136 dns_message_t *msg = NULL; 137 isc_result_t result; 138 dns_rdataset_t *rdataset = NULL; 139 140 isc_buffer_init(&wire, wirebuf, sizeof(wirebuf)); 141 put_header(&wire, 4, 2); 142 put_question(&wire, "dup.example.", dns_rdatatype_a); 143 put_rr_with_ttl(&wire, "dup.example.", dns_rdatatype_cname, 600, 144 "target.example."); 145 put_rr_with_ttl(&wire, "dup.example.", dns_rdatatype_cname, 300, 146 "target.example."); 147 put_rr_with_ttl(&wire, "target.example.", dns_rdatatype_a, 600, 148 "10.0.0.1"); 149 put_rr_with_ttl(&wire, "target.example.", dns_rdatatype_a, 1200, 150 "10.0.0.1"); 151 put_rr_with_ttl(&wire, "example.", dns_rdatatype_soa, 0, 152 "ns.example. hostmaster.example. 1 3600 600 86400 300"); 153 put_rr_with_ttl(&wire, "example.", dns_rdatatype_soa, 1200, 154 "ns.example. hostmaster.example. 1 3600 600 86400 300"); 155 156 result = parse(&wire, 0, &msg); 157 assert_int_equal(result, ISC_R_SUCCESS); 158 159 rdataset = get_rdataset(msg, DNS_SECTION_ANSWER, "dup.example.", 160 dns_rdatatype_cname); 161 assert_non_null(rdataset); 162 assert_int_equal(dns_rdataset_count(rdataset), 1); 163 assert_int_equal(rdataset->ttl, 300); 164 165 rdataset = get_rdataset(msg, DNS_SECTION_ANSWER, "target.example.", 166 dns_rdatatype_a); 167 assert_non_null(rdataset); 168 assert_int_equal(dns_rdataset_count(rdataset), 2); 169 assert_int_equal(rdataset->ttl, 600); 170 171 rdataset = get_rdataset(msg, DNS_SECTION_AUTHORITY, "example.", 172 dns_rdatatype_soa); 173 assert_non_null(rdataset); 174 assert_int_equal(dns_rdataset_count(rdataset), 1); 175 assert_int_equal(rdataset->ttl, 0); 176 177 dns_message_detach(&msg); 178 } 179 180 /* 181 * A singleton type with two different RDATA is still a malformed message. 182 */ 183 ISC_RUN_TEST_IMPL(parse_conflicting_singleton) { 184 unsigned char wirebuf[1024]; 185 isc_buffer_t wire; 186 dns_message_t *msg = NULL; 187 isc_result_t result; 188 dns_rdataset_t *rdataset = NULL; 189 190 isc_buffer_init(&wire, wirebuf, sizeof(wirebuf)); 191 put_header(&wire, 2, 0); 192 put_question(&wire, "dup.example.", dns_rdatatype_a); 193 put_rr(&wire, "dup.example.", dns_rdatatype_cname, "target.example."); 194 put_rr(&wire, "dup.example.", dns_rdatatype_cname, "other.example."); 195 196 result = parse(&wire, 0, &msg); 197 assert_int_equal(result, DNS_R_FORMERR); 198 dns_message_detach(&msg); 199 200 /* 201 * With best-effort parsing the problem is reported but the message 202 * is still usable, and only the first CNAME survives. 203 */ 204 isc_buffer_first(&wire); 205 result = parse(&wire, DNS_MESSAGEPARSE_BESTEFFORT, &msg); 206 assert_int_equal(result, DNS_R_RECOVERABLE); 207 208 rdataset = get_rdataset(msg, DNS_SECTION_ANSWER, "dup.example.", 209 dns_rdatatype_cname); 210 assert_non_null(rdataset); 211 assert_int_equal(dns_rdataset_count(rdataset), 2); 212 213 dns_message_detach(&msg); 214 } 215 216 ISC_TEST_LIST_START 217 ISC_TEST_ENTRY(parse_duplicate_singleton) 218 ISC_TEST_ENTRY(parse_conflicting_singleton) 219 ISC_TEST_LIST_END 220 221 ISC_TEST_MAIN 222