Coverage Report

Created: 2026-01-17 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/dns.c
Line
Count
Source
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: 8138fc337defc4e396d92e52302e38bc0325225e $")
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
0
#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
0
{
36
0
  size_t offset, size = end - start;
37
0
  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
0
  if (!lb) return 0;
44
45
0
  fr_assert(start >= lb->start);
46
0
  fr_assert(end >= start);
47
48
0
  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
0
  if ((offset + size) >= MAX_OFFSET) return 0;
56
57
  /*
58
   *  We're not tracking labels, so don't do anything.
59
   */
60
0
  if (lb->max == 1) return 0;
61
62
0
  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
0
  block = &lb->blocks[lb->num - 1];
69
0
  fr_assert(block->start <= offset);
70
0
  fr_assert(offset);
71
72
0
  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
0
  if (block->end == offset) {
78
0
    block->end += size;
79
0
    FR_PROTO_TRACE("Expanding last block (%d) to %u..%u", lb->num - 1, block->start, block->end);
80
0
    return 0;
81
0
  }
82
83
  /*
84
   *  It's full, die.
85
   */
86
0
  if (lb->num == lb->max) return -1;
87
88
0
  lb->num++;
89
0
  block++;
90
91
0
  block->start = offset;
92
0
  block->end = offset + size;
93
0
  FR_PROTO_TRACE("Appending block (%d) to %u..%u", lb->num - 1, block->start, block->end);
94
95
0
  return 0;
96
0
}
97
98
static void dns_label_mark(fr_dns_labels_t *lb, uint8_t const *p)
99
42.0k
{
100
42.0k
  if (!lb || !lb->mark) return;
101
102
22.3k
  fr_assert(p >= (lb->start + 12)); /* can't point to the packet header */
103
22.3k
  fr_assert(!lb->end || (p < lb->end));
104
105
22.3k
  lb->mark[p - lb->start] = 1;
106
22.3k
}
107
108
109
static bool dns_pointer_valid(fr_dns_labels_t *lb, uint16_t offset)
110
8.21k
{
111
8.21k
  int i;
112
113
8.21k
  if (!lb) return true;  /* we have no idea, so allow it */
114
115
3.50k
  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
0
  for (i = 0; i < lb->num; i++) {
123
0
    FR_PROTO_TRACE("Checking block %d %u..%u against %u",
124
0
             i, lb->blocks[i].start, lb->blocks[i].end, offset);
125
126
0
    if (offset < lb->blocks[i].start) return false;
127
128
0
    if (offset < lb->blocks[i].end) return true;
129
0
  }
130
131
0
  return false;
132
0
}
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
0
{
142
0
  for (/* nothing */; len > 0; len--) {
143
0
    if (*a == *b) {
144
0
      a++;
145
0
      b++;
146
0
      continue;
147
0
    }
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
0
    if (!(((*a >= 'a') && (*a <= 'z')) || ((*a >= 'A') && (*a <= 'Z')))) return false;
155
0
    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
0
    if (((*a)^(*b)) != 0x20) {
162
0
      return false;
163
0
    }
164
165
0
    a++;
166
0
    b++;
167
0
  }
168
169
0
  return true;
170
0
}
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
0
{
269
0
  uint8_t *next;
270
0
  uint8_t const *q, *ptr, *suffix, *search;
271
0
  uint16_t offset;
272
0
  bool compressed = false;
273
274
  /*
275
   *  Don't compress "end of label" byte or pointers.
276
   */
277
0
  if (!*label || (*label > 63)) {
278
0
    return false;
279
0
  }
280
281
  /*
282
   *  Check the next label.  Note that this is *after*
283
   *  "end".  It also MUST be a valid, uncompressed label.
284
   */
285
0
  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
0
  if (!new_search) {
302
0
    search = start;
303
0
  } else {
304
0
    search = *new_search;
305
0
  }
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
0
  if (*next == 0x00) {
334
0
    q = search;
335
0
    while (q < end) {
336
0
      if (*q == 0x00) {
337
0
        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
0
        search = q;
346
0
        continue;
347
0
      }
348
349
      /*
350
       *  Our label is a terminal one.  Which
351
       *  can't point to a pointer.
352
       */
353
0
      if (*q > 63) {
354
0
        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
0
        search = q;
364
0
        continue;
365
0
      }
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
0
      ptr = q + *q + 1;
375
0
      if (ptr > end) return false;
376
377
      /*
378
       *  Label lengths aren't the same, skip
379
       *  it.
380
       */
381
0
      if (*q != *label) {
382
0
        q = ptr;
383
0
        continue;
384
0
      }
385
386
      /*
387
       *  Our input label ends with 0x00.  If
388
       *  this label doesn't end with 0x00, skip
389
       *  it.
390
       */
391
0
      if (*ptr != 0x00) {
392
0
        q = ptr;
393
0
        continue;
394
0
      }
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
0
      if ((q - packet) > (1 << 14)) return false;
402
403
      /*
404
       *  Only now do case-insensitive
405
       *  comparisons.
406
       */
407
0
      if (!labelcmp(q + 1, label + 1, *label)) {
408
0
        q = ptr;
409
0
        continue;
410
0
      }
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
0
      offset = (q - packet);
422
0
      label[0] = (offset >> 8) | 0xc0;
423
0
      label[1] = offset & 0xff;
424
0
      *label_end = label + 2;
425
0
      if (new_search) *new_search = search;
426
0
      return true;
427
0
    }
428
429
0
    return false;
430
0
  }
431
432
  /*
433
   *  The next label is still uncompressed, so we call
434
   *  ourselves recursively in order to compress it.
435
   */
436
0
  if (*next < 63) {
437
0
    if (!dns_label_compress(packet, start, end, &search, next, label_end)) return false;
438
439
    /*
440
     *  Else it WAS compressed.
441
     */
442
0
    compressed = true;
443
0
  }
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
0
  if (*next < 0xc0) {
451
0
    return compressed;
452
0
  }
453
454
  /*
455
   *  Remember where our suffix points to.
456
   */
457
0
  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
0
  q = search;
474
0
  while (q < end) {
475
0
    if (*q == 0x00) {
476
0
      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
0
      search = q;
484
0
      continue;
485
0
    }
486
487
    /*
488
     *  Skip compressed pointers.  We can't point to
489
     *  compressed pointers.
490
     */
491
0
    if (*q > 63) {
492
0
      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
0
      search = q;
501
0
      continue;
502
0
    }
503
504
    /*
505
     *  We now have an uncompressed label in the input
506
     *  buffer.  Check for a match.
507
     */
508
0
    ptr = q + *q + 1;
509
0
    if (ptr > end) return compressed;
510
511
    /*
512
     *  Label lengths aren't the same, skip it.
513
     */
514
0
    if (*q != *label) {
515
0
      q = ptr;
516
0
      continue;
517
0
    }
518
519
    /*
520
     *  If the NEXT label is uncompressed, then skip
521
     *  it unless it's the suffix we're pointing to.
522
     */
523
0
    if (*ptr < 63) {
524
0
      if (ptr != suffix) {
525
0
        q = ptr;
526
0
        continue;
527
0
      }
528
529
0
      goto check_label;
530
0
    }
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
0
    if ((ptr[0] != next[0]) ||
539
0
        (ptr[1] != next[1])) {
540
0
      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
0
      search = q;
549
0
      continue;
550
0
    }
551
552
0
  check_label:
553
    /*
554
     *  Pointer is too far away.  Don't point
555
     *  to it.
556
     */
557
0
    if ((q - packet) > (1 << 14)) return compressed;
558
559
    /*
560
     *  Only now do case-insensitive
561
     *  comparisons.
562
     */
563
0
    if (!labelcmp(q + 1, label + 1, *label)) {
564
0
      q = ptr;
565
0
      continue;
566
0
    }
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
0
    offset = (q - packet);
578
0
    label[0] = (offset >> 8) | 0xc0;
579
0
    label[1] = offset & 0xff;
580
0
    *label_end = label + 2;
581
0
    if (new_search) *new_search = search;
582
0
    return true;
583
0
  }
584
585
  /*
586
   *  Who knows what it is, we couldn't compress it.
587
   */
588
0
  return compressed;
589
0
}
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
 */
