1 /* $NetBSD: dnstap_test.c,v 1.3 2025/01/26 16:25:47 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 <stddef.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #define UNIT_TESTING 26 #include <cmocka.h> 27 #include <fstrm.h> 28 29 #include <protobuf-c/protobuf-c.h> 30 31 #include <isc/buffer.h> 32 #include <isc/file.h> 33 #include <isc/stdio.h> 34 #include <isc/types.h> 35 #include <isc/util.h> 36 37 #include <dns/dnstap.h> 38 #include <dns/view.h> 39 40 #include <tests/dns.h> 41 42 #define TAPFILE TESTS_DIR "/testdata/dnstap/dnstap.file" 43 #define TAPSOCK TESTS_DIR "/testdata/dnstap/dnstap.sock" 44 45 #define TAPSAVED TESTS_DIR "/testdata/dnstap/dnstap.saved" 46 #define TAPTEXT TESTS_DIR "/testdata/dnstap/dnstap.text" 47 48 static void 49 cleanup(void **state ISC_ATTR_UNUSED) { 50 (void)isc_file_remove(TAPFILE); 51 (void)isc_file_remove(TAPSOCK); 52 } 53 54 static int 55 setup(void **state) { 56 /* 57 * Make sure files are cleaned up before the test runs. 58 */ 59 cleanup(state); 60 61 /* 62 * Make sure text conversions match the time zone in which 63 * the testdata was originally generated. 64 */ 65 setenv("TZ", "PDT8", 1); 66 67 setup_loopmgr(state); 68 69 return 0; 70 } 71 72 static int 73 teardown(void **state) { 74 cleanup(state); 75 76 teardown_loopmgr(state); 77 78 return 0; 79 } 80 81 /* set up dnstap environment */ 82 ISC_LOOP_TEST_IMPL(dns_dt_create) { 83 isc_result_t result; 84 dns_dtenv_t *dtenv = NULL; 85 struct fstrm_iothr_options *fopt; 86 87 fopt = fstrm_iothr_options_init(); 88 assert_non_null(fopt); 89 fstrm_iothr_options_set_num_input_queues(fopt, 1); 90 91 result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, &fopt, NULL, 92 &dtenv); 93 assert_int_equal(result, ISC_R_SUCCESS); 94 if (dtenv != NULL) { 95 dns_dt_detach(&dtenv); 96 } 97 if (fopt != NULL) { 98 fstrm_iothr_options_destroy(&fopt); 99 } 100 101 assert_true(isc_file_exists(TAPFILE)); 102 103 fopt = fstrm_iothr_options_init(); 104 assert_non_null(fopt); 105 fstrm_iothr_options_set_num_input_queues(fopt, 1); 106 107 result = dns_dt_create(mctx, dns_dtmode_unix, TAPSOCK, &fopt, NULL, 108 &dtenv); 109 assert_int_equal(result, ISC_R_SUCCESS); 110 if (dtenv != NULL) { 111 dns_dt_detach(&dtenv); 112 } 113 if (fopt != NULL) { 114 fstrm_iothr_options_destroy(&fopt); 115 } 116 117 /* 'create' should succeed, but the file shouldn't exist yet */ 118 assert_false(isc_file_exists(TAPSOCK)); 119 120 fopt = fstrm_iothr_options_init(); 121 assert_non_null(fopt); 122 fstrm_iothr_options_set_num_input_queues(fopt, 1); 123 124 result = dns_dt_create(mctx, 33, TAPSOCK, &fopt, NULL, &dtenv); 125 assert_int_equal(result, ISC_R_FAILURE); 126 assert_null(dtenv); 127 if (dtenv != NULL) { 128 dns_dt_detach(&dtenv); 129 } 130 if (fopt != NULL) { 131 fstrm_iothr_options_destroy(&fopt); 132 } 133 134 isc_loopmgr_shutdown(loopmgr); 135 } 136 137 /* send dnstap messages */ 138 ISC_LOOP_TEST_IMPL(dns_dt_send) { 139 isc_result_t result; 140 dns_dtenv_t *dtenv = NULL; 141 dns_dthandle_t *handle = NULL; 142 uint8_t *data; 143 size_t dsize; 144 unsigned char zone[DNS_NAME_MAXWIRE]; 145 unsigned char qambuffer[4096], rambuffer[4096]; 146 unsigned char qrmbuffer[4096], rrmbuffer[4096]; 147 isc_buffer_t zb, qamsg, ramsg, qrmsg, rrmsg; 148 size_t qasize, qrsize, rasize, rrsize; 149 dns_fixedname_t zfname; 150 dns_name_t *zname; 151 dns_dtmsgtype_t dt; 152 dns_view_t *view = NULL; 153 dns_compress_t cctx; 154 isc_region_t zr; 155 isc_sockaddr_t qaddr; 156 isc_sockaddr_t raddr; 157 struct in_addr in; 158 isc_stdtime_t now = isc_stdtime_now(); 159 160 isc_time_t p, f; 161 struct fstrm_iothr_options *fopt; 162 163 result = dns_test_makeview("test", false, false, &view); 164 assert_int_equal(result, ISC_R_SUCCESS); 165 166 fopt = fstrm_iothr_options_init(); 167 assert_non_null(fopt); 168 fstrm_iothr_options_set_num_input_queues(fopt, 1); 169 170 result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, &fopt, NULL, 171 &dtenv); 172 assert_int_equal(result, ISC_R_SUCCESS); 173 174 dns_dt_attach(dtenv, &view->dtenv); 175 view->dttypes = DNS_DTTYPE_ALL; 176 177 /* 178 * Set up some test data 179 */ 180 zname = dns_fixedname_initname(&zfname); 181 isc_buffer_constinit(&zb, "example.com.", 12); 182 isc_buffer_add(&zb, 12); 183 result = dns_name_fromtext(zname, &zb, NULL, 0, NULL); 184 assert_int_equal(result, ISC_R_SUCCESS); 185 186 memset(&zr, 0, sizeof(zr)); 187 isc_buffer_init(&zb, zone, sizeof(zone)); 188 dns_compress_init(&cctx, mctx, 0); 189 dns_compress_setpermitted(&cctx, false); 190 result = dns_name_towire(zname, &cctx, &zb, NULL); 191 assert_int_equal(result, ISC_R_SUCCESS); 192 dns_compress_invalidate(&cctx); 193 isc_buffer_usedregion(&zb, &zr); 194 195 in.s_addr = inet_addr("10.53.0.1"); 196 isc_sockaddr_fromin(&qaddr, &in, 2112); 197 in.s_addr = inet_addr("10.53.0.2"); 198 isc_sockaddr_fromin(&raddr, &in, 2112); 199 200 isc_time_set(&p, now - 3600, 0); /* past */ 201 isc_time_set(&f, now + 3600, 0); /* future */ 202 203 result = dns_test_getdata(TESTS_DIR "/testdata/dnstap/query.auth", 204 qambuffer, sizeof(qambuffer), &qasize); 205 assert_int_equal(result, ISC_R_SUCCESS); 206 isc_buffer_init(&qamsg, qambuffer, qasize); 207 isc_buffer_add(&qamsg, qasize); 208 209 result = dns_test_getdata(TESTS_DIR "/testdata/dnstap/response.auth", 210 rambuffer, sizeof(rambuffer), &rasize); 211 assert_int_equal(result, ISC_R_SUCCESS); 212 isc_buffer_init(&ramsg, rambuffer, rasize); 213 isc_buffer_add(&ramsg, rasize); 214 215 result = dns_test_getdata(TESTS_DIR "/testdata/dnstap/query.recursive", 216 qrmbuffer, sizeof(qrmbuffer), &qrsize); 217 assert_int_equal(result, ISC_R_SUCCESS); 218 isc_buffer_init(&qrmsg, qrmbuffer, qrsize); 219 isc_buffer_add(&qrmsg, qrsize); 220 221 result = dns_test_getdata(TESTS_DIR 222 "/testdata/dnstap/response.recursive", 223 rrmbuffer, sizeof(rrmbuffer), &rrsize); 224 assert_int_equal(result, ISC_R_SUCCESS); 225 isc_buffer_init(&rrmsg, rrmbuffer, rrsize); 226 isc_buffer_add(&rrmsg, rrsize); 227 228 for (dt = DNS_DTTYPE_SQ; dt <= DNS_DTTYPE_TR; dt <<= 1) { 229 isc_buffer_t *m; 230 isc_sockaddr_t *q = &qaddr, *r = &raddr; 231 232 switch (dt) { 233 case DNS_DTTYPE_AQ: 234 m = &qamsg; 235 break; 236 case DNS_DTTYPE_AR: 237 m = &ramsg; 238 break; 239 default: 240 m = &qrmsg; 241 if ((dt & DNS_DTTYPE_RESPONSE) != 0) { 242 m = &ramsg; 243 } 244 break; 245 } 246 247 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, &p, &f, m); 248 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, NULL, &f, 249 m); 250 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, &p, NULL, 251 m); 252 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, NULL, NULL, 253 m); 254 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, &p, &f, m); 255 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, NULL, &f, 256 m); 257 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, &p, NULL, 258 m); 259 dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, NULL, NULL, 260 m); 261 } 262 263 dns_dt_detach(&view->dtenv); 264 dns_dt_detach(&dtenv); 265 dns_view_detach(&view); 266 267 result = dns_dt_open(TAPFILE, dns_dtmode_file, mctx, &handle); 268 assert_int_equal(result, ISC_R_SUCCESS); 269 270 while (dns_dt_getframe(handle, &data, &dsize) == ISC_R_SUCCESS) { 271 dns_dtdata_t *dtdata = NULL; 272 isc_region_t r; 273 static dns_dtmsgtype_t expected = DNS_DTTYPE_SQ; 274 static int n = 0; 275 276 r.base = data; 277 r.length = dsize; 278 279 result = dns_dt_parse(mctx, &r, &dtdata); 280 assert_int_equal(result, ISC_R_SUCCESS); 281 if (result != ISC_R_SUCCESS) { 282 n++; 283 continue; 284 } 285 286 assert_int_equal(dtdata->type, expected); 287 if (++n % 8 == 0) { 288 expected <<= 1; 289 } 290 291 dns_dtdata_free(&dtdata); 292 } 293 294 if (fopt != NULL) { 295 fstrm_iothr_options_destroy(&fopt); 296 } 297 if (handle != NULL) { 298 dns_dt_close(&handle); 299 } 300 301 isc_loopmgr_shutdown(loopmgr); 302 } 303 304 /* dnstap message to text */ 305 ISC_LOOP_TEST_IMPL(dns_dt_totext) { 306 isc_result_t result; 307 dns_dthandle_t *handle = NULL; 308 uint8_t *data; 309 size_t dsize; 310 FILE *fp = NULL; 311 312 result = dns_dt_open(TAPSAVED, dns_dtmode_file, mctx, &handle); 313 assert_int_equal(result, ISC_R_SUCCESS); 314 315 result = isc_stdio_open(TAPTEXT, "r", &fp); 316 assert_int_equal(result, ISC_R_SUCCESS); 317 318 while (dns_dt_getframe(handle, &data, &dsize) == ISC_R_SUCCESS) { 319 dns_dtdata_t *dtdata = NULL; 320 isc_buffer_t *b = NULL; 321 isc_region_t r; 322 char s[BUFSIZ], *p; 323 324 r.base = data; 325 r.length = dsize; 326 327 /* read the corresponding line of text */ 328 p = fgets(s, sizeof(s), fp); 329 assert_ptr_equal(p, s); 330 if (p == NULL) { 331 break; 332 } 333 334 p = strchr(p, '\n'); 335 if (p != NULL) { 336 *p = '\0'; 337 } 338 339 /* parse dnstap frame */ 340 result = dns_dt_parse(mctx, &r, &dtdata); 341 assert_int_equal(result, ISC_R_SUCCESS); 342 if (result != ISC_R_SUCCESS) { 343 continue; 344 } 345 346 isc_buffer_allocate(mctx, &b, 2048); 347 assert_non_null(b); 348 if (b == NULL) { 349 break; 350 } 351 352 /* convert to text and compare */ 353 result = dns_dt_datatotext(dtdata, &b); 354 assert_int_equal(result, ISC_R_SUCCESS); 355 356 assert_string_equal((char *)isc_buffer_base(b), s); 357 358 dns_dtdata_free(&dtdata); 359 isc_buffer_free(&b); 360 } 361 362 if (handle != NULL) { 363 dns_dt_close(&handle); 364 } 365 isc_loopmgr_shutdown(loopmgr); 366 } 367 368 ISC_TEST_LIST_START 369 370 ISC_TEST_ENTRY_CUSTOM(dns_dt_create, setup, teardown) 371 ISC_TEST_ENTRY_CUSTOM(dns_dt_send, setup, teardown) 372 ISC_TEST_ENTRY_CUSTOM(dns_dt_totext, setup, teardown) 373 374 ISC_TEST_LIST_END 375 376 ISC_TEST_MAIN 377