Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/dhcpv4/decode.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
/**
18
 * $Id: 5d1934609387b2f73ee8420f23e0663dc34d8891 $
19
 *
20
 * @file protocols/dhcpv4/decode.c
21
 * @brief Functions to decode DHCP options.
22
 *
23
 * @copyright 2008,2017 The FreeRADIUS server project
24
 * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25
 * @copyright 2015,2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26
 */
27
#include <freeradius-devel/io/test_point.h>
28
#include <freeradius-devel/util/proto.h>
29
#include <freeradius-devel/util/struct.h>
30
#include <freeradius-devel/util/dns.h>
31
32
#include "dhcpv4.h"
33
#include "attrs.h"
34
35
static _Thread_local uint8_t  concat_buffer[1500]; /* ethernet max */
36
37
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
38
            fr_dict_attr_t const *parent,
39
            uint8_t const *data, size_t const data_len, void *decode_ctx);
40
41
static bool verify_tlvs(uint8_t const *data, size_t data_len)
42
4.77k
{
43
4.77k
  uint8_t const *p = data;
44
4.77k
  uint8_t const *end = data + data_len;
45
46
10.6k
  while (p < end) {
47
7.40k
    if ((end - p) < 2) return false;
48
49
6.46k
    if ((p + 2 + p[1]) > end) return false;
50
51
5.87k
    p += 2 + p[1];
52
5.87k
  }
53
54
3.23k
  return true;
55
4.77k
}
56
57
static ssize_t decode_tlv_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
58
             fr_dict_attr_t const *parent,
59
             uint8_t const *data, size_t const data_len, void *decode_ctx)
60
1.23k
{
61
1.23k
  return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, true);
62
1.23k
}
63
64
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
65
          uint8_t const *data, size_t data_len, void *decode_ctx);
66
67
/** Handle arrays of DNS labels for fr_struct_from_network()
68
 *
69
 */
70
static ssize_t decode_value_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
71
               fr_dict_attr_t const *parent,
72
               uint8_t const *data, size_t const data_len, void *decode_ctx)
73
14.7k
{
74
14.7k
  FR_PROTO_TRACE("decode_value_trampoline of %s with %zu bytes", parent->name, data_len);
75
76
  /*
77
   *  @todo - we might need to limit this to only one DNS label.
78
   */
79
14.7k
  if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) {
80
584
    return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false);
81
584
  }
82
83
14.1k
  return decode_value(ctx, out, parent, data, data_len, decode_ctx);
84
14.7k
}
85
86
/*
87
 *  Decode ONE value into a VP
88
 */
89
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da,
90
          uint8_t const *data, size_t data_len, void *decode_ctx)