604
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)
605
0
{
606
0
  ssize_t     slen;
607
0
  size_t      need = 0;
608
609
0
  slen = fr_dns_label_from_value_box(&need, dbuff->p, fr_dbuff_remaining(dbuff), dbuff->p, compression, value, lb);
610
0
  if (slen < 0) return 0;
611
612
0
  if (slen == 0) return -need;
613
614
0
  fr_dbuff_advance(dbuff, (size_t)slen);
615
0
  return slen;
616
0
}
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,
640
            fr_value_box_t const *value, fr_dns_labels_t *lb)
641
0
{
642
0
  uint8_t *label;
643
0
  uint8_t const *end = buf + buf_len;
644
0
  uint8_t const *q, *strend, *last;
645
0
  uint8_t *data;
646
0
  bool underscore = true;
647
648
0
  if (!buf || !buf_len || !where || !value) {
649
0
    fr_strerror_const("Invalid input");
650
0
    return -1;
651
0
  }
652
653
  /*
654
   *  Don't allow stupidities
655
   */
656
0
  if (!((where >= buf) && (where < (buf + buf_len)))) {
657
0
    fr_strerror_const("Label to write is outside of buffer");
658
0
    return -1;
659
0
  }
660
661
  /*
662
   *  We can only encode strings.
663
   */
664
0
  if (value->type != FR_TYPE_STRING) {
665
0
    fr_strerror_const("Asked to encode non-string type");
666
0
    return -1;
667
0
  }
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
0
  if ((value->vb_length == 0) ||
676
0
      ((value->vb_length == 1) && (value->vb_strvalue[0] == '.'))) {
677
0
    *where = 0x00;
678
0
    return 1;
679
0
  }
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
0
  q = (uint8_t const *) value->vb_strvalue;
690
0
  strend = q + value->vb_length;
691
0
  last = q;
692
693
0
  if (*q == '.') {
694
0
    fr_strerror_const("Empty labels are invalid");
695
0
    return -1;
696
0
  }
697
698
  /*
699
   *  Convert it piece by piece.
700
   */
701
0
  while (q < strend) {
702
    /*
703
     *  Allow underscore at the start of a label.
704
     */
705
0
    if (underscore) {
706
0
      underscore = false;
707
708
0
      if (*q == '_') goto next;
709
0
    }
710
711
0
    if (*q == '.') {
712
      /*
713
       *  Don't count final dot as an
714
       *  intermediate dot, and don't bother
715
       *  encoding it.
716
       */
717
0
      if ((q + 1) == strend) {
718
0
        strend--;
719
0
        break;
720
0
      }
721
722
0
      if (q[1] == '.') {
723
0
        fr_strerror_const("Double dots '..' are forbidden");
724
0
        return -1;
725
0
      }
726
0
      last = q;
727
728
      /*
729
       *  We had a dot, allow underscore as the
730
       *  first character of the next label.
731
       */
732
0
      underscore = true;
733
734
0
    } else if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
735
0
           ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
736
0
      fr_strerror_printf("Invalid character 0x%02x in label", *q);
737
0
      return -1;
738
0
    }
739
740
0
  next:
741
0
    q++;
742
743
0
    if ((q - last) > 63) {
744
0
      fr_strerror_const("Label is larger than 63 characters");
745
0
      return -1;
746
0
    }
747
0
  }
