The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSID("$Id: c5acb20d5c786f9f491ae5b6a97b24abc2acbee9 $")
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 
34 static 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 
98 static 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  */
140 static 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  */
266 static 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 could not encode anything, an error has occurred.
602  * - <0 the number of bytes the dbuff should have had, instead of "remaining".
603  */
605 {
606  ssize_t slen;
607  size_t need = 0;
608 
609  slen = fr_dns_label_from_value_box(&need, dbuff->p, fr_dbuff_remaining(dbuff), dbuff->p, compression, value, lb);
610  if (slen < 0) return 0;
611 
612  if (slen == 0) return -need;
613 
614  fr_dbuff_advance(dbuff, (size_t)slen);
615  return slen;
616 }
617 
618 /** Encode a single value box of type string, serializing its contents to a dns label
619  *
620  * This functions takes a large buffer and encodes the label in part
621  * of the buffer. This API is necessary in order to allow DNS label
622  * compression.
623  *
624  * @param[out] need if not NULL, how long "buf_len" should be to
625  * serialize the rest of the data.
626  * Note: Only variable length types will be partially
627  * encoded. Fixed length types will not be partially encoded.
628  * @param[out] buf Buffer where labels are stored
629  * @param[in] buf_len The length of the output buffer
630  * @param[out] where Where to write this label
631  * @param[in] compression Whether or not to do DNS label compression.
632  * @param[in] value to encode.
633  * @param[in] lb label tracking data structure
634  * @return
635  * - 0 no bytes were written, see need value to determine
636  * - >0 the number of bytes written to "where", NOT "buf + where + outlen"
637  * - <0 on error.
638  */
639 ssize_t fr_dns_label_from_value_box(size_t *need, uint8_t *buf, size_t buf_len, uint8_t *where, bool compression,
641 {
642  uint8_t *label;
643  uint8_t const *end = buf + buf_len;
644  uint8_t const *q, *strend, *last;
645  uint8_t *data;
646  bool underscore = true;
647 
648  if (!buf || !buf_len || !where || !value) {
649  fr_strerror_const("Invalid input");
650  return -1;
651  }
652 
653  /*
654  * Don't allow stupidities
655  */
656  if (!((where >= buf) && (where < (buf + buf_len)))) {
657  fr_strerror_const("Label to write is outside of buffer");
658  return -1;
659  }
660 
661  /*
662  * We can only encode strings.
663  */
664  if (value->type != FR_TYPE_STRING) {
665  fr_strerror_const("Asked to encode non-string type");
666  return -1;
667  }
668 
669  /*
670  * '.' or empty string is special, and is encoded as a
671  * plain zero byte.
672  *
673  * Since "where < end", we can always write 1 byte to it.
674  */
675  if ((value->vb_length == 0) ||
676  ((value->vb_length == 1) && (value->vb_strvalue[0] == '.'))) {
677  *where = 0x00;
678  return 1;
679  }
680 
681  /*
682  * Sanity check the name before writing anything to the
683  * buffer.
684  *
685  * Only encode [-0-9a-zA-Z]. Anything else is forbidden.
686  * Dots at the start are forbidden. Double dots are
687  * forbidden.
688  */
689  q = (uint8_t const *) value->vb_strvalue;
690  strend = q + value->vb_length;
691  last = q;
692 
693  if (*q == '.') {
694  fr_strerror_const("Empty labels are invalid");
695  return -1;
696  }
697 
698  /*
699  * Convert it piece by piece.
700  */
701  while (q < strend) {
702  /*
703  * Allow underscore at the start of a label.
704  */
705  if (underscore) {
706  underscore = false;
707 
708  if (*q == '_') goto next;
709  }
710 
711  if (*q == '.') {
712  /*
713  * Don't count final dot as an
714  * intermediate dot, and don't bother
715  * encoding it.
716  */
717  if ((q + 1) == strend) {
718  strend--;
719  break;
720  }
721 
722  if (q[1] == '.') {
723  fr_strerror_const("Double dots '..' are forbidden");
724  return -1;
725  }
726  last = q;
727 
728  /*
729  * We had a dot, allow underscore as the
730  * first character of the next label.
731  */
732  underscore = true;
733 
734  } else if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
735  ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
736  fr_strerror_printf("Invalid character 0x%02x in label", *q);
737  return -1;
738  }
739 
740  next:
741  q++;
742 
743  if ((q - last) > 63) {
744  fr_strerror_const("Label is larger than 63 characters");
745  return -1;
746  }
747  }
748 
749  q = (uint8_t const *) value->vb_strvalue;
750 
751  /*
752  * For now, just encode the value as-is. We do
753  * compression as a second step.
754  *
755  * We need a minimum length of string + beginning length
756  * + trailing zero. Intermediate '.' are converted to
757  * length bytes.
758  */
759  if ((where + (strend - q) + 2) > end) {
760  if (need) *need = (where + (strend - q) + 2) - buf;
761  return 0;
762  }
763 
764  label = where;
765  *label = 0;
766  data = label + 1;
767 
768  /*
769  * @todo - encode into a local buffer, and then try to
770  * compress that into the output buffer. This means that
771  * the output buffer can be a little bit smaller.
772  */
773  while (q < strend) {
774  fr_assert(data < end);
775  fr_assert((data - where) < 255);
776 
777  /*
778  * '.' is a label delimiter.
779  *
780  * We've already checked above for '.' at the
781  * start, for double dots, and have already
782  * suppressed '.' at the end of the string.
783  *
784  * Start a new label.
785  */
786  if (*q == '.') {
787  label = data;
788  *label = 0;
789  data = label + 1;
790 
791  q++;
792  continue;
793  }
794 
795  *(data++) = *(q++);
796  (*label)++;
797  fr_assert(*label <= 63);
798  }
799 
800  *(data++) = 0; /* end of label */
801 
802  /*
803  * If we're compressing it, and we have data to compress,
804  * then do it.
805  */
806  if (compression && ((data - where) > 2)) {
807  if (lb) {
808  int i;
809 
810  /*
811  * Loop over the parts of the packet which have DNS labels.
812  *
813  * Note that the dns_label_compress() function does NOT follow pointers in the
814  * start/end block which it's searching! It just tries to compress the *input*,
815  * and assumes that the input is compressed last label to first label.
816  *
817  * In addition, dns_label_compress() tracks where in the block it started
818  * searching. So it only scans the block once, even if we pass a NULL search
819  * parameter to it.
820  *
821  * We could start compression from the *last* block. When we add
822  * "www.example.com" and then "ftp.example.com", we could point "ftp" to the
823  * "example.com" portion. which is already in the packet. However, doing that
824  * would require that dns_label_compress() follows pointers in the block it's
825  * searching. Which would greatly increase the complexity of the algorithm.
826  *
827  *
828  * We could still optimize this algorithm a bit, by tracking which parts of the
829  * buffer have DNS names of label length 1, 2, etc. Doing that would mean more
830  * complex data structures, but fewer passes over the packet.
831  */
832  for (i = 0; i < lb->num; i++) {
833  bool compressed;
834 
835  FR_PROTO_TRACE("Trying to compress %s in block %d of %u..%u",
836  value->vb_strvalue, i,
837  lb->blocks[i].start, lb->blocks[i].end);
838 
839  compressed = dns_label_compress(lb->start, lb->start + lb->blocks[i].start,
840  lb->start + lb->blocks[i].end,
841  NULL, where, &data);
842  if (compressed) {
843  FR_PROTO_TRACE("Compressed label in block %d", i);
844  if (*(where + *where + 1) >= 0xc0) {
845  FR_PROTO_TRACE("Next label is compressed, stopping");
846  }
847  }
848  }
849 
850  dns_label_add(lb, where, data);
851 
852  } else if (buf != where) {
853  if (dns_label_compress(buf, buf, where, NULL, where, &data)) {
854  FR_PROTO_TRACE("Compressed single label %s to %zu bytes",
855  value->vb_strvalue, data - where);
856  } else {
857  FR_PROTO_TRACE("Did not compress single label");
858  }
859  }
860  } else {
861  FR_PROTO_TRACE("Not compressing label");
862  }
863 
864  fr_assert(data > where);
865  return data - where;
866 }
867 
868 /** Get the *uncompressed* length of a DNS label in a network buffer.
869  *
870  * i.e. how bytes are required to store the uncompressed version of
871  * the label.
872  *
873  * Note that a bare 0x00 byte has length 1, to account for '.'
874  *
875  * @param[in] packet where the packet starts
876  * @param[in] buf buffer holding one or more DNS labels
877  * @param[in] buf_len total length of the buffer
878  * @param[in,out] next the DNS label to check, updated to point to the next label
879  * @param[in] lb label tracking data structure
880  * @return
881  * - <=0 on error, offset from buf where the invalid label is located.
882  * - > 0 decoded size of this particular DNS label
883  */
884 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)
885 {
886  uint8_t const *p, *q, *end, *label_end;
887  uint8_t const *current, *start;
888  size_t length;
889  bool at_first_label, already_set_next;
890 
891  if (!packet || !buf || (buf_len == 0) || !next) {
892  fr_strerror_printf("Invalid argument");
893  return 0;
894  }
895 
896  start = *next;
897 
898  /*
899  * Don't allow stupidities
900  */
901  if (!((start >= packet) && (start < (buf + buf_len)))) {
902  fr_strerror_printf("Label is not within the buffer");
903  return 0;
904  }
905 
906  end = buf + buf_len;
907  p = current = start;
908  length = 0;
909  at_first_label = true;
910  already_set_next = false;
911 
912  /*
913  * We silently accept labels *without* a trailing 0x00,
914  * so long as they end at the end of the input buffer.
915  */
916  while (p < end) {
917  /*
918  * End of label byte. Skip it.
919  *
920  * Empty labels are length 1, to account for the
921  * '.'. The caller has to take care of this
922  * manually.
923  */
924  if (*p == 0x00) {
925  p++;
926  if (at_first_label) length++;
927 
928  /*
929  * We're still processing the first
930  * label, tell the caller where the next
931  * one is located.
932  */
933  if (current == start) {
934  *next = p;
935  already_set_next = true;
936  }
937 
938  break;
939  }
940 
941  /*
942  * If there's only one byte in the packet, then
943  * it MUST be 0x00. If it's not, then the label
944  * overflows the buffer.
945  */
946  if ((p + 1) >= end) goto overflow;
947 
948  /*
949  * 0b10 and 0b10 are forbidden
950  */
951  if ((*p > 63) && (*p < 0xc0)) {
952  fr_strerror_const("Data with invalid high bits");
953  return -(p - packet);
954  }
955 
956  /*
957  * Maybe it's a compressed pointer.
958  */
959  if (*p > 63) {
960  uint16_t offset;
961 
962  if ((p + 2) > end) {
963  overflow:
964  fr_strerror_const("Label overflows buffer");
965  return -(p - packet);
966  }
967 
968  offset = p[1];
969  offset += ((*p & ~0xc0) << 8);
970 
971  /*
972  * Forward references are forbidden,
973  * including self-references.
974  *
975  * This requirement follows RFC 1035
976  * Section 4.1.4, which says:
977  *
978  * ... an entire domain name or a list of
979  * labels at the end of a domain name is
980  * replaced with a pointer to a prior
981  * occurrence of the same name.
982  * ...
983  *
984  * Note the key word PRIOR. If we
985  * enforce that the pointer is backwards,
986  * and do various other enforcements,
987  * then it is very difficult for
988  * attackers to create malicious DNS
989  * packets which will cause the decoder
990  * to do bad things.
991  */
992  if (offset >= (p - packet)) {
993  fr_strerror_printf("Pointer %04x at offset %04x is an invalid forward reference",
994  offset, (int) (p - packet));
995  return -(p - packet);
996  }
997 
998  q = packet + offset;
999 
1000  /*
1001  * As an additional sanity check, the
1002  * pointer MUST NOT point to something
1003  * within the label we're parsing. If
1004  * that happens, we have a loop.
1005  *
1006  * i.e. the pointer must point backwards
1007  * to *before* our current label. When
1008  * that limitation is enforced, pointer
1009  * loops are impossible.
1010  */
1011  if (q >= current) {
1012  fr_strerror_printf("Pointer %04x at offset %04x creates a loop within a label",
1013  offset, (int) (p - packet));
1014  return -(p - packet);
1015  }
1016 
1017  /*
1018  * If we're tracking which labels are
1019  * valid, then check the pointer, too.
1020  */
1021  if (!dns_pointer_valid(lb, offset)) {
1022  fr_strerror_printf("Pointer %04x at offset %04x does not point to a DNS label",
1023  offset, (int) (p - packet));
1024  return -(p - packet);
1025  }
1026 
1027  /*
1028  * The pointer MUST point to a valid
1029  * length field, and not to another
1030  * pointer.
1031  */
1032  if (*q > 63) {
1033  fr_strerror_printf("Pointer %04x at offset %04x does not point to the start of a label",
1034  offset, (int) (p - packet));
1035  return -(p - packet);
1036  }
1037 
1038  /*
1039  * The pointer MUST NOT point to an end of label field.
1040  */
1041  if (!*q) {
1042  fr_strerror_printf("Pointer %04x at offset %04x refers to an invalid field", offset,
1043  (int) (p - packet));
1044  return -(p - packet);
1045  }
1046 
1047  /*
1048  * If we're jumping away from the label
1049  * we started with, tell the caller where
1050  * the next label is in the network
1051  * buffer.
1052  */
1053  if (current == start) {
1054  *next = p + 2;
1055  already_set_next = true;
1056  }
1057 
1058  p = current = q;
1059  continue;
1060  }
1061 
1062  /*
1063  * Else it's an uncompressed label
1064  */
1065  if ((p + *p + 1) > end) goto overflow;
1066 
1067  /*
1068  * It's a valid label. Mark it as such.
1069  */
1070  dns_label_mark(lb, p);
1071 
1072  /*
1073  * Account for the '.' on every label after the
1074  * first one.
1075  */
1076  if (!at_first_label) length++;
1077  at_first_label = false;
1078  length += *p;
1079 
1080  /*
1081  * DNS names can be no more than 255 octets.
1082  */
1083  if (length > 255) {
1084  fr_strerror_const("Total length of labels is > 255");
1085  return -(p - packet);
1086  }
1087 
1088  q = p + 1;
1089  label_end = q + *p;
1090 
1091  /*
1092  * Allow for underscore at the beginning of a
1093  * label.
1094  */
1095  if (*q == '_') q++;
1096 
1097  /*
1098  * Verify that the contents of the label are OK.
1099  */
1100  while (q < label_end) {
1101  if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
1102  ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
1103  fr_strerror_printf("Invalid character 0x%02x in label", *q);
1104  return -(q - packet);
1105  }
1106 
1107  q++;
1108  }
1109 
1110  p += *p + 1;
1111  }
1112 
1113  /*
1114  * Return the length of this label.
1115  */
1116  if (!already_set_next) *next = p; /* should be <='end' */
1117 
1118  /*
1119  * Add the label, only if we're not using the markup field.
1120  */
1121  if (lb && !lb->mark) (void) dns_label_add(lb, start, *next);
1122 
1123  return length;
1124 }
1125 
1126 /** Verify that a network buffer contains valid DNS labels.
1127  *
1128  * @param[in] packet where the packet starts
1129  * @param[in] buf buffer holding one or more DNS labels
1130  * @param[in] buf_len total length of the buffer
1131  * @param[in] start where to start looking
1132  * @param[in] lb label tracking data structure
1133  * @return
1134  * - <=0 on error, where in the buffer the invalid label is located.
1135  * - > 0 total size of the encoded label(s). Will be <= buf_len
1136  */
1137 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)
1138 {
1139  ssize_t slen;
1140  uint8_t const *label = start;
1141  uint8_t const *end = buf + buf_len;
1142 
1143  while (label < end) {
1144  if (*label == 0x00) {
1145  label++;
1146  break;
1147  }
1148 
1149  slen = fr_dns_label_uncompressed_length(packet, buf, buf_len, &label, lb);
1150  if (slen <= 0) return slen; /* already is offset from 'buf' and not 'label' */
1151  }
1152 
1153  return label - buf;
1154 }
1155 
1156 static ssize_t dns_label_decode(uint8_t const *packet, uint8_t const *end, uint8_t const **start, uint8_t const **next)
1157 {
1158  uint8_t const *p, *q;
1159 
1160  p = *start;
1161 
1162  if (end == packet) return -1;
1163 
1164  if (*p == 0x00) {
1165  *next = p + 1;
1166  return 0;
1167  }
1168 
1169  /*
1170  * Pointer, which points somewhere in the packet.
1171  */
1172  if (*p >= 0xc0) {
1173  uint16_t offset;
1174 
1175  if ((end - packet) < 2) {
1176  return -(p - packet);
1177  }
1178 
1179  offset = p[1];
1180  offset += ((*p & ~0xc0) << 8);
1181 
1182  q = packet + offset;
1183  if (q >= p) {
1184  return -(p - packet);
1185  }
1186  p = q;
1187  }
1188 
1189  /*
1190  * 0b10 and 0b10 are forbidden, and pointers can't point to other pointers.
1191  */
1192  if (*p > 63) return -(p - packet);
1193 
1194  if ((p + *p + 1) > end) {
1195  return -(p - packet);
1196  }
1197 
1198  /*
1199  * Tell the caller where the actual label is located.
1200  */
1201  *start = p;
1202  *next = p + *p + 1;
1203  return *p;
1204 }
1205 
1206 
1207 /** Decode a #fr_value_box_t from one DNS label
1208  *
1209  * The output type is always FR_TYPE_STRING
1210  *
1211  * Note that the caller MUST call fr_dns_labels_network_verify(src, len, start)
1212  * before calling this function. Otherwise bad things will happen.
1213  *
1214  * @param[in] ctx Where to allocate any talloc buffers required.
1215  * @param[out] dst value_box to write the result to.
1216  * @param[in] src Start of the buffer containing DNS labels
1217  * @param[in] len Length of the buffer to decode
1218  * @param[in] label This particular label
1219  * @param[in] tainted Whether the value came from a trusted source.
1220  * @param[in] lb label tracking data structure
1221  * @return
1222  * - >= 0 The number of network bytes consumed.
1223  * - <0 on error.
1224  */
1226  uint8_t const *src, size_t len, uint8_t const *label,
1227  bool tainted, fr_dns_labels_t *lb)
1228 {
1229  ssize_t slen;
1230  uint8_t const *after = label;
1231  uint8_t const *current, *next = NULL;
1232  uint8_t const *packet = src;
1233  uint8_t const *end = packet + len;
1234  uint8_t *p;
1235  char *q;
1236 
1237  if (!len) return -1;
1238 
1239  /*
1240  * The label must be within the current buffer we're
1241  * passed.
1242  */
1243  if ((label < src) || (label >= end)) return -1;
1244 
1245  /*
1246  * The actual packet might start earlier than the buffer,
1247  * so reset it if necessary.
1248  */
1249  if (lb) packet = lb->start;
1250 
1251  /*
1252  * Get the uncompressed length of the label, and the
1253  * label after this one.
1254  */
1255  slen = fr_dns_label_uncompressed_length(packet, src, len, &after, lb);
1256  if (slen <= 0) {
1257  FR_PROTO_TRACE("dns_label_to_value_box - Failed getting length");
1258  return slen;
1259  }
1260 
1262 
1263  /*
1264  * An empty label is a 0x00 byte. Just create an empty
1265  * string.
1266  */
1267  if (slen == 1) {
1268  if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, 1, tainted) < 0) return -1;
1269  q[0] = '.';
1270  return after - label;
1271  }
1272 
1273  /*
1274  * Allocate the string and set up the value_box
1275  */
1276  if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, slen, tainted) < 0) return -1;
1277 
1278  current = label;
1279  p = (uint8_t *) q;
1280  q += slen;
1281 
1282  while (current && (current < after) && (*current != 0x00)) {
1283  /*
1284  * Get how many bytes this label has, and where
1285  * we will go to obtain the next label.
1286  */
1287  slen = dns_label_decode(packet, end, &current, &next);
1288  if (slen < 0) return slen;
1289 
1290  /*
1291  * As a sanity check, ensure we don't have a
1292  * buffer overflow.
1293  */
1294  if ((p + slen) > (uint8_t *) q) {
1295  FR_PROTO_TRACE("dns_label_to_value_box - length %zd Failed at %d", slen, __LINE__);
1296 
1297  fail:
1298  fr_value_box_clear(dst);
1299  return -1;
1300  }
1301 
1302  /*
1303  * Add '.' before the label, but only for the
1304  * second and subsequent labels.
1305  */
1306  if (p != (uint8_t const *) dst->vb_strvalue) {
1307  *(p++) = '.';
1308  }
1309 
1310  /*
1311  * Copy the raw bytes from the network.
1312  */
1313  memcpy(p, current + 1, slen);
1314 
1315  /*
1316  * Go ahead in the output string, and go to the
1317  * next label for decoding.
1318  */
1319  p += slen;
1320  current = next;
1321  }
1322 
1323  /*
1324  * As a last sanity check, ensure that we've filled the
1325  * buffer exactly.
1326  */
1327  if (p != (uint8_t *) q) {
1328  FR_PROTO_TRACE("dns_label_to_value_box - Failed at %d", __LINE__);
1329  goto fail;
1330  }
1331 
1332  *p = '\0';
1333 
1334  /*
1335  * Return the number of network bytes used to parse this
1336  * part of the label.
1337  */
1338  return after - label;
1339 }
#define RCSID(id)
Definition: build.h:444
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition: dbuff.h:1067
#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:738
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:884
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:604
#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:639
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:1137
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:1225
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:1156
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 max
Definition: dns.h:40
int num
number of used labels
Definition: dns.h:39
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
uint8_t * p
Definition: merged_model.c:42
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static rc_request_t * current
Definition: radclient-ng.c:97
if(!subtype_vp) goto fail
fr_assert(0)
#define FR_PROTO_TRACE(_fmt,...)
Definition: proto.h:40
#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:4020
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition: value.c:3672
static fr_slen_t data
Definition: value.h:1259
#define fr_value_box_init_null(_vb)
Initialise an empty/null box that will be filled later.
Definition: value.h:580