91
38.2k
{
92
38.2k
  ssize_t slen;
93
38.2k
  fr_pair_t *vp;
94
38.2k
  uint8_t const *p = data;
95
38.2k
  uint8_t const *end = data + data_len;
96
38.2k
  bool exact = !da->flags.array;
97
98
38.2k
  FR_PROTO_TRACE("%s called to parse %zu bytes from %s", __FUNCTION__, data_len, da->name);
99
38.2k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
100
101
  /*
102
   *  Structs create their own VP wrapper.
103
   */
104
38.2k
  if (da->type == FR_TYPE_STRUCT) {
105
11.1k
    slen = fr_struct_from_network(ctx, out, da, data, data_len,
106
11.1k
                decode_ctx, decode_value_trampoline, decode_tlv_trampoline);
107
11.1k
    if (slen < 0) return slen;
108
109
7.22k
    if (!exact) return slen;
110
111
2.37k
    return data_len;
112
7.22k
  }
113
114
  /*
115
   *  These are always raw.
116
   */
117
27.0k
  if (da->flags.is_unknown) {
118
0
    return fr_pair_raw_from_network(ctx, out, da, data, data_len);
119
0
  }
120
121
27.0k
  vp = fr_pair_afrom_da(ctx, da);
122
27.0k
  if (!vp) return PAIR_DECODE_OOM;
123
27.0k
  PAIR_ALLOCED(vp);
124
125
  /*
126
   *  string / octets / bool can be empty.  Other data types are
127
   *  raw if they're empty.
128
   */
129
27.0k
  if (data_len == 0) {
130
4.93k
    if (da->type == FR_TYPE_BOOL) {
131
804
      vp->vp_bool = true;
132
804
      goto finish;
133
804
    }
134
135
4.12k
    if ((da->type == FR_TYPE_OCTETS) || (da->type == FR_TYPE_STRING)) {
136
2.43k
      goto finish;
137
2.43k
    }
138
139
4.12k
    talloc_free(vp);
140
1.68k
    return fr_pair_raw_from_network(ctx, out, da, data, 0);
141
4.12k
  }
142
143
22.1k
  switch (vp->vp_type) {
144
3.71k
  case FR_TYPE_ATTR:
145
    /*
146
     *  Force the length of the data to be one,
147
     *  otherwise the "from network" call complains.
148
     *  Because we pass in the enumv as the _parent_
149
     *  and not the da.  The da is marked as "array",
150
     *  but the parent is not.
151
     */
152
3.71k
    end = p + 1;
153
154
3.71k
    fr_assert(da->parent->flags.is_root);
155
156
3.71k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da->parent,
157
3.71k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
158
3.71k
    if (slen <= 0) goto raw;
159
160
3.71k
    p++;
161
3.71k
    break;
162
163
  /*
164
   *  Doesn't include scope, whereas the generic format can.
165
   */
166
934
  case FR_TYPE_IPV6_ADDR:
167
934
    slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da,
168
934
              128, p, (size_t) (end - p),
169
934
              exact, true);
170
934
    if (slen < 0) goto raw;
171
374
    fr_assert(slen == sizeof(vp->vp_ipv6addr));
172
173
374
    p += sizeof(vp->vp_ipv6addr);
174
374
    break;
175
176
0
  case FR_TYPE_IPV6_PREFIX:
177
    /*
178
     *  Not enough room for the prefix length, that's an issue.
179
     *
180
     *  Note that there's actually no standard for IPv6 prefixes inside of DHCPv4.
181
     */
182
0
    if ((end - p) < 1) goto raw;
183
184
0
    slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da,
185
0
              p[0], p + 1, ((size_t) (end - p)) - 1,
186
0
              exact, true);
187
0
    if (slen < 0) goto raw;
188
189
0
    p += slen + 1;
190
0
    break;
191
192
0
  case FR_TYPE_STRUCTURAL:
193
0
    fr_strerror_printf("Cannot decode type '%s' as value", fr_type_to_str(vp->vp_type));
194
0
    talloc_free(vp);
195
0
    return 0;
196
197
3.92k
  case FR_TYPE_IPV4_PREFIX:
198
3.92k
    fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true);
199
3.92k
    vp->vp_ip.af = AF_INET;
200
201
    /*
202
     *  4 octets of address
203
     *  4 octets of mask
204
     */
205
3.92k
    if (fr_dhcpv4_flag_prefix_split(da)) {
206
1.31k
      uint32_t ipaddr, mask;
207
208
1.31k
      if (data_len < 8) goto raw;
209
210
880
      ipaddr = fr_nbo_to_uint32(p);
211
880
      mask = fr_nbo_to_uint32(p + 4);
212
880
      p += 8;
213
214
      /*
215
       *  0/0 means a prefix of 0, too.
216
       */
217
880
      if (!mask) {
218
143
        break;
219
143
      }
220
221
      /*
222
       *  Try to figure out the prefix value from the mask.
223
       */
224
21.5k
      while (mask) {
225
20.7k
        vp->vp_ip.prefix++;
226
20.7k
        mask <<= 1;
227
20.7k
      }
228
229
      /*
230
       *  Mash the IP based on the calculated mask.  We don't really care if the mask
231
       *  has holes, or if the IP address overlaps with the mask.  We just fix it all up
232
       *  so it's sane.
233
       */
234
737
      mask = ~(uint32_t) 0;
235
737
      mask <<= (32 - vp->vp_ip.prefix);
236
237
737
      vp->vp_ipv4addr = htonl(ipaddr & mask);
238
737
      break;
239
880
    }