748
749
0
  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
0
  if ((where + (strend - q) + 2) > end) {
760
0
    if (need) *need = (where + (strend - q) + 2) - buf;
761
0
    return 0;
762
0
  }
763
764
0
  label = where;
765
0
  *label = 0;
766
0
  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
0
  while (q < strend) {
774
0
    fr_assert(data < end);
775
0
    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
0
    if (*q == '.') {
787
0
      label = data;
788
0
      *label = 0;
789
0
      data = label + 1;
790
791
0
      q++;
792
0
      continue;
793
0
    }
794
795
0
    *(data++) = *(q++);
796
0
    (*label)++;
797
0
    fr_assert(*label <= 63);
798
0
  }
799
800
0
  *(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
0
  if (compression && ((data - where) > 2)) {
807
0
    if (lb) {
808
0
      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
0
      for (i = 0; i < lb->num; i++) {
833
0
        bool compressed;
834
835
0
        FR_PROTO_TRACE("Trying to compress %s in block %d of %u..%u",
836
0
                 value->vb_strvalue, i,
837
0
                 lb->blocks[i].start, lb->blocks[i].end);
838
839
0
        compressed = dns_label_compress(lb->start, lb->start + lb->blocks[i].start,
840
0
                lb->start + lb->blocks[i].end,
841
0
                NULL, where, &data);
842
0
        if (compressed) {
843
0
          FR_PROTO_TRACE("Compressed label in block %d", i);
844
0
          if (*(where + *where + 1) >= 0xc0) {
845
0
            FR_PROTO_TRACE("Next label is compressed, stopping");
846
0
          }
847
0
        }
848
0
      }
849
850
0
      dns_label_add(lb, where, data);
851
852
0
    } else if (buf != where) {
853
0
      if (dns_label_compress(buf, buf, where, NULL, where, &data)) {
854
0
        FR_PROTO_TRACE("Compressed single label %s to %zu bytes",
855
0
                 value->vb_strvalue, (size_t) (data - where));
856
0
      } else {
857
0
        FR_PROTO_TRACE("Did not compress single label");
858
0
      }
859
0
    }
860
0
  } else {
861
0
    FR_PROTO_TRACE("Not compressing label");
862
0
  }
863
864
0
  fr_assert(data > where);
865
0
  return data - where;
866
0
}
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
77.2k
{
886
77.2k
  uint8_t const *p, *q, *end, *label_end;
887
77.2k
  uint8_t const *current, *start;
888
77.2k
  size_t length;
889
77.2k
  bool at_first_label, already_set_next;
890
891
77.2k
  if (!packet || !buf || (buf_len == 0) || !next) {
892
1.31k
    fr_strerror_printf("Invalid argument");
893
1.31k
    return 0;
894
1.31k
  }
895
896
75.9k
  start = *next;
897
898
  /*
899
   *  Don't allow stupidities
900
   */
901
75.9k
  if (!((start >= packet) && (start < (buf + buf_len)))) {
902
0
    fr_strerror_printf("Label is not within the buffer");
903
0
    return 0;
904
0
  }
905
906
75.9k
  end = buf + buf_len;
907
75.9k
  p = current = start;
908
75.9k
  length = 0;
909
75.9k
  at_first_label = true;
910
75.9k
  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
123k
  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
122k
    if (*p == 0x00) {
925
66.6k
      p++;
926
66.6k
      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
66.6k
      if (current == start) {
934
62.5k
        *next = p;
935
62.5k
        already_set_next = true;
936
62.5k
      }
937
938
66.6k
      break;
939
66.6k
    }
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
55.4k
    if ((p + 1) >= end) goto overflow;
947
948
    /*
949
     *  0b10 and 0b10 are forbidden
950
     */
951
53.3k
    if ((*p > 63) && (*p < 0xc0)) {
952
814
      fr_strerror_const("Data with invalid high bits");
953
814
      return -(p - packet);
954
814
    }
955
956
    /*
957
     *  Maybe it's a compressed pointer.
958
     */
959
52.5k
    if (*p > 63) {
960
9.62k
      uint16_t offset;
961
962
9.62k
      if ((p + 2) > end) {
963
2.93k
      overflow:
964
2.93k
        fr_strerror_const("Label overflows buffer");
965
2.93k
        return -(p - packet);
966
0
      }
967
968
9.62k
      offset = p[1];
969
9.62k
      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
9.62k
      if (offset >= (p - packet)) {
993
1.05k
        fr_strerror_printf("Pointer %04x at offset %04x is an invalid forward reference",
994
1.05k
               offset, (unsigned int) (p - packet));
995
1.05k
        return -(p - packet);
996
1.05k
      }
997
998
8.56k
      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
8.56k
      if (q >= current) {
1012
357
        fr_strerror_printf("Pointer %04x at offset %04x creates a loop within a label",
1013
357
               offset, (unsigned int) (p - packet));
1014
357
        return -(p - packet);
1015
357
      }
1016
1017
      /*
1018
       *  If we're tracking which labels are
1019
       *  valid, then check the pointer, too.
1020
       */
1021
8.21k
      if (!dns_pointer_valid(lb, offset)) {
1022
443
        fr_strerror_printf("Pointer %04x at offset %04x does not point to a DNS label",
1023
443
               offset, (unsigned int) (p - packet));
1024
443
        return -(p - packet);
1025
443
      }
1026
1027
      /*
1028
       *  The pointer MUST point to a valid
1029
       *  length field, and not to another
1030
       *  pointer.
1031
       */
1032
7.76k
      if (*q > 63) {
1033
158
        fr_strerror_printf("Pointer %04x at offset %04x does not point to the start of a label",
1034
158
               offset, (unsigned int) (p - packet));
1035
158
        return -(p - packet);
1036
158
      }
1037
1038
      /*
1039
       *  The pointer MUST NOT point to an end of label field.
1040
       */
1041
7.60k
      if (!*q) {
1042
158
        fr_strerror_printf("Pointer %04x at offset %04x refers to an invalid field", offset,
1043
158
               (unsigned int) (p - packet));
1044
158
        return -(p - packet);
1045
158
      }
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
7.45k
      if (current == start) {
1054
5.08k
        *next = p + 2;
1055
5.08k
        already_set_next = true;
1056
5.08k
      }
1057
1058
7.45k
      p = current = q;
1059
7.45k
      continue;
1060
7.60k
    }
1061
1062
    /*
1063
     *  Else it's an uncompressed label
1064
     */
1065
42.9k
    if ((p + *p + 1) > end) goto overflow;
1066
1067
    /*
1068
     *  It's a valid label.  Mark it as such.
1069
     */
1070
42.0k
    dns_label_mark(lb, p);
1071
1072
    /*
1073
     *  Account for the '.' on every label after the
1074
     *  first one.
1075
     */
1076
42.0k
    if (!at_first_label) length++;
1077
42.0k
    at_first_label = false;
1078
42.0k
    length += *p;
1079
1080
    /*
1081
     *  DNS names can be no more than 255 octets.
1082
     */
1083
42.0k
    if (length > 255) {
1084
80
      fr_strerror_const("Total length of labels is > 255");
1085
80
      return -(p - packet);
1086
80
    }
1087
1088
41.9k
    q = p + 1;
1089
41.9k
    label_end = q + *p;
1090
1091
    /*
1092
     *  Allow for underscore at the beginning of a
1093
     *  label.
1094
     */
1095
41.9k
    if (*q == '_') q++;
1096
1097
    /*
1098
     *  Verify that the contents of the label are OK.
1099
     */
1100
92.2k
    while (q < label_end) {
1101
52.1k
      if (!((*q == '-') || ((*q >= '0') && (*q <= '9')) ||
1102
29.5k
            ((*q >= 'A') && (*q <= 'Z')) || ((*q >= 'a') && (*q <= 'z')))) {
1103
1.78k
        fr_strerror_printf("Invalid character 0x%02x in label", *q);
1104
1.78k
        return -(q - packet);
1105
1.78k
      }
1106
1107
50.3k
      q++;
1108
50.3k
    }
1109
1110
40.1k
    p += *p + 1;
1111
40.1k
  }
