The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dns_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for DNS label encoding / decoding
18 *
19 * @file src/lib/util/test/dns_tests.c
20 *
21 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
22 */
23
24#include "acutest.h"
25#include "acutest_helpers.h"
26#include <freeradius-devel/util/value.h>
27#include <freeradius-devel/util/dns.h>
28
29#define DNS_HDR_LEN 12
30
31/*
32 * Helper: set up an fr_dns_labels_t with tracking blocks.
33 */
35static uint8_t test_marker[65536];
36
37static void labels_init(fr_dns_labels_t *lb, uint8_t const *packet, size_t packet_len, bool use_mark)
38{
39 lb->max = 256;
40 lb->blocks = test_blocks;
41 lb->start = packet;
42 lb->end = packet + packet_len;
43 lb->num = 1;
44 lb->blocks[0].start = DNS_HDR_LEN;
45 lb->blocks[0].end = DNS_HDR_LEN;
46
47 if (use_mark) {
48 fr_assert(packet_len <= sizeof(test_marker));
49 memset(test_marker, 0, packet_len);
50 lb->mark = test_marker;
51 } else {
52 lb->mark = NULL;
53 }
54}
55
56/*
57 * Helper: encode a string into DNS label format in a buffer.
58 * Returns total bytes written at 'where', or < 0 on error.
59 */
60static ssize_t encode_label(uint8_t *buf, size_t buf_len, uint8_t *where,
61 char const *str, bool compression, fr_dns_labels_t *lb)
62{
63 return fr_dns_label_from_value_box(NULL, buf, buf_len, where, compression, fr_box_strvalue(str), lb);
64}
65
66/*
67 * Encoding tests
68 *
69 */
70
71static void test_encode_empty_string(void)
72{
73 ssize_t slen;
74 uint8_t buf[256] = {};
75
76 /* empty string => single 0x00 byte */
77 slen = encode_label(buf, sizeof(buf), buf, "", false, NULL);
78 TEST_CHECK(slen == 1);
79 TEST_CHECK(buf[0] == 0x00);
80}
81
82static void test_encode_root_dot(void)
83{
84 ssize_t slen;
85 uint8_t buf[256] = {};
86
87 /* "." => single 0x00 byte */
88 slen = encode_label(buf, sizeof(buf), buf, ".", false, NULL);
89 TEST_CHECK(slen == 1);
90 TEST_CHECK(buf[0] == 0x00);
91}
92
93static void test_encode_simple_label(void)
94{
95 ssize_t slen;
96 uint8_t buf[256] = {};
97
98 /* "com" => 03 63 6f 6d 00 */
99 slen = encode_label(buf, sizeof(buf), buf, "com", false, NULL);
100 TEST_CHECK(slen == 5);
101 TEST_MSG("Expected 5, got %zd", slen);
102 TEST_CHECK(buf[0] == 3);
103 TEST_CHECK(memcmp(buf + 1, "com", 3) == 0);
104 TEST_CHECK(buf[4] == 0x00);
105}
106
107static void test_encode_multi_label(void)
108{
109 ssize_t slen;
110 uint8_t buf[256] = {};
111
112 /* "www.example.com" => 03 www 07 example 03 com 00 */
113 slen = encode_label(buf, sizeof(buf), buf, "www.example.com", false, NULL);
114 TEST_CHECK(slen == 17);
115 TEST_MSG("Expected 17, got %zd", slen);
116 TEST_CHECK(buf[0] == 3);
117 TEST_CHECK(memcmp(buf + 1, "www", 3) == 0);
118 TEST_CHECK(buf[4] == 7);
119 TEST_CHECK(memcmp(buf + 5, "example", 7) == 0);
120 TEST_CHECK(buf[12] == 3);
121 TEST_CHECK(memcmp(buf + 13, "com", 3) == 0);
122 TEST_CHECK(buf[16] == 0x00);
123}
124
126{
127 ssize_t slen;
128 uint8_t buf[256] = {};
129
130 /* "example.com." trailing dot should be stripped */
131 slen = encode_label(buf, sizeof(buf), buf, "example.com.", false, NULL);
132 TEST_CHECK(slen == 13);
133 TEST_MSG("Expected 13, got %zd", slen);
134 /* same as "example.com" */
135 TEST_CHECK(buf[0] == 7);
136 TEST_CHECK(buf[8] == 3);
137 TEST_CHECK(buf[12] == 0x00);
138}
139
141{
142 ssize_t slen;
143 uint8_t buf[256] = {};
144
145 /* "_srv.example.com" - underscore allowed at label start */
146 slen = encode_label(buf, sizeof(buf), buf, "_srv.example.com", false, NULL);
147 TEST_CHECK(slen > 0);
148 TEST_MSG("Expected >0, got %zd", slen);
149 TEST_CHECK(buf[0] == 4); /* _srv is 4 chars */
150 TEST_CHECK(buf[1] == '_');
151}
152
153/*
154 * Encoding error paths
155 */
156static void test_encode_null_inputs(void)
157{
158 ssize_t slen;
159 uint8_t buf[256] = {};
160
161 /* NULL buf */
162 slen = fr_dns_label_from_value_box(NULL, NULL, 100, (uint8_t[100]){0}, false, fr_box_strvalue("test"), NULL);
163 TEST_CHECK(slen < 0);
164
165 /* zero buf_len */
166 slen = fr_dns_label_from_value_box(NULL, buf, 0, buf, false, fr_box_strvalue("test"), NULL);
167 TEST_CHECK(slen < 0);
168
169 /* NULL value */
170 slen = fr_dns_label_from_value_box(NULL, buf, sizeof(buf), buf, false, NULL, NULL);
171 TEST_CHECK(slen < 0);
172}
173
175{
176 ssize_t slen;
178 uint8_t buf[256] = {};
179
181 slen = fr_dns_label_from_value_box(NULL, buf, sizeof(buf), buf, false, &vb, NULL);
182 TEST_CHECK(slen < 0);
183}
184
186{
187 ssize_t slen;
188 uint8_t buf[256] = {};
189
190 /* space is invalid */
191 slen = encode_label(buf, sizeof(buf), buf, "hello world", false, NULL);
192 TEST_CHECK(slen < 0);
193
194 /* @ is invalid */
195 slen = encode_label(buf, sizeof(buf), buf, "user@host", false, NULL);
196 TEST_CHECK(slen < 0);
197
198 /* tab is invalid */
199 slen = encode_label(buf, sizeof(buf), buf, "a\tb", false, NULL);
200 TEST_CHECK(slen < 0);
201}
202
203static void test_encode_double_dot(void)
204{
205 ssize_t slen;
206 uint8_t buf[256] = {};
207
208 slen = encode_label(buf, sizeof(buf), buf, "www..example.com", false, NULL);
209 TEST_CHECK(slen < 0);
210}
211
212static void test_encode_leading_dot(void)
213{
214 ssize_t slen;
215 uint8_t buf[256] = {};
216
217 slen = encode_label(buf, sizeof(buf), buf, ".example.com", false, NULL);
218 TEST_CHECK(slen < 0);
219}
220
222{
223 ssize_t slen;
224 char label[65];
225 uint8_t buf[256] = {};
226
227 /* 64-char label exceeds the 63-byte limit */
228 memset(label, 'a', 64);
229 label[64] = '\0';
230 slen = encode_label(buf, sizeof(buf), buf, label, false, NULL);
231 TEST_CHECK(slen < 0);
232}
233
235{
236 ssize_t slen;
237 size_t need = 0;
238 uint8_t buf[5] = {}; /* too small for "example.com" */
239
240 slen = fr_dns_label_from_value_box(&need, buf, sizeof(buf), buf, false,
241 fr_box_strvalue("example.com"), NULL);
242 TEST_CHECK(slen == 0); /* returns 0 when buffer too small */
243 TEST_CHECK(need == 13);
244 TEST_MSG("need=%zu, buf_len=%zu", need, sizeof(buf));
245}
246
248{
249 ssize_t slen;
250 uint8_t outside[16];
251 uint8_t buf[256] = {};
252
253 /* 'where' pointer is outside the buffer */
254 slen = fr_dns_label_from_value_box(NULL, buf, sizeof(buf), outside, false, fr_box_strvalue("test"), NULL);
255 TEST_CHECK(slen < 0);
256}
257
258/*
259 * Decode / uncompressed length tests
260 */
261
263{
264 uint8_t const *next;
265 ssize_t slen;
266 uint8_t pkt[DNS_HDR_LEN + 5];
267
268 memset(pkt, 0, DNS_HDR_LEN);
269 pkt[12] = 3;
270 pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm';
271 pkt[16] = 0x00;
272
273 next = pkt + DNS_HDR_LEN;
274 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 5, &next, NULL);
275 TEST_CHECK(slen == 3);
276 TEST_MSG("Expected 3, got %zd", slen);
277 TEST_CHECK(next == pkt + 17);
278}
279
280static void test_decode_multi_label(void)
281{
282 ssize_t slen;
283 uint8_t const *next;
284 uint8_t pkt[DNS_HDR_LEN + 17]; /* "www.example.com" */
285
286 memset(pkt, 0, DNS_HDR_LEN);
287 pkt[12] = 3; memcpy(pkt + 13, "www", 3);
288 pkt[16] = 7; memcpy(pkt + 17, "example", 7);
289 pkt[24] = 3; memcpy(pkt + 25, "com", 3);
290 pkt[28] = 0x00;
291
292 next = pkt + DNS_HDR_LEN;
293 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 17, &next, NULL);
294 TEST_CHECK(slen == 15);
295 TEST_MSG("Expected 15, got %zd", slen);
296 TEST_CHECK(next == pkt + 29);
297}
298
299static void test_decode_root_label(void)
300{
301 ssize_t slen;
302 uint8_t const *next;
303 uint8_t pkt[DNS_HDR_LEN + 1];
304
305 memset(pkt, 0, DNS_HDR_LEN);
306 pkt[12] = 0x00;
307
308 next = pkt + DNS_HDR_LEN;
309 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 1, &next, NULL);
310 TEST_CHECK(slen == 1);
311 TEST_MSG("Expected 1, got %zd", slen);
312}
313
315{
316 ssize_t slen;
317 uint8_t const *next;
319 uint8_t pkt[DNS_HDR_LEN + 11];
320
321 memset(pkt, 0, DNS_HDR_LEN);
322 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
323 pkt[17] = 3; pkt[18] = 'f'; pkt[19] = 'o'; pkt[20] = 'o';
324 pkt[21] = 0xc0; pkt[22] = 0x0c; /* pointer to offset 12 */
325
326 labels_init(&lb, pkt, sizeof(pkt), true);
327 test_marker[12] = 1;
328
329 next = pkt + 17;
330 slen = fr_dns_label_uncompressed_length(pkt, pkt + 17, 6, &next, &lb);
331 TEST_CHECK(slen == 7);
332 TEST_MSG("Expected 7, got %zd", slen);
333 TEST_CHECK(next == pkt + 23);
334}
335
337{
338 ssize_t slen;
339 uint8_t const *next;
340 uint8_t pkt[DNS_HDR_LEN + 8];
341
342 memset(pkt, 0, DNS_HDR_LEN);
343 pkt[12] = 0xc0; pkt[13] = 17; /* forward pointer */
344 pkt[14] = 3; pkt[15] = 'c'; pkt[16] = 'o'; pkt[17] = 'm'; pkt[18] = 0x00;
345
346 next = pkt + DNS_HDR_LEN;
347 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 8, &next, NULL);
348 TEST_CHECK(slen <= 0);
349 TEST_MSG("Forward pointer should be rejected, got %zd", slen);
350}
351
353{
354 ssize_t slen;
355 uint8_t const *next;
356 uint8_t pkt[DNS_HDR_LEN + 2];
357
358 memset(pkt, 0, DNS_HDR_LEN);
359 pkt[12] = 0xc0; pkt[13] = 12;
360
361 next = pkt + DNS_HDR_LEN;
362 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 2, &next, NULL);
363 TEST_CHECK(slen <= 0);
364 TEST_MSG("Self-pointer should be rejected, got %zd", slen);
365}
366
368{
369 ssize_t slen;
370 uint8_t const *next;
371 uint8_t pkt[DNS_HDR_LEN + 9];
372
373 memset(pkt, 0, DNS_HDR_LEN);
374 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
375 pkt[17] = 0xc0; pkt[18] = 0x0c;
376 pkt[19] = 0xc0; pkt[20] = 0x11; /* pointer to offset 17 (which is a pointer) */
377
378 next = pkt + 19;
379 slen = fr_dns_label_uncompressed_length(pkt, pkt + 19, 2, &next, NULL);
380 TEST_CHECK(slen <= 0);
381 TEST_MSG("Pointer-to-pointer should be rejected, got %zd", slen);
382}
383
385{
386 ssize_t slen;
387 uint8_t const *next;
388 uint8_t pkt[DNS_HDR_LEN + 2];
389
390 memset(pkt, 0, DNS_HDR_LEN);
391 pkt[12] = 0x80; pkt[13] = 0x00;
392
393 next = pkt + DNS_HDR_LEN;
394 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 2, &next, NULL);
395 TEST_CHECK(slen <= 0);
396 TEST_MSG("0x80 high bits should be rejected, got %zd", slen);
397}
398
400{
401 ssize_t slen;
402 uint8_t const *next;
403 uint8_t pkt[DNS_HDR_LEN + 4];
404
405 memset(pkt, 0, DNS_HDR_LEN);
406 pkt[12] = 10; /* 10 bytes, but only 3 available */
407 pkt[13] = 'a'; pkt[14] = 'b'; pkt[15] = 'c';
408
409 next = pkt + DNS_HDR_LEN;
410 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 4, &next, NULL);
411 TEST_CHECK(slen <= 0);
412 TEST_MSG("Overflow should be rejected, got %zd", slen);
413}
414
415static void test_decode_null_inputs(void)
416{
417 ssize_t slen;
418 uint8_t const *next = NULL;
419
420 slen = fr_dns_label_uncompressed_length(NULL, (uint8_t const *)"x", 1, &next, NULL);
421 TEST_CHECK(slen == 0);
422
423 slen = fr_dns_label_uncompressed_length((uint8_t const *)"x", NULL, 1, &next, NULL);
424 TEST_CHECK(slen == 0);
425
426 {
427 uint8_t pkt[1] = {0};
428 slen = fr_dns_label_uncompressed_length(pkt, pkt, 0, &next, NULL);
429 TEST_CHECK(slen == 0);
430 }
431
432 {
433 uint8_t pkt[13];
434 memset(pkt, 0, sizeof(pkt));
435 slen = fr_dns_label_uncompressed_length(pkt, pkt + 12, 1, NULL, NULL);
436 TEST_CHECK(slen == 0);
437 }
438}
439
441{
442 ssize_t slen;
443 uint8_t const *next;
444 uint8_t pkt[DNS_HDR_LEN + 4];
445
446 memset(pkt, 0, DNS_HDR_LEN);
447 pkt[12] = 3; pkt[13] = 'a'; pkt[14] = ' '; pkt[15] = 'b';
448
449 next = pkt + DNS_HDR_LEN;
450 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, 4, &next, NULL);
451 TEST_CHECK(slen <= 0);
452 TEST_MSG("Invalid char in label should be rejected, got %zd", slen);
453}
454
456{
457 uint8_t const *next;
458 ssize_t slen;
459 int i;
460 uint8_t pkt[DNS_HDR_LEN + 5*(1+52) + 1];
461 uint8_t *p = pkt + DNS_HDR_LEN;
462
463 memset(pkt, 0, DNS_HDR_LEN);
464
465 for (i = 0; i < 5; i++) {
466 *p++ = 52;
467 memset(p, 'a', 52);
468 p += 52;
469 }
470 *p++ = 0x00;
471
472 next = pkt + DNS_HDR_LEN;
473 slen = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN, (size_t)(p - pkt - DNS_HDR_LEN), &next, NULL);
474 TEST_CHECK(slen <= 0);
475 TEST_MSG("Total length > 255 should be rejected, got %zd", slen);
476}
477
478/*
479 * network_verify tests
480 */
481
482static void test_verify_simple(void)
483{
484 ssize_t slen;
485 uint8_t pkt[DNS_HDR_LEN + 5];
486
487 memset(pkt, 0, DNS_HDR_LEN);
488 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
489
490 slen = fr_dns_labels_network_verify(pkt, pkt + DNS_HDR_LEN, 5, pkt + DNS_HDR_LEN, NULL);
491 TEST_CHECK(slen > 0);
492 TEST_MSG("Expected >0, got %zd", slen);
493}
494
495static void test_verify_empty(void)
496{
497 ssize_t slen;
498 uint8_t pkt[DNS_HDR_LEN + 1];
499
500 memset(pkt, 0, DNS_HDR_LEN);
501 pkt[12] = 0x00;
502
503 slen = fr_dns_labels_network_verify(pkt, pkt + DNS_HDR_LEN, 1, pkt + DNS_HDR_LEN, NULL);
504 TEST_CHECK(slen > 0);
505}
506
507/* ----------------------------------------------------------------
508 * Round-trip tests: encode then decode
509 * ---------------------------------------------------------------- */
510
511static void test_roundtrip_simple(void)
512{
513 ssize_t enc_len, dec_len;
514 TALLOC_CTX *ctx = talloc_init("test");
515 fr_value_box_t vb_out;
516 uint8_t buf[256] = {};
517
518 enc_len = encode_label(buf, sizeof(buf), buf, "example.com", false, NULL);
519 TEST_CHECK(enc_len > 0);
520 TEST_MSG("encode returned %zd", enc_len);
521 if (enc_len <= 0) return;
522
523 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, buf, enc_len, buf, false, NULL);
524 TEST_CHECK(dec_len > 0);
525 TEST_MSG("decode returned %zd", dec_len);
526
527 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
528 TEST_CHECK(strcmp(vb_out.vb_strvalue, "example.com") == 0);
529 TEST_MSG("Expected 'example.com', got '%s'", vb_out.vb_strvalue);
530
531 fr_value_box_clear(&vb_out);
532 talloc_free(ctx);
533}
534
536{
537 ssize_t enc_len, dec_len;
538 TALLOC_CTX *ctx = talloc_init("test");
539 fr_value_box_t vb_out;
540 uint8_t buf[256] = {};
541
542 enc_len = encode_label(buf, sizeof(buf), buf, "example.com.", false, NULL);
543 TEST_CHECK(enc_len > 0);
544 if (enc_len <= 0) return;
545
546 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, buf, enc_len, buf, false, NULL);
547 TEST_CHECK(dec_len > 0);
548 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
549 TEST_CHECK(strcmp(vb_out.vb_strvalue, "example.com") == 0);
550 TEST_MSG("Expected 'example.com', got '%s'", vb_out.vb_strvalue);
551
552 fr_value_box_clear(&vb_out);
553 talloc_free(ctx);
554}
555
556static void test_roundtrip_root(void)
557{
558 ssize_t enc_len, dec_len;
559 TALLOC_CTX *ctx = talloc_init("test");
560 fr_value_box_t vb_out;
561 uint8_t buf[256] = {};
562
563 enc_len = encode_label(buf, sizeof(buf), buf, ".", false, NULL);
564 TEST_CHECK(enc_len == 1);
565 if (enc_len <= 0) return;
566
567 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, buf, enc_len, buf, false, NULL);
568 TEST_CHECK(dec_len > 0);
569 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
570 TEST_CHECK(strcmp(vb_out.vb_strvalue, ".") == 0);
571 TEST_MSG("Expected '.', got '%s'", vb_out.vb_strvalue);
572
573 fr_value_box_clear(&vb_out);
574 talloc_free(ctx);
575}
576
578{
579 ssize_t enc_len, dec_len;
580 TALLOC_CTX *ctx = talloc_init("test");
581 fr_value_box_t vb_out;
582 uint8_t buf[256] = {};
583
584 enc_len = encode_label(buf, sizeof(buf), buf, "_tcp.example.com", false, NULL);
585 TEST_CHECK(enc_len > 0);
586 if (enc_len <= 0) return;
587
588 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, buf, enc_len, buf, false, NULL);
589 TEST_CHECK(dec_len > 0);
590 TEST_CHECK(strcmp(vb_out.vb_strvalue, "_tcp.example.com") == 0);
591 TEST_MSG("Expected '_tcp.example.com', got '%s'", vb_out.vb_strvalue);
592
593 fr_value_box_clear(&vb_out);
594 talloc_free(ctx);
595}
596
598{
599 ssize_t enc_len, dec_len;
600 TALLOC_CTX *ctx = talloc_init("test");
601 fr_value_box_t vb_out;
602 uint8_t buf[256] = {};
603
604 enc_len = encode_label(buf, sizeof(buf), buf, "WWW.Example.COM", false, NULL);
605 TEST_CHECK(enc_len > 0);
606 if (enc_len <= 0) return;
607
608 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, buf, enc_len, buf, false, NULL);
609 TEST_CHECK(dec_len > 0);
610 TEST_CHECK(strcmp(vb_out.vb_strvalue, "WWW.Example.COM") == 0);
611 TEST_MSG("Expected 'WWW.Example.COM', got '%s'", vb_out.vb_strvalue);
612
613 fr_value_box_clear(&vb_out);
614 talloc_free(ctx);
615}
616
617/*
618 * Compression tests (encoding with compression)
619 */
620
621static void test_compress_two_names(void)
622{
623 ssize_t slen1, slen2;
625 uint8_t pkt[256] = {};
626
627 labels_init(&lb, pkt, sizeof(pkt), false);
628
629 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, "example.com", true, &lb);
630 TEST_CHECK(slen1 > 0);
631 TEST_MSG("First encode: %zd", slen1);
632
633 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, "foo.example.com", true, &lb);
634 TEST_CHECK(slen2 > 0);
635 TEST_MSG("Second encode: %zd", slen2);
636
637 /*
638 * Without compression: "foo.example.com" = 17 bytes
639 * With compression: "foo" (4 bytes) + pointer (2 bytes) = 6 bytes
640 */
641 TEST_CHECK(slen2 < 17);
642 TEST_MSG("Compressed size %zd should be < 17", slen2);
643}
644
645/* ----------------------------------------------------------------
646 * Decode: fr_dns_label_to_value_box error paths
647 * ---------------------------------------------------------------- */
648
650{
652 ssize_t slen;
653 uint8_t pkt[1] = {};
654
655 slen = fr_dns_label_to_value_box(NULL, &vb, pkt, 0, pkt, false, NULL);
656 TEST_CHECK(slen < 0);
657}
658
660{
662 ssize_t slen;
663 uint8_t other[16] = {};
664 uint8_t pkt[16] = {};
665
666 slen = fr_dns_label_to_value_box(NULL, &vb, pkt, sizeof(pkt), other, false, NULL);
667 TEST_CHECK(slen < 0);
668}
669
670/* ----------------------------------------------------------------
671 * Compression with label tracking (block-based)
672 * ---------------------------------------------------------------- */
673
675{
676 ssize_t slen;
678 uint8_t pkt[256] = {};
679
680 labels_init(&lb, pkt, sizeof(pkt), false);
681
682 slen = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, "test.example.org", true, &lb);
683 TEST_CHECK(slen > 0);
684 TEST_MSG("encode returned %zd", slen);
685
686 TEST_CHECK(lb.num >= 1);
688}
689
691{
692 ssize_t slen;
693 uint8_t const *next;
694 uint8_t pkt[DNS_HDR_LEN + 11];
695
696 memset(pkt, 0, DNS_HDR_LEN);
697 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
698 pkt[17] = 3; pkt[18] = 'f'; pkt[19] = 'o'; pkt[20] = 'o';
699 pkt[21] = 0xc0; pkt[22] = 0x0c;
700
701 next = pkt + 17;
702 slen = fr_dns_label_uncompressed_length(pkt, pkt + 17, 6, &next, NULL);
703 TEST_CHECK(slen == 7);
704 TEST_MSG("Expected 7, got %zd", slen);
705}
706
708{
709 ssize_t slen;
710 uint8_t const *next;
712 uint8_t pkt[DNS_HDR_LEN + 11];
713
714 memset(pkt, 0, DNS_HDR_LEN);
715 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
716 pkt[17] = 3; pkt[18] = 'f'; pkt[19] = 'o'; pkt[20] = 'o';
717 pkt[21] = 0xc0; pkt[22] = 0x0c;
718
719 labels_init(&lb, pkt, sizeof(pkt), true);
720 /* Do NOT mark offset 12 */
721
722 next = pkt + 17;
723 slen = fr_dns_label_uncompressed_length(pkt, pkt + 17, 6, &next, &lb);
724 TEST_CHECK(slen <= 0);
725 TEST_MSG("Unmarked pointer target should be rejected, got %zd", slen);
726}
727
729{
730 ssize_t slen;
731 uint8_t const *next;
732 uint8_t pkt[DNS_HDR_LEN + 8];
733
734 memset(pkt, 0, DNS_HDR_LEN);
735 pkt[12] = 3; pkt[13] = 'c'; pkt[14] = 'o'; pkt[15] = 'm'; pkt[16] = 0x00;
736 pkt[17] = 0xc0; pkt[18] = 0x10; /* pointer to offset 16 (0x00 byte) */
737
738 next = pkt + 17;
739 slen = fr_dns_label_uncompressed_length(pkt, pkt + 17, 2, &next, NULL);
740 TEST_CHECK(slen <= 0);
741 TEST_MSG("Pointer to 0x00 should be rejected, got %zd", slen);
742}
743
744/*
745 * Encode: max label length (63 bytes exactly)
746 */
747
749{
750 ssize_t slen;
751 char label[64];
752 uint8_t buf[256] = {};
753
754 memset(label, 'a', 63);
755 label[63] = '\0';
756 slen = encode_label(buf, sizeof(buf), buf, label, false, NULL);
757 TEST_CHECK(slen == 65); /* 1 (length) + 63 (data) + 1 (terminator) */
758 TEST_MSG("Expected 65, got %zd", slen);
759}
760
762{
763 ssize_t slen;
764 uint8_t buf[256] = {};
765
766 slen = encode_label(buf, sizeof(buf), buf, "my-host-01.example.com", false, NULL);
767 TEST_CHECK(slen > 0);
768 TEST_MSG("Expected >0, got %zd", slen);
769}
770
771/* ----------------------------------------------------------------
772 * Roundtrip with compression
773 * ---------------------------------------------------------------- */
774
776{
777 ssize_t slen1, slen2, dec_len;
778 ssize_t uncomp_len;
779 uint8_t const *next_label;
780 fr_value_box_t vb_out;
782 TALLOC_CTX *ctx = talloc_init("test");
783 uint8_t pkt[256] = {};
784
785 labels_init(&lb, pkt, sizeof(pkt), false);
786
787 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, "example.com", true, &lb);
788 TEST_CHECK(slen1 > 0);
789
790 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, "foo.example.com", true, &lb);
791 TEST_CHECK(slen2 > 0);
792
793 next_label = pkt + DNS_HDR_LEN + slen1;
794 uncomp_len = fr_dns_label_uncompressed_length(pkt, pkt + DNS_HDR_LEN,
795 slen1 + slen2, &next_label, &lb);
796 TEST_CHECK(uncomp_len == 15);
797 TEST_MSG("Expected 15, got %zd", uncomp_len);
798
799 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, pkt + DNS_HDR_LEN, slen1 + slen2,
800 pkt + DNS_HDR_LEN + slen1, false, &lb);
801 TEST_CHECK(dec_len > 0);
802 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
803 TEST_CHECK(strcmp(vb_out.vb_strvalue, "foo.example.com") == 0);
804 TEST_MSG("Expected 'foo.example.com', got '%s'", vb_out.vb_strvalue);
805
806 fr_value_box_clear(&vb_out);
807 talloc_free(ctx);
808}
809
810/*
811 * Compression with 62 and 63 byte labels.
812 *
813 * These test both <= 63 checks in dns_label_compress():
814 *
815 * Path 1: recursive call when the NEXT label after the
816 * current one is still uncompressed. Tested by encoding "foo.LONG"
817 * where LONG is 62 or 63 bytes - the recursive call to compress LONG
818 * has *next == 62/63.
819 *
820 * Path 2: buffer scan where a candidate label is followed
821 * by an uncompressed label of length 62/63. Tested by encoding
822 * "bar.LONG" first (uncompressed), then "foo.LONG" - during the
823 * scan for "foo", we find "bar" whose next label (*ptr) is 62/63.
824 */
826{
827 ssize_t slen1, slen2, dec_len;
829 fr_value_box_t vb_out;
830 TALLOC_CTX *ctx = talloc_init("test");
831 char const *name1 = "bar.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
832 char const *name2 = "foo.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
833 uint8_t pkt[512] = {};
834
835 TEST_CHECK(strlen(name1) == 66);
836 TEST_CHECK(strlen(name2) == 66);
837
838 labels_init(&lb, pkt, sizeof(pkt), false);
839
840 /* First name goes in uncompressed */
841 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, name1, true, &lb);
842 TEST_CHECK(slen1 > 0);
843 TEST_MSG("First encode: %zd", slen1);
844
845 /*
846 * Second name should compress the 62-byte suffix.
847 * Uncompressed would be 4 + 1 + 62 + 1 = 68 bytes.
848 * Compressed should be "foo" (4 bytes) + pointer (2 bytes) = 6 bytes.
849 */
850 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, name2, true, &lb);
851 TEST_CHECK(slen2 > 0);
852 TEST_CHECK(slen2 < 68);
853 TEST_MSG("62-byte label compression: %zd (expected < 68)", slen2);
854
855 /* Round-trip decode to verify correctness */
856 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, pkt + DNS_HDR_LEN,
857 slen1 + slen2,
858 pkt + DNS_HDR_LEN + slen1,
859 false, &lb);
860 TEST_CHECK(dec_len > 0);
861 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
862 TEST_CHECK(strcmp(vb_out.vb_strvalue, name2) == 0);
863 TEST_MSG("Expected '%s', got '%s'", name2, vb_out.vb_strvalue);
864
865 fr_value_box_clear(&vb_out);
866 talloc_free(ctx);
867}
868
870{
871 ssize_t slen1, slen2, dec_len;
873 fr_value_box_t vb_out;
874 TALLOC_CTX *ctx = talloc_init("test");
875 char long63[64];
876 uint8_t pkt[512] = {};
877
878 /*
879 * The encoder limits non-first labels to 62 bytes (off-by-one in the dot tracking), so we test
880 * 63-byte labels as thue first (and only) label. Encode a standalone 63-byte label twice; the
881 * second should compress to a 2-byte pointer.
882 */
883 memset(long63, 'a', 63);
884 long63[63] = '\0';
885
886 labels_init(&lb, pkt, sizeof(pkt), false);
887
888 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, long63, true, &lb);
889 TEST_CHECK(slen1 == 65); /* 1 (length) + 63 (data) + 1 (terminator) */
890 TEST_MSG("First encode: %zd", slen1);
891
892 /* Duplicate name should fully compress to a 2-byte pointer */
893 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, long63, true, &lb);
894 TEST_CHECK(slen2 > 0);
895 TEST_CHECK(slen2 == 2);
896 TEST_MSG("63-byte duplicate compression: %zd (expected 2)", slen2);
897
898 /* Round-trip decode the compressed entry */
899 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, pkt + DNS_HDR_LEN,
900 slen1 + slen2,
901 pkt + DNS_HDR_LEN + slen1,
902 false, &lb);
903 TEST_CHECK(dec_len > 0);
904 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
905 TEST_CHECK(strcmp(vb_out.vb_strvalue, long63) == 0);
906 TEST_MSG("Expected '%s', got '%s'", long63, vb_out.vb_strvalue);
907
908 fr_value_box_clear(&vb_out);
909 talloc_free(ctx);
910}
911
912/*
913 * The same tests, but with a three-label name so that the 62/63-byte
914 * label is a middle suffix. This exercises the Path 2 more
915 * directly: when scanning the buffer, the candidate "bar" is
916 * followed by an uncompressed 62/63-byte label which IS the suffix
917 * pointer target.
918 */
920{
921 ssize_t slen1, slen2;
923 char const *name1 = "bar.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com";
924 char const *name2 = "foo.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com";
925 uint8_t pkt[512] = {};
926
927 TEST_CHECK(strlen(name1) == 70); /* "bar." + 62 + ".com"*/
928 TEST_CHECK(strlen(name2) == 70);
929
930 labels_init(&lb, pkt, sizeof(pkt), false);
931
932 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, name1, true, &lb);
933 TEST_CHECK(slen1 > 0);
934 TEST_MSG("First encode: %zd", slen1);
935
936 /*
937 * Compression produces "foo" + pointer to the "aaa...com" suffix. When scanning for "foo" in
938 * the buffer, the code finds "bar" whose next label is the 62-byte 'a' label (*ptr == 62). The
939 * <= 63 check at line 523 allows this to be recognized as the suffix target.
940 */
941 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, name2, true, &lb);
942 TEST_CHECK(slen2 > 0);
943 TEST_CHECK(slen2 < slen1);
944 TEST_MSG("Compressed size %zd should be < %zd", slen2, slen1);
945}
946
948{
949 ssize_t slen1, slen2, dec_len;
951 fr_value_box_t vb_out;
952 TALLOC_CTX *ctx = talloc_init("test");
953 char const *name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com";
954 uint8_t pkt[512] = {};
955
956 /*
957 * Test compression of a multi-label name where the first label is 63 bytes. Encode "<63
958 * a's>.com" twice.
959 *
960 * On the second encode, dns_label_compress recursively compresses "com" first, then scans the
961 * buffer for the 63-byte label. The buffer scan at checks *ptr <= 63 where ptr points to the
962 * "com" label (3 bytes) after the 63-byte label in the first entry. It then verifies ptr ==
963 * suffix (both point to "com" in name1) and compares the 63-byte labels.
964 */
965 TEST_CHECK(strlen(name) == 67);
966
967 labels_init(&lb, pkt, sizeof(pkt), false);
968
969 slen1 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN, name, true, &lb);
970 TEST_CHECK(slen1 > 0);
971 TEST_MSG("First encode: %zd", slen1);
972
973 /* Duplicate should fully compress to a 2-byte pointer */
974 slen2 = encode_label(pkt, sizeof(pkt), pkt + DNS_HDR_LEN + slen1, name, true, &lb);
975 TEST_CHECK(slen2 > 0);
976 TEST_CHECK(slen2 == 2);
977 TEST_MSG("Compressed size %zd (expected 2)", slen2);
978
979 /* Round-trip decode */
980 dec_len = fr_dns_label_to_value_box(ctx, &vb_out, pkt + DNS_HDR_LEN,
981 slen1 + slen2,
982 pkt + DNS_HDR_LEN + slen1,
983 false, &lb);
984 TEST_CHECK(dec_len > 0);
985 TEST_CHECK(vb_out.type == FR_TYPE_STRING);
986 TEST_CHECK(strcmp(vb_out.vb_strvalue, name) == 0);
987 TEST_MSG("Expected '%s', got '%s'", name, vb_out.vb_strvalue);
988
989 fr_value_box_clear(&vb_out);
990 talloc_free(ctx);
991}
992
994 /* Encoding */
995 { "encode_empty_string", test_encode_empty_string },
996 { "encode_root_dot", test_encode_root_dot },
997 { "encode_simple_label", test_encode_simple_label },
998 { "encode_multi_label", test_encode_multi_label },
999 { "encode_trailing_dot", test_encode_trailing_dot },
1000 { "encode_underscore_prefix", test_encode_underscore_prefix },
1001 { "encode_max_label_length", test_encode_max_label_length },
1002 { "encode_hyphen_and_digits", test_encode_hyphen_and_digits },
1003
1004 /* Encoding errors */
1005 { "encode_null_inputs", test_encode_null_inputs },
1006 { "encode_non_string_type", test_encode_non_string_type },
1007 { "encode_invalid_chars", test_encode_invalid_chars },
1008 { "encode_double_dot", test_encode_double_dot },
1009 { "encode_leading_dot", test_encode_leading_dot },
1010 { "encode_label_too_long", test_encode_label_too_long },
1011 { "encode_buffer_too_small", test_encode_buffer_too_small },
1012 { "encode_where_outside_buf", test_encode_where_outside_buf },
1013
1014 /* Decoding / uncompressed length */
1015 { "decode_simple_label", test_decode_simple_label },
1016 { "decode_multi_label", test_decode_multi_label },
1017 { "decode_root_label", test_decode_root_label },
1018 { "decode_compressed_pointer", test_decode_compressed_pointer },
1019 { "decode_null_inputs", test_decode_null_inputs },
1020 { "decode_label_invalid_chars", test_decode_label_invalid_chars },
1021 { "decode_total_length_exceeds_255", test_decode_total_length_exceeds_255 },
1022
1023 /* Decode error paths */
1024 { "decode_forward_pointer_rejected", test_decode_forward_pointer_rejected },
1025 { "decode_self_pointer_rejected", test_decode_self_pointer_rejected },
1026 { "decode_pointer_to_pointer_rejected", test_decode_pointer_to_pointer_rejected },
1027 { "decode_invalid_high_bits", test_decode_invalid_high_bits },
1028 { "decode_label_overflow", test_decode_label_overflow },
1029 { "decode_pointer_to_zero_rejected", test_pointer_to_zero_label_rejected },
1030
1031 /* Network verify */
1032 { "verify_simple", test_verify_simple },
1033 { "verify_empty", test_verify_empty },
1034
1035 /* Value box decode errors */
1036 { "decode_vb_zero_len", test_decode_to_value_box_zero_len },
1037 { "decode_vb_label_outside_buf", test_decode_to_value_box_label_outside_buf },
1038
1039 /* Pointer tracking */
1040 { "pointer_valid_no_tracking", test_pointer_valid_no_tracking },
1041 { "pointer_invalid_with_mark", test_pointer_invalid_with_mark_tracking },
1042
1043 /* Label block tracking */
1044 { "labels_block_tracking", test_labels_block_tracking },
1045
1046 /* Round-trip */
1047 { "roundtrip_simple", test_roundtrip_simple },
1048 { "roundtrip_trailing_dot", test_roundtrip_trailing_dot },
1049 { "roundtrip_root", test_roundtrip_root },
1050 { "roundtrip_underscore", test_roundtrip_underscore },
1051 { "roundtrip_case_preservation", test_roundtrip_case_preservation },
1052
1053 /* Compression */
1054 { "compress_two_names", test_compress_two_names },
1055 { "roundtrip_compressed", test_roundtrip_compressed },
1056 { "compress_62_byte_label", test_compress_62_byte_label },
1057 { "compress_63_byte_label", test_compress_63_byte_label },
1058 { "compress_middle_62_byte_label", test_compress_middle_62_byte_label },
1059 { "compress_middle_63_byte_label", test_compress_middle_63_byte_label },
1060
1062};
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
ssize_t fr_dns_label_uncompressed_length(uint8_t const *packet, uint8_t const *buf, size_t buf_len, uint8_t const **next, fr_dns_labels_t *lb)
Get the uncompressed length of a DNS label in a network buffer.
Definition dns.c:883
ssize_t fr_dns_label_from_value_box(size_t *need, uint8_t *buf, size_t buf_len, uint8_t *where, bool compression, fr_value_box_t const *value, fr_dns_labels_t *lb)
Encode a single value box of type string, serializing its contents to a dns label.
Definition dns.c:636
ssize_t fr_dns_labels_network_verify(uint8_t const *packet, uint8_t const *buf, size_t buf_len, uint8_t const *start, fr_dns_labels_t *lb)
Verify that a network buffer contains valid DNS labels.
Definition dns.c:1136
ssize_t fr_dns_label_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *dst, uint8_t const *src, size_t len, uint8_t const *label, bool tainted, fr_dns_labels_t *lb)
Decode a fr_value_box_t from one DNS label.
Definition dns.c:1224
static void test_encode_multi_label(void)
Definition dns_tests.c:107
static void test_roundtrip_simple(void)
Definition dns_tests.c:511
TEST_LIST
Definition dns_tests.c:993
static void test_encode_underscore_prefix(void)
Definition dns_tests.c:140
static void test_decode_self_pointer_rejected(void)
Definition dns_tests.c:352
static void test_encode_buffer_too_small(void)
Definition dns_tests.c:234
static void test_roundtrip_compressed(void)
Definition dns_tests.c:775
static void test_encode_root_dot(void)
Definition dns_tests.c:82
static void test_encode_where_outside_buf(void)
Definition dns_tests.c:247
static void test_encode_double_dot(void)
Definition dns_tests.c:203
static void test_decode_pointer_to_pointer_rejected(void)
Definition dns_tests.c:367
static void test_encode_label_too_long(void)
Definition dns_tests.c:221
static void labels_init(fr_dns_labels_t *lb, uint8_t const *packet, size_t packet_len, bool use_mark)
Definition dns_tests.c:37
static void test_encode_max_label_length(void)
Definition dns_tests.c:748
static void test_compress_middle_63_byte_label(void)
Definition dns_tests.c:947
static void test_verify_empty(void)
Definition dns_tests.c:495
static void test_decode_to_value_box_zero_len(void)
Definition dns_tests.c:649
#define DNS_HDR_LEN
Definition dns_tests.c:29
static fr_dns_block_t test_blocks[256]
Definition dns_tests.c:34
static void test_encode_null_inputs(void)
Definition dns_tests.c:156
static void test_compress_two_names(void)
Definition dns_tests.c:621
static void test_compress_63_byte_label(void)
Definition dns_tests.c:869
static void test_decode_compressed_pointer(void)
Definition dns_tests.c:314
static void test_encode_non_string_type(void)
Definition dns_tests.c:174
static void test_decode_label_invalid_chars(void)
Definition dns_tests.c:440
static void test_encode_trailing_dot(void)
Definition dns_tests.c:125
static void test_decode_label_overflow(void)
Definition dns_tests.c:399
static void test_pointer_invalid_with_mark_tracking(void)
Definition dns_tests.c:707
static void test_roundtrip_root(void)
Definition dns_tests.c:556
static void test_labels_block_tracking(void)
Definition dns_tests.c:674
static void test_encode_leading_dot(void)
Definition dns_tests.c:212
static void test_verify_simple(void)
Definition dns_tests.c:482
static void test_encode_invalid_chars(void)
Definition dns_tests.c:185
static void test_decode_root_label(void)
Definition dns_tests.c:299
static void test_compress_62_byte_label(void)
Definition dns_tests.c:825
static void test_decode_forward_pointer_rejected(void)
Definition dns_tests.c:336
static ssize_t encode_label(uint8_t *buf, size_t buf_len, uint8_t *where, char const *str, bool compression, fr_dns_labels_t *lb)
Definition dns_tests.c:60
static void test_decode_to_value_box_label_outside_buf(void)
Definition dns_tests.c:659
static void test_compress_middle_62_byte_label(void)
Definition dns_tests.c:919
static void test_pointer_valid_no_tracking(void)
Definition dns_tests.c:690
static void test_roundtrip_trailing_dot(void)
Definition dns_tests.c:535
static uint8_t test_marker[65536]
Definition dns_tests.c:35
static void test_pointer_to_zero_label_rejected(void)
Definition dns_tests.c:728
static void test_encode_empty_string(void)
Definition dns_tests.c:71
static void test_encode_hyphen_and_digits(void)
Definition dns_tests.c:761
static void test_decode_total_length_exceeds_255(void)
Definition dns_tests.c:455
static void test_decode_null_inputs(void)
Definition dns_tests.c:415
static void test_decode_invalid_high_bits(void)
Definition dns_tests.c:384
static void test_roundtrip_case_preservation(void)
Definition dns_tests.c:597
static void test_roundtrip_underscore(void)
Definition dns_tests.c:577
static void test_decode_simple_label(void)
Definition dns_tests.c:262
static void test_decode_multi_label(void)
Definition dns_tests.c:280
static void test_encode_simple_label(void)
Definition dns_tests.c:93
talloc_free(hp)
uint16_t start
Definition dns.h:31
uint8_t const * start
start of packet
Definition dns.h:36
uint8_t const * end
end of the packet
Definition dns.h:37
fr_dns_block_t * blocks
maximum number of labels
Definition dns.h:41
uint8_t * mark
markup buffer used for decoding.
Definition dns.h:38
uint16_t end
Definition dns.h:32
int num
number of used labels
Definition dns.h:39
@ FR_TYPE_STRING
String of printable characters.
long int ssize_t
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
static char const * name
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4362
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition value.h:616
#define fr_box_strvalue(_val)
Definition value.h:308