240
241
2.61k
    if (fr_dhcpv4_flag_prefix_bits(vp->da)) {
242
2.61k
      size_t needs;
243
244
2.61k
      if ((data_len == 0) || (*p > 32)) goto raw;
245
246
2.24k
      needs = 1 + ((*p + 0x07) >> 3);
247
2.24k
      if (data_len < needs) goto raw;
248
249
      /*
250
       *  Don't do exact checks here, as the content is variable-sized.
251
       */
252
253
1.94k
      vp->vp_ip.prefix = *p;
254
255
      /*
256
       *  If the IP address is longer than necessary, then only grab the pieces we need.
257
       */
258
1.94k
      if (vp->vp_ip.prefix) {
259
1.08k
        uint32_t ipaddr, mask;
260
261
1.08k
        mask = ~(uint32_t) 0;
262
1.08k
        mask <<= (32 - vp->vp_ip.prefix);
263
264
1.08k
        if (*p > 24) {
265
253
          ipaddr = fr_nbo_to_uint32(p + 1);
266
267
830
        } else if (*p > 16) {
268
281
          ipaddr = fr_nbo_to_uint24(p + 1);
269
281
          ipaddr <<= 8;
270
271
549
        } else if (*p > 8) {
272
110
          ipaddr = fr_nbo_to_uint16(p + 1);
273
110
          ipaddr <<= 16;
274
275
439
        } else { /* 1..8 */
276
439
          ipaddr = p[1];
277
439
          ipaddr <<= 24;
278
439
        }
279
280
1.08k
        vp->vp_ipv4addr = htonl(ipaddr & mask);
281
1.08k
      } /* else *p==0, and we leave ipaddr set to zero */
282
283
1.94k
      p += needs;
284
1.94k
      break;
285
2.24k
    }
286
287
0
    FALL_THROUGH;
288
289
13.5k
  default:
290
13.5k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da,
291
13.5k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
292
13.5k
    if (slen < 0) {
293
5.62k
    raw:
294
5.62k
      FR_PROTO_TRACE("decoding as unknown type");
295
5.62k
      if (fr_pair_raw_afrom_pair(vp, p, (end - p)) < 0) {
296
0
        return -1;
297
0
      }
298
5.62k
      p = end;
299
5.62k
      break;
300
5.62k
    }
301
302
9.81k
    if (exact && (slen != (end - p))) {
303
202
      goto raw;
304
202
    }
305
306
9.61k
    p += (size_t) slen;
307
9.61k
    break;
308
22.1k
  }
309
310
25.3k
finish:
311
25.3k
  FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", (size_t) (p - data));
312
25.3k
  fr_pair_append(out, vp);
313
314
25.3k
  return p - data;
315
22.1k
}
316
317
/** RFC 4243 Vendor Specific Suboptions
318
 *
319
 * Vendor specific suboptions are in the format.
320
 @verbatim
321
      0                   1                   2                   3
322
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
323
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
324
     |                     Enterprise Number 0                       |
325
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326
     |    Len 0      |                                               /
327
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
328
     /                      Suboption Data 0                         /
329
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330
     |                     Enterprise Number n                       |
331
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332
     |    Len n      |                                               /
333
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334
     /                      Suboption Data n                         /
335
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
336
 @endverbatim
337
 *
338
 * So although the vendor is identified, the format of the data isn't
339
 * specified so we can't actually resolve the suboption to an
340
 * attribute.  For now, we just convert it to an attribute of
341
 * Vendor-Specific-Information with raw octets contents.
342
 */
343
344
345
/*
346
 *  One VSA option may contain multiple vendors, each vendor
347
 *  may contain one or more sub-options.
348
 *
349
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
350
 *  |  option-code  |  option-len   |
351
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352
 *  |      enterprise-number1       |
353
 *  |                               |
354
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355
 *  |   data-len1   |               |
356
 *  +-+-+-+-+-+-+-+-+ option-data1  |
357
 *  /                               /
358
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
359
 *  |      enterprise-number2       |   ^
360
 *  |                               |   |
361
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |
362
 *  |   data-len2   |               | optional
363
 *  +-+-+-+-+-+-+-+-+ option-data2  |   |
364
 *  /                               /   |
365
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |
366
 *  ~            ...                ~   V
367
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
368
 */
369
static ssize_t decode_vsa(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
370
        uint8_t const *data, size_t const data_len, void *decode_ctx)