1112
1113
  /*
1114
   *  Return the length of this label.
1115
   */
1116
68.1k
  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
68.1k
  if (lb && !lb->mark) (void) dns_label_add(lb, start, *next);
1122
1123
68.1k
  return length;
1124
75.9k
}
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
6.77k
{
1139
6.77k
  ssize_t slen;
1140
6.77k
  uint8_t const *label = start;
1141
6.77k
  uint8_t const *end = buf + buf_len;
1142
1143
9.59k
  while (label < end) {
1144
8.08k
    if (*label == 0x00) {
1145
1.64k
      label++;
1146
1.64k
      break;
1147
1.64k
    }
1148
1149
6.44k
    slen = fr_dns_label_uncompressed_length(packet, buf, buf_len, &label, lb);
1150
6.44k
    if (slen <= 0) return slen; /* already is offset from 'buf' and not 'label' */
1151
6.44k
  }
1152
1153
3.14k
  return label - buf;
1154
6.77k
}
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
11.8k
{
1158
11.8k
  uint8_t const *p, *q;
1159
1160
11.8k
  p = *start;
1161
1162
11.8k
  if (end == packet) return -1;
1163
1164
11.8k
  if (*p == 0x00) {
1165
0
    *next = p + 1;
1166
0
    return 0;
1167
0
  }
1168
1169
  /*
1170
   *  Pointer, which points somewhere in the packet.
1171
   */
1172
11.8k
  if (*p >= 0xc0) {
1173
2.59k
    uint16_t offset;
1174
1175
2.59k
    if ((end - packet) < 2) {
1176
0
      return -(p - packet);
1177
0
    }
1178
1179
2.59k
    offset = p[1];
1180
2.59k
    offset += ((*p & ~0xc0) << 8);
1181
1182
2.59k
    q = packet + offset;
1183
2.59k
    if (q >= p) {
1184
0
      return -(p - packet);
1185
0
    }
1186
2.59k
    p = q;
1187
2.59k
  }
1188
1189
  /*
1190
   *  0b10 and 0b10 are forbidden, and pointers can't point to other pointers.
1191
   */
1192
11.8k
  if (*p > 63) return -(p - packet);
1193
1194
11.8k
  if ((p + *p + 1) > end) {
1195
0
    return -(p - packet);
1196
0
  }
1197
1198
  /*
1199
   *  Tell the caller where the actual label is located.
1200
   */
1201
11.8k
  *start = p;
1202
11.8k
  *next = p + *p + 1;
1203
11.8k
  return *p;
1204
11.8k
}
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
 */
