The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dns.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/** Functions to manipulate DNS labels
18 *
19 * @file src/lib/util/dns.c
20 *
21 * @copyright 2019 The FreeRADIUS server project
22 * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
23 */
24RCSID("$Id: 1b6ff71fd6bb4a6a42377f12780b68f5f51a2ae3 $")
25
26#include <freeradius-devel/util/misc.h>
27#include <freeradius-devel/util/strerror.h>
28#include <freeradius-devel/util/value.h>
29#include <freeradius-devel/util/dns.h>
30#include <freeradius-devel/util/proto.h>
31
32#define MAX_OFFSET (1 << 14)
33
34static int dns_label_add(fr_dns_labels_t *lb, uint8_t const *start, uint8_t const *end)
35{
36 size_t offset, size = end - start;
37 fr_dns_block_t *block;
38
39 /*
40 * If we don't care about tracking the blocks, then don't
41 * do anything.
42 */
43 if (!lb) return 0;
44
45 fr_assert(start >= lb->start);
46 fr_assert(end >= start);
47
48 offset = start - lb->start;
49
50 /*
51 * DNS packets can be up to 64K in size, but the
52 * compressed pointers can only be up to 2^14 in size.
53 * So we just ignore offsets which are greater than 2^14.
54 */
55 if ((offset + size) >= MAX_OFFSET) return 0;
56
57 /*
58 * We're not tracking labels, so don't do anything.
59 */
60 if (lb->max == 1) return 0;
61
62 FR_PROTO_TRACE("adding label at offset %zu", offset);
63
64 /*
65 * We add blocks append-only. No adding new blocks in
66 * the middle of a packet.
67 */
68 block = &lb->blocks[lb->num - 1];
69 fr_assert(block->start <= offset);
70 fr_assert(offset);
71
72 FR_PROTO_TRACE("Last block (%d) is %u..%u", lb->num - 1, block->start, block->end);
73
74 /*
75 * Fits within an existing block.
76 */
77 if (block->end == offset) {
78 block->end += size;
79 FR_PROTO_TRACE("Expanding last block (%d) to %u..%u", lb->num - 1, block->start, block->end);
80 return 0;
81 }
82
83 /*
84 * It's full, die.
85 */
86 if (lb->num == lb->max) return -1;
87
88 lb->num++;
89 block++;
90
91 block->start = offset;
92 block->end = offset + size;
93 FR_PROTO_TRACE("Appending block (%d) to %u..%u", lb->num - 1, block->start, block->end);
94
95 return 0;
96}
97
98static void dns_label_mark(fr_dns_labels_t *lb, uint8_t const *p)
99{
100 if (!lb || !lb->mark) return;
101
102 fr_assert(p >= (lb->start + 12)); /* can't point to the packet header */
103 fr_assert(!lb->end || (p < lb->end));
104
105 lb->mark[p - lb->start] = 1;
106}
107
108
110{
111 int i;
112
113 if (!lb) return true; /* we have no idea, so allow it */
114
115 if (lb->mark) return (lb->mark[offset] != 0);
116
117 /*
118 * Brute-force searching.
119 *
120 * @todo - manually walk through the pointers for the block?
121 */
122 for (i = 0; i < lb->num; i++) {
123 FR_PROTO_TRACE("Checking block %d %u..%u against %u",
124 i, lb->blocks[i].start, lb->blocks[i].end, offset);
125
126 if (offset < lb->blocks[i].start) return false;
127
128 if (offset < lb->blocks[i].end) return true;
129 }
130
131 return false;
132}
133
134/** Compare two labels in a case-insensitive fashion.
135 *
136 * This function requires that the input is valid, i.e. all
137 * characters are within [-0-9A-Za-z]. If any other input is given,
138 * it will break.
139 */
140static bool labelcmp(uint8_t const *a, uint8_t const *b, size_t len)
141{
142 for (/* nothing */; len > 0; len--) {
143 if (*a == *b) {
144 a++;
145 b++;
146 continue;
147 }
148
149 /*
150 * If one or the other isn't a letter, we can't
151 * do case insensitive comparisons of them, so
152 * they can't match.
153 */
154 if (!(((*a >= 'a') && (*a <= 'z')) || ((*a >= 'A') && (*a <= 'Z')))) return false;
155 if (!(((*b >= 'a') && (*b <= 'z')) || ((*b >= 'A') && (*b <= 'Z')))) return false;
156
157 /*
158 * If they're equal but different case, then the
159 * only bit that's different is 0x20.
160 */
161 if (((*a)^(*b)) != 0x20) {
162 return false;
163 }
164
165 a++;
166 b++;
167 }
168
169 return true;
170}
171
172/** Compress "label" by looking at the label recursively.
173 *
174 * For "ftp.example.com", it searches the input buffer for a matching
175 * "com". It only does string compares if it finds bytes "03 xx xx
176 * xx 00". This means that the scan is quick, because most bytes are
177 * skipped.
178 *
179 * If a matching string is found, the label is updated to replace
180 * "com" with a 2-byte pointer P1. The process then proceeds
181 * recursively, with "exampleP1".
182 *
183 * The input buffer is the scanned again for labels of matching
184 * length (7 here), AND which either end in the value of "P1", or end
185 * *at* P1. If we find a match, we replace "exampleP1" with "P2".
186 * The process then proceeds recursively with "ftpP2".
187 *
188 * Since the algorithm replaces known suffixes with pointers, we
189 * *never* have to compare full names. Instead, we only ever compare
190 * one label to one other label. And then only if the labels have
191 * the same lengths AND the same suffixes (00 byte, or a pointer P).
192 *
193 * As an extra optimization, we track the start of the label where we
194 * found the compressed pointer. e.g. "www.example.com" when
195 * compressing "com". We know that the "com" string CANNOT appear
196 * before this label. Because if it did, then the "www.example.com"
197 * name would have instead been compressed, as in "www.exampleP1".
198 *
199 * This optimization ensures that we scan as little of the buffer as
200 * possible, by moving the search start ahead in the buffer. This
201 * optimization also means that in many cases, the suffix we're
202 * looking for (e.g. "example.com") is in the first label we search.
203 * Which means that we end up ignoring most of the buffer.
204 *
205 * This algorithm is O(N * B), where N is the number of labels in a
206 * name (e.g. 3 for "ftp.example.com"), and "B" is the size of the
207 * buffer. It also does linear scans of the buffer, which are good
208 * for read-ahead. Each input label is compared to labels in the
209 * buffer only when very limited situations apply. And we never
210 * compare the full input name to full names in the buffer.
211 *
212 * In the case where we are adding many names from the same zone to
213 * the input buffer, the input buffer will start with the zone name.
214 * So any searches will match that. The only reason to continue
215 * scanning the buffer is to see if the name prefix already exists.
216 * If we assume that the records do not contain duplicates, then we
217 * can likely skip that scan, too.
218 *
219 * Adding that optimization, however, requires tracking the maximum
220 * size of a name across multiple invocations of the function. For
221 * example, if the maximum length name in the buffer is 3 labels, and
222 * we're adding a 3 label name, then we can stop scanning the buffer
223 * as soon as we compressed the 2 suffix labels. Since we are
224 * guaranteed that there are no duplicates, we are sure that there is
225 * no existing 3-label name which matches a 3-label name in the
226 * buffer.
227 *
228 * Note that this function does NOT follow pointers in the input
229 * buffer!
230 *
231 *
232 * A different and more straightforward approach is to loop over all
233 * labels in the name from longest to shortest, and comparing them to
234 * each name in the buffer in turn. That algorithm ends up being
235 * O(L1 * T * L2), where L1 is the length of the input name, T is the
236 * total number of names in the buffer, and L2 is the average length
237 * of names in the buffer. This algorithm can result in the buffer
238 * being scanned many, many, times. The scan is also done forwards
239 * (due to comparing names one after the other), but also backwards
240 * (due to following pointers). Which makes for poor locality of
241 * reference.
242 *
243 * i.e. that approach *has* to scan the entire input buffer, because
244 * that's where all of the names are. Further, it has to scan it at
245 * least "N" times, because there are N labels in the input name. So
246 * O(N * B) is the *lower* bound for this algorithm.
247 *
248 * It gets worse when the straightforward algorithm does pointer
249 * following instead of pointer comparisons. It ends up scanning
250 * portions of the input buffer many, many, times. i.e. it can
251 * compare an input "com" name to "org" once for every "org" name in
252 * the input buffer. In contrast, because our algorithm does not do
253 * pointer following, it only compares "com" to "org" once.
254 *
255 * @param[in] packet where the packet starts
256 * @param[in] start input buffer holding one or more labels
257 * @param[in] end end of the input buffer
258 * @param[out] new_search Where the parent call to dns_label_compress()
259 * should start searching from, instead of from "start".
260 * @param[in] label label to add to the buffer.
261 * @param[out] label_end updated end of the input label after compression.
262 * @return
263 * - false, we didn't compress the input
264 * - true, we did compress the input.
265 */
266static bool dns_label_compress(uint8_t const *packet, uint8_t const *start, uint8_t const *end, uint8_t const **new_search,
267 uint8_t *label, uint8_t **label_end)
268{
269 uint8_t *next;
270 uint8_t const *q, *ptr, *suffix, *search;
271 uint16_t offset;
272 bool compressed = false;
273
274 /*
275 * Don't compress "end of label" byte or pointers.
276 */
277 if (!*label || (*label > 63)) {
278 return false;
279 }
280
281 /*
282 * Check the next label. Note that this is *after*
283 * "end". It also MUST be a valid, uncompressed label.
284 */
285 next = label + *label + 1;
286
287 /*
288 * Note that by design, next > end. We don't care about
289 * the size of the buffer we put "label" into. We only
290 * care that all bytes of "label" are valid, and we don't
291 * access memory after "label".
292 */
293
294 /*
295 * On the first call, begin searching from the start of
296 * the buffer.
297 *
298 * For subsequent calls, begin from where we started
299 * searching before.
300 */
301 if (!new_search) {
302 search = start;
303 } else {
304 search = *new_search;
305 }
306
307 /*
308 * We're at the last uncompressed label, scan the input
309 * buffer to see if there's a match.
310 *
311 * The scan skips ahead until it find both a label length
312 * that matches, AND "next" label which is 0x00. Only
313 * then does it do the string compare of the label
314 * values.
315 *
316 * We speed this up slightly by tracking the previous
317 * uncompressed pointer. If we do compress the current
318 * label, then we should also tell the caller where the
319 * previous uncompressed label started. That way the
320 * caller can start looking there for the next piece to
321 * compress. There's no need to search from the
322 * beginning of the input buffer, as we're sure that
323 * there is no earlier instance of the suffix we found.
324 *
325 * i.e. as we compress the current name, we start
326 * searching not at the beginning of the input buffer/
327 * Instead, we start searching at the name which contains
328 * the label that we just compressed. The previous
329 * sarching guarantees that no name *before* that one
330 * will match the suffix we're looking for. So we can
331 * skip all of the previous names in subsequent searches,
332 */
333 if (*next == 0x00) {
334 q = search;
335 while (q < end) {
336 if (*q == 0x00) {
337 q++;
338
339 /*
340 * None of the previous names
341 * matched, so we tell the caller
342 * to start searching from the
343 * next name in the buffer.
344 */
345 search = q;
346 continue;
347 }
348
349 /*
350 * Our label is a terminal one. Which
351 * can't point to a pointer.
352 */
353 if (*q > 63) {
354 q += 2;
355
356 /*
357 * None of the previous
358 * uncompressed names matched,
359 * and this pointer refers to a
360 * compressed name. So it
361 * doesn't match, either.
362 */
363 search = q;
364 continue;
365 }
366
367 /*
368 * We now have a label which MIGHT match.
369 * We have to walk down it until it does
370 * match. But we don't update "search"
371 * here, because there may be a suffix
372 * which matches.
373 */
374 ptr = q + *q + 1;
375 if (ptr >= end) return false;
376
377 /*
378 * Label lengths aren't the same, skip
379 * it.
380 */
381 if (*q != *label) {
382 q = ptr;
383 continue;
384 }
385
386 /*
387 * Our input label ends with 0x00. If
388 * this label doesn't end with 0x00, skip
389 * it.
390 */
391 if (*ptr != 0x00) {
392 q = ptr;
393 continue;
394 }
395
396 /*
397 * The pointer is too far away. Don't
398 * point to it. This check is mainly for
399 * static analyzers.
400 */
401 if ((q - packet) > (1 << 14)) return false;
402
403 /*
404 * Only now do case-insensitive
405 * comparisons.
406 */
407 if (!labelcmp(q + 1, label + 1, *label)) {
408 q = ptr;
409 continue;
410 }
411
412 /*
413 * We have a match. Replace the input
414 * label with a compressed pointer. Tell
415 * the caller the start of the found
416 * name, so subsequent searches can start
417 * from there. Then return to the caller
418 * that we managed to compress this
419 * label.
420 */
421 offset = (q - packet);
422 label[0] = (offset >> 8) | 0xc0;
423 label[1] = offset & 0xff;
424 *label_end = label + 2;
425 if (new_search) *new_search = search;
426 return true;
427 }
428
429 return false;
430 }
431
432 /*
433 * The next label is still uncompressed, so we call
434 * ourselves recursively in order to compress it.
435 */
436 if (*next <= 63) {
437 if (!dns_label_compress(packet, start, end, &search, next, label_end)) return false;
438
439 /*
440 * Else it WAS compressed.
441 */
442 compressed = true;
443 }
444
445 /*
446 * The next label wasn't compressed, OR it is invalid,
447 * skip it. This check is here only to shut up the
448 * static analysis tools.
449 */
450 if (*next < 0xc0) {
451 return compressed;
452 }
453
454 /*
455 * Remember where our suffix points to.
456 */
457 suffix = packet + ((next[0] & ~0xc0) << 8) + next[1];
458
459 /*
460 * Our label now ends with a compressed pointer. Scan
461 * the input until we find either an uncompressed label
462 * which ends with the same compressed pointer, OR we
463 * find an uncompressed label which ends AT our
464 * compressed pointer.
465 *
466 * Note that we start searching from the beginning of the
467 * label which resulted in us finding the compressed
468 * pointer!
469 *
470 * We're guaranteed that any label BEFORE that one
471 * doesn't end with a matching compressed pointer.
472 */
473 q = search;
474 while (q < end) {
475 if (*q == 0x00) {
476 q++;
477
478 /*
479 * None of the previous stuff matched, so
480 * we tell the caller to start searching
481 * from the next name.
482 */
483 search = q;
484 continue;
485 }
486
487 /*
488 * Skip compressed pointers. We can't point to
489 * compressed pointers.
490 */
491 if (*q > 63) {
492 q += 2;
493
494 /*
495 * None of the previous uncompressed
496 * names matched, and this pointer refers
497 * to a compressed name. So it doesn't
498 * match, either.
499 */
500 search = q;
501 continue;
502 }
503
504 /*
505 * We now have an uncompressed label in the input
506 * buffer. Check for a match.
507 */
508 ptr = q + *q + 1;
509 if (ptr >= end) return compressed;
510
511 /*
512 * Label lengths aren't the same, skip it.
513 */
514 if (*q != *label) {
515 q = ptr;
516 continue;
517 }
518
519 /*
520 * If the NEXT label is uncompressed, then skip
521 * it unless it's the suffix we're pointing to.
522 */
523 if (*ptr <= 63) {
524 if (ptr != suffix) {
525 q = ptr;
526 continue;
527 }
528
529 goto check_label;
530 }
531
532 /*
533 * The next label is a compressed pointer. If
534 * the compressed pointers are different, then
535 * skip both this label and the compressed
536 * pointer after it.
537 */
538 if ((ptr[0] != next[0]) ||
539 (ptr[1] != next[1])) {
540 q = ptr + 2;
541
542 /*
543 * None of the previous uncompressed
544 * names matched, and this pointer refers
545 * to a compressed name. So it doesn't
546 * match, either.
547 */
548 search = q;
549 continue;
550 }
551
552 check_label:
553 /*
554 * Pointer is too far away. Don't point
555 * to it.
556 */
557 if ((q - packet) > (1 << 14)) return compressed;
558
559 /*
560 * Only now do case-insensitive
561 * comparisons.
562 */
563 if (!labelcmp(q + 1, label + 1, *label)) {
564 q = ptr;
565 continue;
566 }
567
568 /*
569 * We have a match. Replace the input
570 * label with a compressed pointer. Tell
571 * the caller the start of the found
572 * name, so subsequent searches can start
573 * from there. Then return to the caller
574 * that we managed to compress this
575 * label.
576 */
577 offset = (q - packet);
578 label[0] = (offset >> 8) | 0xc0;
579 label[1] = offset & 0xff;
580 *label_end = label + 2;
581 if (new_search) *new_search = search;
582 return true;
583 }
584
585 /*
586 * Who knows what it is, we couldn't compress it.
587 */
588 return compressed;
589}
590
591
592/** Encode a single value box of type string, serializing its contents to a dns label
593 * in a dbuff
594 *
595 * @param[in] dbuff Buffer where labels are written
596 * @param[in] compression Whether or not to do DNS label compression.
597 * @param[in] value to encode.
598 * @param[in] lb label tracking data structure.
599 * @return
600 * - >=0 the number of bytes written to the dbuff
601 * - <0 error
602 */
604{
605 ssize_t slen;
606 size_t need = 0;
607
608 slen = fr_dns_label_from_value_box(&need, dbuff->p, fr_dbuff_remaining(dbuff), dbuff->p, compression, value, lb);
609 if (slen < 0) return -need;
610
611 fr_dbuff_advance(dbuff, (size_t) slen);
612 return slen;
613}
614
615/** Encode a single value box of type string, serializing its contents to a dns label
616 *
617 * This functions takes a large buffer and encodes the label in part
618 * of the buffer. This API is necessary in order to allow DNS label
619 * compression.
620 *
621 * @param[out] need if not NULL, how long "buf_len" should be to
622 * serialize the rest of the data.
623 * Note: Only variable length types will be partially
624 * encoded. Fixed length types will not be partially encoded.
625 * @param[out] buf Buffer where labels are stored
626 * @param[in] buf_len The length of the output buffer
627 * @param[out] where Where to write this label
628 * @param[in] compression Whether or not to do DNS label compression.
629 * @param[in] value to encode.
630 * @param[in] lb label tracking data structure
631 * @return
632 * - 0 no bytes were written, see need value to determine
633 * - >0 the number of bytes written to "where", NOT "buf + where + outlen"
634 * - <0 on error.
635 */
636ssize_t fr_dns_label_from_value_box(size_t *need, uint8_t *buf, size_t buf_len, uint8_t *where, bool compression,
638{
639 uint8_t *label;
640 uint8_t const *end;
641 uint8_t const *q, *strend, *last;
642 uint8_t *data;
643 bool underscore = true;
644
645 if (!buf || !buf_len || !where || !value) {
646 fr_strerror_const("Invalid input");
647 return -1;
648 }
649
650 end = buf + buf_len;
651
652 /*
653 * Don't allow stupidities
654 */
655 if (!((where >= buf) && (where < (buf + buf_len)))) {
656 fr_strerror_const("Label to write is outside of buffer");
657 return -1;
658 }
659
660 /*
661 * We can only encode strings.
662 */
663 if (value->type != FR_TYPE_STRING) {
664 fr_strerror_const("Asked to encode non-string type");
665 return -1;
666 }
667
668 /*
669 * '.' or empty string is special, and is encoded as a
670 * plain zero byte.
671 *
672 * Since "where < end", we can always write 1 byte to it.
673 */
674 if ((value->vb_length == 0) ||
675 ((value->vb_length == 1) && (value->vb_strvalue[0] == '.'))) {
676 *where = 0x00;
677 return 1;
678 }
679
680 /*
681 * Sanity check the name before writing anything to the
682 * buffer.
683 *
684 * Only encode [-0-9a-zA-Z]. Anything else is forbidden.
685 * Dots at the start are forbidden. Double dots are
686 * forbidden.
687 */
688 q = (uint8_t const *) value->vb_strvalue;
689 strend = q + value->vb_length;
690 last = q;
691
692 if (*q == '.') {
693 fr_strerror_const("Empty labels are invalid");
694 return -1;
695 }
696
697 /*
698 * Convert it piece by piece.
699 */
700 while (q < strend) {
701 /*
702 * Allow underscore at the start of a label.
703 */
704 if (underscore) {
705 underscore = false;
706
707 if (*q == '_') goto next;
708 }
709
710 if (*q == '.') {
711 /*
712 * Don't count final dot as an
713 * intermediate dot, and don't bother
714 * encoding it.
715 */
716 if ((q + 1) == strend) {
717 strend--;
718 break;
719 }
720
721 if (q[1] == '.') {
722 fr_strerror_const("Double dots '..' are forbidden");
723 return -1;
724 }
725 last = q;
726
727 /*
728 * We had a dot, allow underscore as the
729 * first character of the next label.
730 */
731 underscore = true;
732
733 } else if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
734 ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
735 fr_strerror_printf("Invalid character 0x%02x in label", *q);
736 return -1;
737 }
738
739 next:
740 q++;
741
742 if ((q - last) > 63) {
743 fr_strerror_const("Label is larger than 63 characters");
744 return -1;
745 }
746 }
747
748 q = (uint8_t const *) value->vb_strvalue;
749
750 /*
751 * For now, just encode the value as-is. We do
752 * compression as a second step.
753 *
754 * We need a minimum length of string + beginning length
755 * + trailing zero. Intermediate '.' are converted to
756 * length bytes.
757 */
758 if ((where + (strend - q) + 2) > end) {
759 if (need) *need = (where + (strend - q) + 2) - buf;
760 return 0;
761 }
762
763 label = where;
764 *label = 0;
765 data = label + 1;
766
767 /*
768 * @todo - encode into a local buffer, and then try to
769 * compress that into the output buffer. This means that
770 * the output buffer can be a little bit smaller.
771 */
772 while (q < strend) {
773 fr_assert(data < end);
774 fr_assert((data - where) < 255);
775
776 /*
777 * '.' is a label delimiter.
778 *
779 * We've already checked above for '.' at the
780 * start, for double dots, and have already
781 * suppressed '.' at the end of the string.
782 *
783 * Start a new label.
784 */
785 if (*q == '.') {
786 label = data;
787 *label = 0;
788 data = label + 1;
789
790 q++;
791 continue;
792 }
793
794 *(data++) = *(q++);
795 (*label)++;
796 fr_assert(*label <= 63);
797 }
798
799 *(data++) = 0; /* end of label */
800
801 /*
802 * If we're compressing it, and we have data to compress,
803 * then do it.
804 */
805 if (compression && ((data - where) > 2)) {
806 if (lb) {
807 int i;
808
809 /*
810 * Loop over the parts of the packet which have DNS labels.
811 *
812 * Note that the dns_label_compress() function does NOT follow pointers in the
813 * start/end block which it's searching! It just tries to compress the *input*,
814 * and assumes that the input is compressed last label to first label.
815 *
816 * In addition, dns_label_compress() tracks where in the block it started
817 * searching. So it only scans the block once, even if we pass a NULL search
818 * parameter to it.
819 *
820 * We could start compression from the *last* block. When we add
821 * "www.example.com" and then "ftp.example.com", we could point "ftp" to the
822 * "example.com" portion. which is already in the packet. However, doing that
823 * would require that dns_label_compress() follows pointers in the block it's
824 * searching. Which would greatly increase the complexity of the algorithm.
825 *
826 *
827 * We could still optimize this algorithm a bit, by tracking which parts of the
828 * buffer have DNS names of label length 1, 2, etc. Doing that would mean more
829 * complex data structures, but fewer passes over the packet.
830 */
831 for (i = 0; i < lb->num; i++) {
832 bool compressed;
833
834 FR_PROTO_TRACE("Trying to compress %s in block %d of %u..%u",
835 value->vb_strvalue, i,
836 lb->blocks[i].start, lb->blocks[i].end);
837
838 compressed = dns_label_compress(lb->start, lb->start + lb->blocks[i].start,
839 lb->start + lb->blocks[i].end,
840 NULL, where, &data);
841 if (compressed) {
842 FR_PROTO_TRACE("Compressed label in block %d", i);
843 if (*(where + *where + 1) >= 0xc0) {
844 FR_PROTO_TRACE("Next label is compressed, stopping");
845 }
846 }
847 }
848
849 dns_label_add(lb, where, data);
850
851 } else if (buf != where) {
852 if (dns_label_compress(buf, buf, where, NULL, where, &data)) {
853 FR_PROTO_TRACE("Compressed single label %s to %zu bytes",
854 value->vb_strvalue, (size_t) (data - where));
855 } else {
856 FR_PROTO_TRACE("Did not compress single label");
857 }
858 }
859 } else {
860 FR_PROTO_TRACE("Not compressing label");
861 }
862
863 fr_assert(data > where);
864 return data - where;
865}
866
867/** Get the *uncompressed* length of a DNS label in a network buffer.
868 *
869 * i.e. how bytes are required to store the uncompressed version of
870 * the label.
871 *
872 * Note that a bare 0x00 byte has length 1, to account for '.'
873 *
874 * @param[in] packet where the packet starts
875 * @param[in] buf buffer holding one or more DNS labels
876 * @param[in] buf_len total length of the buffer
877 * @param[in,out] next the DNS label to check, updated to point to the next label
878 * @param[in] lb label tracking data structure
879 * @return
880 * - <=0 on error, offset from buf where the invalid label is located.
881 * - > 0 decoded size of this particular DNS label
882 */
883ssize_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)
884{
885 uint8_t const *p, *q, *end, *label_end;
886 uint8_t const *current, *start;
887 size_t length;
888 bool at_first_label, already_set_next;
889
890 if (!packet || !buf || (buf_len == 0) || !next) {
891 fr_strerror_printf("Invalid argument");
892 return 0;
893 }
894
895 start = *next;
896
897 /*
898 * Don't allow stupidities
899 */
900 if (!((start >= packet) && (start < (buf + buf_len)))) {
901 fr_strerror_printf("Label is not within the buffer");
902 return 0;
903 }
904
905 end = buf + buf_len;
906 p = current = start;
907 length = 0;
908 at_first_label = true;
909 already_set_next = false;
910
911 /*
912 * We silently accept labels *without* a trailing 0x00,
913 * so long as they end at the end of the input buffer.
914 */
915 while (p < end) {
916 /*
917 * End of label byte. Skip it.
918 *
919 * Empty labels are length 1, to account for the
920 * '.'. The caller has to take care of this
921 * manually.
922 */
923 if (*p == 0x00) {
924 p++;
925 if (at_first_label) length++;
926
927 /*
928 * We're still processing the first
929 * label, tell the caller where the next
930 * one is located.
931 */
932 if (current == start) {
933 *next = p;
934 already_set_next = true;
935 }
936
937 break;
938 }
939
940 /*
941 * If there's only one byte in the packet, then
942 * it MUST be 0x00. If it's not, then the label
943 * overflows the buffer.
944 */
945 if ((p + 1) >= end) goto overflow;
946
947 /*
948 * 0b01 and 0b10 are forbidden
949 */
950 if ((*p > 63) && (*p < 0xc0)) {
951 fr_strerror_const("Data with invalid high bits");
952 return -(p - packet);
953 }
954
955 /*
956 * Maybe it's a compressed pointer.
957 */
958 if (*p > 63) {
959 uint16_t offset;
960
961 if ((p + 2) > end) {
962 overflow:
963 fr_strerror_const("Label overflows buffer");
964 return -(p - packet);
965 }
966
967 offset = p[1];
968 offset += ((*p & ~0xc0) << 8);
969
970 /*
971 * Forward references are forbidden,
972 * including self-references.
973 *
974 * This requirement follows RFC 1035
975 * Section 4.1.4, which says:
976 *
977 * ... an entire domain name or a list of
978 * labels at the end of a domain name is
979 * replaced with a pointer to a prior
980 * occurrence of the same name.
981 * ...
982 *
983 * Note the key word PRIOR. If we
984 * enforce that the pointer is backwards,
985 * and do various other enforcements,
986 * then it is very difficult for
987 * attackers to create malicious DNS
988 * packets which will cause the decoder
989 * to do bad things.
990 */
991 if (offset >= (p - packet)) {
992 fr_strerror_printf("Pointer %04x at offset %04x is an invalid forward reference",
993 offset, (unsigned int) (p - packet));
994 return -(p - packet);
995 }
996
997 q = packet + offset;
998
999 /*
1000 * As an additional sanity check, the
1001 * pointer MUST NOT point to something
1002 * within the label we're parsing. If
1003 * that happens, we have a loop.
1004 *
1005 * i.e. the pointer must point backwards
1006 * to *before* our current label. When
1007 * that limitation is enforced, pointer
1008 * loops are impossible.
1009 */
1010 if (q >= current) {
1011 fr_strerror_printf("Pointer %04x at offset %04x creates a loop within a label",
1012 offset, (unsigned int) (p - packet));
1013 return -(p - packet);
1014 }
1015
1016 /*
1017 * If we're tracking which labels are
1018 * valid, then check the pointer, too.
1019 */
1020 if (!dns_pointer_valid(lb, offset)) {
1021 fr_strerror_printf("Pointer %04x at offset %04x does not point to a DNS label",
1022 offset, (unsigned int) (p - packet));
1023 return -(p - packet);
1024 }
1025
1026 /*
1027 * The pointer MUST point to a valid
1028 * length field, and not to another
1029 * pointer.
1030 */
1031 if (*q > 63) {
1032 fr_strerror_printf("Pointer %04x at offset %04x does not point to the start of a label",
1033 offset, (unsigned int) (p - packet));
1034 return -(p - packet);
1035 }
1036
1037 /*
1038 * The pointer MUST NOT point to an end of label field.
1039 */
1040 if (!*q) {
1041 fr_strerror_printf("Pointer %04x at offset %04x refers to an invalid field", offset,
1042 (unsigned int) (p - packet));
1043 return -(p - packet);
1044 }
1045
1046 /*
1047 * If we're jumping away from the label
1048 * we started with, tell the caller where
1049 * the next label is in the network
1050 * buffer.
1051 */
1052 if (current == start) {
1053 *next = p + 2;
1054 already_set_next = true;
1055 }
1056
1057 p = current = q;
1058 continue;
1059 }
1060
1061 /*
1062 * Else it's an uncompressed label
1063 */
1064 if ((p + *p + 1) > end) goto overflow;
1065
1066 /*
1067 * It's a valid label. Mark it as such.
1068 */
1069 dns_label_mark(lb, p);
1070
1071 /*
1072 * Account for the '.' on every label after the
1073 * first one.
1074 */
1075 if (!at_first_label) length++;
1076 at_first_label = false;
1077 length += *p;
1078
1079 /*
1080 * DNS names can be no more than 255 octets.
1081 */
1082 if (length > 255) {
1083 fr_strerror_const("Total length of labels is > 255");
1084 return -(p - packet);
1085 }
1086
1087 q = p + 1;
1088 label_end = q + *p;
1089
1090 /*
1091 * Allow for underscore at the beginning of a
1092 * label.
1093 */
1094 if (*q == '_') q++;
1095
1096 /*
1097 * Verify that the contents of the label are OK.
1098 */
1099 while (q < label_end) {
1100 if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
1101 ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
1102 fr_strerror_printf("Invalid character 0x%02x in label", *q);
1103 return -(q - packet);
1104 }
1105
1106 q++;
1107 }
1108
1109 p += *p + 1;
1110 }
1111
1112 /*
1113 * Return the length of this label.
1114 */
1115 if (!already_set_next) *next = p; /* should be <='end' */
1116
1117 /*
1118 * Add the label, only if we're not using the markup field.
1119 */
1120 if (lb && !lb->mark) (void) dns_label_add(lb, start, *next);
1121
1122 return length;
1123}
1124
1125/** Verify that a network buffer contains valid DNS labels.
1126 *
1127 * @param[in] packet where the packet starts
1128 * @param[in] buf buffer holding one or more DNS labels
1129 * @param[in] buf_len total length of the buffer
1130 * @param[in] start where to start looking
1131 * @param[in] lb label tracking data structure
1132 * @return
1133 * - <=0 on error, where in the buffer the invalid label is located.
1134 * - > 0 total size of the encoded label(s). Will be <= buf_len
1135 */
1136ssize_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)
1137{
1138 ssize_t slen;
1139 uint8_t const *label = start;
1140 uint8_t const *end = buf + buf_len;
1141
1142 while (label < end) {
1143 if (*label == 0x00) {
1144 label++;
1145 break;
1146 }
1147
1148 slen = fr_dns_label_uncompressed_length(packet, buf, buf_len, &label, lb);
1149 if (slen <= 0) return slen; /* already is offset from 'buf' and not 'label' */
1150 }
1151
1152 return label - buf;
1153}
1154
1155static ssize_t dns_label_decode(uint8_t const *packet, uint8_t const *end, uint8_t const **start, uint8_t const **next)
1156{
1157 uint8_t const *p, *q;
1158
1159 p = *start;
1160
1161 if (end == packet) return -1;
1162
1163 if (*p == 0x00) {
1164 *next = p + 1;
1165 return 0;
1166 }
1167
1168 /*
1169 * Pointer, which points somewhere in the packet.
1170 */
1171 if (*p >= 0xc0) {
1172 uint16_t offset;
1173
1174 if ((end - p) < 2) {
1175 return -(p - packet);
1176 }
1177
1178 offset = p[1];
1179 offset += ((*p & ~0xc0) << 8);
1180
1181 q = packet + offset;
1182 if (q >= p) {
1183 return -(p - packet);
1184 }
1185 p = q;
1186 }
1187
1188 /*
1189 * 0b01 and 0b10 are forbidden, and pointers can't point to other pointers.
1190 */
1191 if (*p > 63) return -(p - packet);
1192
1193 if ((p + *p + 1) > end) {
1194 return -(p - packet);
1195 }
1196
1197 /*
1198 * Tell the caller where the actual label is located.
1199 */
1200 *start = p;
1201 *next = p + *p + 1;
1202 return *p;
1203}
1204
1205
1206/** Decode a #fr_value_box_t from one DNS label
1207 *
1208 * The output type is always FR_TYPE_STRING
1209 *
1210 * Note that the caller MUST call fr_dns_labels_network_verify(src, len, start)
1211 * before calling this function. Otherwise bad things will happen.
1212 *
1213 * @param[in] ctx Where to allocate any talloc buffers required.
1214 * @param[out] dst value_box to write the result to.
1215 * @param[in] src Start of the buffer containing DNS labels
1216 * @param[in] len Length of the buffer to decode
1217 * @param[in] label This particular label
1218 * @param[in] tainted Whether the value came from a trusted source.
1219 * @param[in] lb label tracking data structure
1220 * @return
1221 * - >= 0 The number of network bytes consumed.
1222 * - <0 on error.
1223 */
1225 uint8_t const *src, size_t len, uint8_t const *label,
1226 bool tainted, fr_dns_labels_t *lb)
1227{
1228 ssize_t slen;
1229 uint8_t const *after = label;
1230 uint8_t const *current, *next = NULL;
1231 uint8_t const *packet = src;
1232 uint8_t const *end = packet + len;
1233 uint8_t *p;
1234 char *q;
1235
1236 if (!len) return -1;
1237
1238 /*
1239 * The label must be within the current buffer we're
1240 * passed.
1241 */
1242 if ((label < src) || (label >= end)) return -1;
1243
1244 /*
1245 * The actual packet might start earlier than the buffer,
1246 * so reset it if necessary.
1247 */
1248 if (lb) packet = lb->start;
1249
1250 /*
1251 * Get the uncompressed length of the label, and the
1252 * label after this one.
1253 */
1254 slen = fr_dns_label_uncompressed_length(packet, src, len, &after, lb);
1255 if (slen <= 0) {
1256 FR_PROTO_TRACE("dns_label_to_value_box - Failed getting length");
1257 return slen;
1258 }
1259
1261
1262 /*
1263 * An empty label is a 0x00 byte. Just create an empty
1264 * string.
1265 */
1266 if (slen == 1) {
1267 if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, 1, tainted) < 0) return -1;
1268 q[0] = '.';
1269 return after - label;
1270 }
1271
1272 /*
1273 * Allocate the string and set up the value_box
1274 */
1275 if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, slen, tainted) < 0) return -1;
1276
1277 current = label;
1278 p = (uint8_t *) q;
1279 q += slen;
1280
1281 while (current && (current < after) && (*current != 0x00)) {
1282 /*
1283 * Get how many bytes this label has, and where
1284 * we will go to obtain the next label.
1285 */
1286 slen = dns_label_decode(packet, end, &current, &next);
1287 if (slen < 0) return slen;
1288
1289 /*
1290 * As a sanity check, ensure we don't have a
1291 * buffer overflow.
1292 */
1293 if ((p + slen) > (uint8_t *) q) {
1294 FR_PROTO_TRACE("dns_label_to_value_box - length %zd Failed at %d", slen, __LINE__);
1295
1296 fail:
1297 fr_value_box_clear(dst);
1298 return -1;
1299 }
1300
1301 /*
1302 * Add '.' before the label, but only for the
1303 * second and subsequent labels.
1304 */
1305 if (p != (uint8_t const *) dst->vb_strvalue) {
1306 *(p++) = '.';
1307 }
1308
1309 /*
1310 * Copy the raw bytes from the network.
1311 */
1312 memcpy(p, current + 1, slen);
1313
1314 /*
1315 * Go ahead in the output string, and go to the
1316 * next label for decoding.
1317 */
1318 p += slen;
1319 current = next;
1320 }
1321
1322 /*
1323 * As a last sanity check, ensure that we've filled the
1324 * buffer exactly.
1325 */
1326 if (p != (uint8_t *) q) {
1327 FR_PROTO_TRACE("dns_label_to_value_box - Failed at %d", __LINE__);
1328 goto fail;
1329 }
1330
1331 *p = '\0';
1332
1333 /*
1334 * Return the number of network bytes used to parse this
1335 * part of the label.
1336 */
1337 return after - label;
1338}
#define RCSID(id)
Definition build.h:506
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition dbuff.h:1081
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition dbuff.h:751
Test enumeration values.
Definition dict_test.h:92
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_dbuff(fr_dbuff_t *dbuff, 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 in a dbuff.
Definition dns.c:603
#define MAX_OFFSET
Definition dns.c:32
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
static void dns_label_mark(fr_dns_labels_t *lb, uint8_t const *p)
Definition dns.c:98
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
static int dns_label_add(fr_dns_labels_t *lb, uint8_t const *start, uint8_t const *end)
Definition dns.c:34
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 bool dns_label_compress(uint8_t const *packet, uint8_t const *start, uint8_t const *end, uint8_t const **new_search, uint8_t *label, uint8_t **label_end)
Compress "label" by looking at the label recursively.
Definition dns.c:266
static bool dns_pointer_valid(fr_dns_labels_t *lb, uint16_t offset)
Definition dns.c:109
static ssize_t dns_label_decode(uint8_t const *packet, uint8_t const *end, uint8_t const **start, uint8_t const **next)
Definition dns.c:1155
static bool labelcmp(uint8_t const *a, uint8_t const *b, size_t len)
Compare two labels in a case-insensitive fashion.
Definition dns.c:140
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
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
uint8_t * p
long int ssize_t
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
#define FR_PROTO_TRACE(_fmt,...)
Definition proto.h:41
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_value_box_bstr_alloc(TALLOC_CTX *ctx, char **out, fr_value_box_t *dst, fr_dict_attr_t const *enumv, size_t len, bool tainted)
Alloc and assign an empty \0 terminated string to a fr_value_box_t.
Definition value.c:4749
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4362
static fr_slen_t data
Definition value.h:1340
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition value.h:616