371
3.82k
{
372
3.82k
  size_t      len;
373
3.82k
  ssize_t     slen;
374
3.82k
  uint8_t     option_len;
375
3.82k
  uint32_t    pen;
376
3.82k
  fr_pair_t   *vp;
377
3.82k
  fr_dict_attr_t const  *vendor;
378
3.82k
  uint8_t const   *end = data + data_len;
379
3.82k
  uint8_t const   *p = data;
380
3.82k
  fr_pair_list_t    list;
381
382
3.82k
  FR_PROTO_HEX_DUMP(data, data_len, "decode_vsa");
383
384
3.82k
  if (!fr_cond_assert_msg(parent->type == FR_TYPE_VSA,
385
3.82k
        "%s: Internal sanity check failed, attribute \"%s\" is not of type 'vsa'",
386
3.82k
        __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
387
388
3.82k
  fr_pair_list_init(&list);
389
390
5.26k
next:
391
  /*
392
   *  RFC 4243 Section 3 says that "the minimum length is 4 bytes."
393
   */
394
5.26k
  len = (size_t) (end - p);
395
5.26k
  if (len < (sizeof(uint32_t))) {
396
2.73k
fail:
397
2.73k
    fr_pair_list_free(&list);
398
2.73k
    return -1;
399
2.30k
  }
400
401
  /*
402
   *  RFC 4243 is silent about this, but we assime that anything with no vendor data means that we
403
   *  ignore it.  i.e. we don't create an empty vendor attribute.
404
   */
405
2.96k
  if (len == (sizeof(uint32_t) + 1)) {
406
    /*
407
     *  There's no more data, so this length field must be zero.
408
     */
409
222
    if (p[4] != 0) goto fail;
410
122
    goto done;
411
222
  }
412
413
2.73k
  pen = fr_nbo_to_uint32(p);
414
415
  /*
416
   *  Verify that the parent (which should be a VSA)
417
   *  contains a fake attribute representing the vendor.
418
   *
419
   *  If it doesn't then this vendor is unknown, but we know
420
   *  vendor attributes have a standard format, so we can
421
   *  decode the data anyway.
422
   */
423
2.73k
  vendor = fr_dict_attr_child_by_num(parent, pen);
424
2.73k
  if (!vendor) {
425
2.15k
    fr_dict_attr_t *n;
426
427
2.15k
    n = fr_dict_attr_unknown_vendor_afrom_num(ctx, parent, pen);
428
2.15k
    if (!n) {
429
0
    oom:
430
0
      fr_pair_list_free(&list);
431
0
      return PAIR_DECODE_OOM;
432
0
    }
433
2.15k
    vendor = n;
434
2.15k
  }
435
2.73k
  p += sizeof(uint32_t);
436
437
  /*
438
   *  No more data, we're done.
439
   */
440
2.73k
  if (p == end) goto done;
441
442
2.65k
  FR_PROTO_TRACE("decode context %s -> %s", parent->name, vendor->name);
443
444
2.65k
  option_len = p[0];
445
2.65k
  if ((p + 1 + option_len) > end) {
446
491
    slen = fr_pair_raw_from_network(ctx, out, vendor, p, end - p);
447
491
    if (slen < 0) goto fail;
448
449
491
    goto done;
450
491
  }
451
2.16k
  p++;
452
453
  /*
454
   *  Pathological case of no data.
455
   */
456
2.16k
  if (option_len == 0) goto next;
457
458
1.43k
  vp = fr_pair_find_by_da(out, NULL, vendor);
459
1.43k
  if (!vp) {
460
1.12k
    vp = fr_pair_afrom_da(ctx, vendor);
461
1.12k
    if (!vp) goto oom;
462
1.12k
    PAIR_ALLOCED(vp);
463
464
1.12k
    fr_pair_append(out, vp);
465
1.12k
  }
466
467
1.43k
  slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false);
468
1.43k
  if (slen < 0) goto fail;
469
470
1.10k
  p += option_len;
471
1.10k
  if (p < end) goto next;
472
473
  /*
474
   *  Tell the caller we read all of it, even if we didn't.
475
   */
476
1.09k
done:
477
1.09k
  fr_pair_list_append(out, &list);
478
1.09k
  return data_len;
479
1.10k
}
480
481
482
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
483
            fr_dict_attr_t const *parent,
484
            uint8_t const *data, size_t const data_len, void *decode_ctx)