1225
ssize_t fr_dns_label_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *dst,
1226
          uint8_t const *src, size_t len, uint8_t const *label,
1227
          bool tainted, fr_dns_labels_t *lb)
1228
34.2k
{
1229
34.2k
  ssize_t slen;
1230
34.2k
  uint8_t const *after = label;
1231
34.2k
  uint8_t const *current, *next = NULL;
1232
34.2k
  uint8_t const *packet = src;
1233
34.2k
  uint8_t const *end = packet + len;
1234
34.2k
  uint8_t *p;
1235
34.2k
  char *q;
1236
1237
34.2k
  if (!len) return -1;
1238
1239
  /*
1240
   *  The label must be within the current buffer we're
1241
   *  passed.
1242
   */
1243
34.2k
  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
34.2k
  if (lb) packet = lb->start;
1250
1251
  /*
1252
   *  Get the uncompressed length of the label, and the
1253
   *  label after this one.
1254
   */
1255
34.2k
  slen = fr_dns_label_uncompressed_length(packet, src, len, &after, lb);
1256
34.2k
  if (slen <= 0) {
1257
0
    FR_PROTO_TRACE("dns_label_to_value_box - Failed getting length");
1258
0
    return slen;
1259
0
  }
1260
1261
34.2k
  fr_value_box_init_null(dst);
1262
1263
  /*
1264
   *  An empty label is a 0x00 byte.  Just create an empty
1265
   *  string.
1266
   */
1267
34.2k
  if (slen == 1) {
1268
31.8k
    if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, 1, tainted) < 0) return -1;