485
27.9k
{
486
27.9k
  unsigned int      option;
487
27.9k
  size_t      len;
488
27.9k
  ssize_t     slen;
489
27.9k
  fr_dict_attr_t const  *da;
490
27.9k
  fr_dhcpv4_ctx_t   *packet_ctx = decode_ctx;
491
492
#ifdef STATIC_ANALYZER
493
  if (!packet_ctx || !packet_ctx->tmp_ctx) return PAIR_DECODE_FATAL_ERROR;
494
#endif
495
496
27.9k
  fr_assert(parent != NULL);
497
498
  /*
499
   *      RFC 3046 is very specific about not allowing termination
500
   *      with a 255 sub-option. But it's required for decoding
501
   *      option 43, and vendors will probably screw it up
502
   *      anyway.
503
   *
504
   *      Similarly, option 0 is sometimes treated as
505
   *      "end of options".
506
   *
507
   *  @todo - this check is likely correct only when at the
508
   *  dhcpv4 root, OR inside of option 43.  It could be
509
   *  argued that it's wrong for all other TLVs.
510
   */
511
27.9k
  if ((data_len == 1) && ((data[0] == 0) || (data[0] == 255))) return data_len;
512
513
  /*
514
   *  Must have at least an option header.
515
   */
516
27.9k
  if (data_len < 2) {
517
0
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
518
0
    return -(data_len);
519
0
  }
520
521
27.9k
  option = data[0];
522
27.9k
  len = data[1];
523
27.9k
  if (len > (data_len - 2)) {
524
0
    fr_strerror_printf("%s: Option overflows input.  "
525
0
           "Optional length must be less than %zu bytes, got %zu bytes",
526
0
           __FUNCTION__, data_len - 2, len);
527
0
    return PAIR_DECODE_FATAL_ERROR;
528
0
  }
529
530
27.9k
  da = fr_dict_attr_child_by_num(parent, option);
531
27.9k
  if (!da) {
532
4.13k
    da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option);
533
4.13k
    if (!da) return PAIR_DECODE_OOM;
534
535
4.13k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
536
537
23.8k
  } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
538
1.90k
    slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true);
539
540
21.9k
  } else if (da->flags.array) {
541
4.30k
    slen = fr_pair_array_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_value);
542
543
17.6k
  } else if (da->type == FR_TYPE_VSA) {
544
3.48k
    bool append = false;
545
3.48k
    fr_pair_t *vp;
546
547
3.48k
    vp = fr_pair_find_by_da(out, NULL, da);
548
3.48k
    if (!vp) {
549
2.46k
      vp = fr_pair_afrom_da(ctx, da);
550
2.46k
      if (!vp) return PAIR_DECODE_FATAL_ERROR;
551
2.46k
      PAIR_ALLOCED(vp);
552
553
2.46k
      append = true;
554
2.46k
    }
555
556
3.48k
    slen = decode_vsa(vp, &vp->vp_group, da, data + 2, len, decode_ctx);
557
3.48k
    if (append) {
558
2.46k
      if (slen < 0) {
559
1.95k
        TALLOC_FREE(vp);
560
1.95k
      } else {
561
511
        fr_pair_append(out, vp);
562
511
      }
563
2.46k
    }
564
565
14.1k
  } else if (da->type == FR_TYPE_TLV) {
566
1.66k
    slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, true);
567
568
12.5k
  } else {
569
12.5k
    slen = decode_value(ctx, out, da, data + 2, len, decode_ctx);
570
12.5k
  }
571
572
27.9k
  if (slen < 0) {
573
6.28k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
574
6.28k
    if (slen < 0) return slen;
575
6.28k
  }
576
577
27.9k
  return len + 2;
578
27.9k
}
579
580
/** Decode DHCP option
581
 *
582
 * @param[in] ctx context to alloc new attributes in.
583
 * @param[out] out    Where to write the decoded options.
584
 * @param[in] data    to parse.
585
 * @param[in] data_len    of data to parse.
586
 * @param[in] decode_ctx  Unused.
587
 */
588
ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
589
              uint8_t const *data, size_t data_len, void *decode_ctx)
590
32.4k
{
591
32.4k
  ssize_t     slen;
592
32.4k
  uint8_t const   *p = data, *end = data + data_len;
593
32.4k
  uint8_t const   *next;
594
32.4k
  fr_dhcpv4_ctx_t   *packet_ctx = decode_ctx;
595
596
32.4k
  FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);
597
598
32.4k
  if (data_len == 0) return 0;
599
600
32.4k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
601
602
  /*
603
   *  Padding / End of options
604
   */
605
32.4k
  if (p[0] == 0) {     /* 0x00 - Padding option    */
606
4.18k
    data_len = 1;     /* Walk over any consecutive 0x00 */
607
4.18k
    p++;        /* for efficiency     */
608
8.53k
    while ((p < end) && (p[0] == 0)) {
609
4.34k
      p++;
610
4.34k
      data_len ++;
611
4.34k
    }
612
4.18k
    return data_len;
613
4.18k
  }
614
28.2k
  if (p[0] == 255) return data_len;  /* 0xff - End of options signifier */
615
616
  /*
617
   *  Everything else should be real options
618
   */
619
28.0k
  if ((data_len < 2) || ((size_t) (data[1] + 2) > data_len)) {
620
2.62k
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
621
2.62k
    return -1;
622
2.62k
  }
623
624
  /*
625
   *  Check for multiple options of the same type, and concatenate their values together.
626
   *
627
   *  RFC 2131 Section 4.1 says:
628
   *
629
   *    The client concatenates the values of multiple
630
   *    instances of the same option into a single parameter
631
   *    list for configuration.
632
   *
633
   *  which presumably also means the same for the server on reception.
634
   *
635
   *  We therefore peek ahead, and concatenate the values into a temporary buffer.  The buffer is
636
   *  allocated only if necessary, and is re-used for the entire packet.
637
   *
638
   *  If the options are *not* consecutive, then we don't concatenate them.  Too bad for you!
639
   *
640
   *  Note that we don't (yet) do this for TLVs.
641
   */
642
25.4k
  next = data + 2 + data[1];
643
25.4k
  if ((data[1] > 0) && (next < end) && (next[0] == data[0])) {
644
2.86k
    uint8_t *q;
645
2.86k
    fr_dict_attr_t const *da;
646
647
2.86k
    q = concat_buffer;
648
649
8.67k
    for (next = data; next < end; next += 2 + next[1]) {
650
8.28k
      if (next[0] != data[0]) break;
651
6.11k
      if ((end - next) < 2) return -1;
652
6.06k
      if ((next + 2 + next[1]) > end) return -1;
653
654
5.81k
      if ((size_t) (q + next[1] - concat_buffer) > sizeof(concat_buffer)) return -1;
655
656
5.81k
      memcpy(q, next + 2, next[1]);
657
5.81k
      q += next[1];
658
5.81k
    }
659
660
2.55k
    da = fr_dict_attr_child_by_num(packet_ctx->root, p[0]);
661
2.55k
    if (!da) {
662
235
      da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, packet_ctx->root, p[0]);
663
235
      if (!da) return -1;
664
665
235
      slen = fr_pair_raw_from_network(ctx, out, da, concat_buffer, q - concat_buffer);
666
667
2.32k
    } else if (da->type == FR_TYPE_VSA) {
668
341
      slen = decode_vsa(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
669
670
1.98k
    } else if (da->type == FR_TYPE_TLV) {
671
920
      slen = fr_pair_tlvs_from_network(ctx, out, da, concat_buffer, q - concat_buffer,
672
920
               packet_ctx, decode_option, verify_tlvs, true);
673
674
1.06k
    } else if (da->flags.array) {
675
168
      slen = fr_pair_array_from_network(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx, decode_value);
676
893
    } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
677
33
      slen = fr_pair_dns_labels_from_network(ctx, out, da, concat_buffer, concat_buffer,
678
33
                     q - concat_buffer, NULL, true);
679
680
860
    } else {
681
860
      slen = decode_value(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
682
860
    }
683
2.55k
    if (slen < 0) return slen;
684
685
    /*
686
     *  The actual amount of data we decoded, including the various headers.
687
     */
688
2.22k
    FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %zu byte(s)", slen, (size_t) (next - data));
689
2.22k
    return next - data;
690
2.55k
  }