1269
31.8k
    q[0] = '.';
1270
31.8k
    return after - label;
1271
31.8k
  }
1272
1273
  /*
1274
   *  Allocate the string and set up the value_box
1275
   */
1276
2.43k
  if (fr_value_box_bstr_alloc(ctx, &q, dst, NULL, slen, tainted) < 0) return -1;
1277
1278
2.43k
  current = label;
1279
2.43k
  p = (uint8_t *) q;
1280
2.43k
  q += slen;
1281
1282
14.3k
  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
11.8k
    slen = dns_label_decode(packet, end, &current, &next);
1288
11.8k
    if (slen < 0) return slen;
1289
1290
    /*
1291
     *  As a sanity check, ensure we don't have a
1292
     *  buffer overflow.
1293
     */
1294
11.8k
    if ((p + slen) > (uint8_t *) q) {
1295
0
      FR_PROTO_TRACE("dns_label_to_value_box - length %zd Failed at %d", slen, __LINE__);
1296
1297
0
    fail:
1298
0
      fr_value_box_clear(dst);
1299
0
      return -1;
1300
0
    }
1301
1302
    /*
1303
     *  Add '.' before the label, but only for the
1304
     *  second and subsequent labels.
1305
     */
1306
11.8k
    if (p != (uint8_t const *) dst->vb_strvalue) {
1307
9.44k
      *(p++) = '.';
1308
9.44k
    }
1309
1310
    /*
1311
     *  Copy the raw bytes from the network.
1312
     */
1313
11.8k
    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
11.8k
    p += slen;
1320
11.8k
    current = next;
1321
11.8k
  }
1322
1323
  /*
1324
   *  As a last sanity check, ensure that we've filled the
1325
   *  buffer exactly.
1326
   */
1327
2.43k
  if (p != (uint8_t *) q) {
1328
0
    FR_PROTO_TRACE("dns_label_to_value_box - Failed at %d", __LINE__);
1329
0
    goto fail;
1330
0
  }
1331
1332
2.43k
  *p = '\0';
1333
1334
  /*
1335
   *  Return the number of network bytes used to parse this
1336
   *  part of the label.
1337
   */
1338
2.43k
  return after - label;
1339
2.43k
}