691
692
22.5k
  slen = decode_option(ctx, out, packet_ctx->root, data, data[1] + 2, decode_ctx);
693
22.5k
  if (slen < 0) return slen;
694
695
22.5k
  FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %u byte(s)", slen, (unsigned int) data[1] + 2);
696
22.5k
  return data[1] + 2;
697
22.5k
}
698
699
ssize_t fr_dhcpv4_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out,
700
         uint8_t const *data, size_t data_len)
701
6.18k
{
702
6.18k
  ssize_t slen;
703
6.18k
  uint8_t const *attr, *end;
704
705
6.18k
  fr_dhcpv4_ctx_t decode_ctx = {
706
6.18k
    .root = fr_dict_root(dict_dhcpv4)
707
6.18k
  };
708
709
6.18k
  fr_assert(dict_dhcpv4 != NULL);
710
711
6.18k
  decode_ctx.tmp_ctx = talloc(ctx, uint8_t);
712
713
6.18k
  attr = data;
714
6.18k
  end = data + data_len;
715
716
35.3k
  while (attr < end) {
717
32.4k
    slen = fr_dhcpv4_decode_option(ctx, out, attr, (end - attr), &decode_ctx);
718
32.4k
    if (slen < 0) {
719
3.26k
      talloc_free(decode_ctx.tmp_ctx);
720
3.26k
      return slen;
721
3.26k
    }
722
723
    /*
724
     *  If slen is larger than the room in the packet,
725
     *  all kinds of bad things happen.
726
     */
727
29.1k
     if (!fr_cond_assert(slen <= (end - attr))) {
728
0
      talloc_free(decode_ctx.tmp_ctx);
729
0
       return -slen - (attr - data);
730
0
     }
731
732
29.1k
    attr += slen;
733
29.1k
    talloc_free_children(decode_ctx.tmp_ctx);
734
29.1k
  }
735
736
6.18k
  talloc_free(decode_ctx.tmp_ctx);
737
2.92k
  return data_len;
738
6.18k
}
739
740
741
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
742
         fr_dict_attr_t const *root_da)
743
0
{
744
0
  fr_dhcpv4_ctx_t *test_ctx;
745
746
0
  test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t);
747
0
  test_ctx->tmp_ctx = talloc(test_ctx, uint8_t);
748
0
  test_ctx->root = root_da ? root_da : fr_dict_root(dict_dhcpv4);
749
750
0
  *out = test_ctx;
751
752
0
  return 0;
753
0
}
754
755
756
static ssize_t fr_dhcpv4_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out,
757
              uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
758
0
{
759
0
  unsigned int  code;
760
761
0
  if (!fr_dhcpv4_ok(data, data_len, NULL, NULL)) return -1;
762
763
0
  if (fr_dhcpv4_decode(ctx, out, data, data_len, &code) < 0) return -1;
764
765
0
  return data_len;
766
0
}
767
768
static ssize_t decode_option_wrapper(TALLOC_CTX *ctx, fr_pair_list_t *out, NDEBUG_UNUSED fr_dict_attr_t const *parent,
769
              uint8_t const *data, size_t data_len, void *decode_ctx)
770
0
{
771
0
  fr_assert(parent == fr_dict_root(dict_dhcpv4));
772
773
0
  return fr_dhcpv4_decode_option(ctx, out, data, data_len, decode_ctx);
774
0
}
775
776
/*
777
 *  Test points
778
 */
779
extern fr_test_point_pair_decode_t dhcpv4_tp_decode_pair;
780
fr_test_point_pair_decode_t dhcpv4_tp_decode_pair = {
781
  .test_ctx = decode_test_ctx,
782
  .func   = decode_option_wrapper
783
};
784
785
extern fr_test_point_proto_decode_t dhcpv4_tp_decode_proto;
786
fr_test_point_proto_decode_t dhcpv4_tp_decode_proto = {
787
  .test_ctx = decode_test_ctx,
788
  .func   = fr_dhcpv4_decode_proto
789
};