Coverage Report

Created: 2026-05-11 06:44

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.72k
{
43
4.72k
  uint8_t const *p = data;
44
4.72k
  uint8_t const *end = data + data_len;
45
46
9.56k
  while (p < end) {
47
6.95k
    if ((end - p) < 2) return false;
48
49
5.57k
    if ((p + 2 + p[1]) > end) return false;
50
51
4.84k
    p += 2 + p[1];
52
4.84k
  }
53
54
2.61k
  return true;
55
4.72k
}
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
979
{
61
979
  return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, true);
62
979
}
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
22.8k
{
74
22.8k
  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
22.8k
  if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) {
80
1.01k
    return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false);
81
1.01k
  }
82
83
21.8k
  return decode_value(ctx, out, parent, data, data_len, decode_ctx);
84
22.8k
}
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
58.5k
{
92
58.5k
  ssize_t slen;
93
58.5k
  fr_pair_t *vp;
94
58.5k
  uint8_t const *p = data;
95
58.5k
  uint8_t const *end = data + data_len;
96
58.5k
  bool exact = !da->flags.array;
97
98
58.5k
  FR_PROTO_TRACE("%s called to parse %zu bytes from %s", __FUNCTION__, data_len, da->name);
99
58.5k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
100
101
  /*
102
   *  Structs create their own VP wrapper.
103
   */
104
58.5k
  if (da->type == FR_TYPE_STRUCT) {
105
15.1k
    slen = fr_struct_from_network(ctx, out, da, data, data_len,
106
15.1k
                decode_ctx, decode_value_trampoline, decode_tlv_trampoline);
107
15.1k
    if (slen < 0) return slen;
108
109
10.1k
    if (!exact) return slen;
110
111
3.66k
    return data_len;
112
10.1k
  }
113
114
  /*
115
   *  These are always raw.
116
   */
117
43.4k
  if (da->flags.is_unknown) {
118
0
    return fr_pair_raw_from_network(ctx, out, da, data, data_len);
119
0
  }
120
121
43.4k
  vp = fr_pair_afrom_da(ctx, da);
122
43.4k
  if (!vp) return PAIR_DECODE_OOM;
123
43.4k
  PAIR_ALLOCED(vp);
124
125
  /*
126
   *  string / octets / bool can be empty.  Other data types are
127
   *  raw if they're empty.
128
   */
129
43.4k
  if (data_len == 0) {
130
4.52k
    if (da->type == FR_TYPE_BOOL) {
131
429
      vp->vp_bool = true;
132
429
      goto finish;
133
429
    }
134
135
4.09k
    if ((da->type == FR_TYPE_OCTETS) || (da->type == FR_TYPE_STRING)) {
136
2.28k
      goto finish;
137
2.28k
    }
138
139
4.09k
    talloc_free(vp);
140
1.80k
    return fr_pair_raw_from_network(ctx, out, da, data, 0);
141
4.09k
  }
142
143
38.9k
  switch (vp->vp_type) {
144
9.40k
  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
9.40k
    end = p + 1;
153
154
9.40k
    fr_assert(da->parent->flags.is_root);
155
156
9.40k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da->parent,
157
9.40k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
158
9.40k
    if (slen <= 0) goto raw;
159
160
9.40k
    p++;
161
9.40k
    break;
162
163
  /*
164
   *  Doesn't include scope, whereas the generic format can.
165
   */
166
1.16k
  case FR_TYPE_IPV6_ADDR:
167
1.16k
    slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da,
168
1.16k
              128, p, (size_t) (end - p),
169
1.16k
              exact, true);
170
1.16k
    if (slen < 0) goto raw;
171
665
    fr_assert(slen == sizeof(vp->vp_ipv6addr));
172
173
665
    p += sizeof(vp->vp_ipv6addr);
174
665
    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
5.37k
  case FR_TYPE_IPV4_PREFIX:
198
5.37k
    fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true);
199
5.37k
    vp->vp_ip.af = AF_INET;
200
201
    /*
202
     *  4 octets of address
203
     *  4 octets of mask
204
     */
205
5.37k
    if (fr_dhcpv4_flag_prefix_split(da)) {
206
1.45k
      uint32_t ipaddr, mask;
207
208
1.45k
      if (data_len < 8) goto raw;
209
210
772
      ipaddr = fr_nbo_to_uint32(p);
211
772
      mask = fr_nbo_to_uint32(p + 4);
212
772
      p += 8;
213
214
      /*
215
       *  0/0 means a prefix of 0, too.
216
       */
217
772
      if (!mask) {
218
186
        break;
219
186
      }
220
221
      /*
222
       *  Try to figure out the prefix value from the mask.
223
       */
224
17.1k
      while (mask) {
225
16.5k
        vp->vp_ip.prefix++;
226
16.5k
        mask <<= 1;
227
16.5k
      }
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
586
      mask = ~(uint32_t) 0;
235
586
      mask <<= (32 - vp->vp_ip.prefix);
236
237
586
      vp->vp_ipv4addr = htonl(ipaddr & mask);
238
586
      break;
239
772
    }
240
241
3.92k
    if (fr_dhcpv4_flag_prefix_bits(vp->da)) {
242
3.92k
      size_t needs;
243
244
3.92k
      if ((data_len == 0) || (*p > 32)) goto raw;
245
246
3.10k
      needs = 1 + ((*p + 0x07) >> 3);
247
3.10k
      if (data_len < needs) goto raw;
248
249
      /*
250
       *  Don't do exact checks here, as the content is variable-sized.
251
       */
252
253
2.43k
      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
2.43k
      if (vp->vp_ip.prefix) {
259
1.61k
        uint32_t ipaddr, mask;
260
261
1.61k
        mask = ~(uint32_t) 0;
262
1.61k
        mask <<= (32 - vp->vp_ip.prefix);
263
264
1.61k
        if (*p > 24) {
265
267
          ipaddr = fr_nbo_to_uint32(p + 1);
266
267
1.34k
        } else if (*p > 16) {
268
251
          ipaddr = fr_nbo_to_uint24(p + 1);
269
251
          ipaddr <<= 8;
270
271
1.09k
        } else if (*p > 8) {
272
301
          ipaddr = fr_nbo_to_uint16(p + 1);
273
301
          ipaddr <<= 16;
274
275
792
        } else { /* 1..8 */
276
792
          ipaddr = p[1];
277
792
          ipaddr <<= 24;
278
792
        }
279
280
1.61k
        vp->vp_ipv4addr = htonl(ipaddr & mask);
281
1.61k
      } /* else *p==0, and we leave ipaddr set to zero */
282
283
2.43k
      p += needs;
284
2.43k
      break;
285
3.10k
    }
286
287
0
    FALL_THROUGH;
288
289
22.9k
  default:
290
22.9k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da,
291
22.9k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
292
22.9k
    if (slen < 0) {
293
8.77k
    raw:
294
8.77k
      FR_PROTO_TRACE("decoding as unknown type");
295
8.77k
      if (fr_pair_raw_afrom_pair(vp, p, (end - p)) < 0) {
296
0
        return -1;
297
0
      }
298
8.77k
      p = end;
299
8.77k
      break;
300
8.77k
    }
301
302
16.9k
    if (exact && (slen != (end - p))) {
303
91
      goto raw;
304
91
    }
305
306
16.8k
    p += (size_t) slen;
307
16.8k
    break;
308
38.9k
  }
309
310
41.6k
finish:
311
41.6k
  FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", (size_t) (p - data));
312
41.6k
  fr_pair_append(out, vp);
313
314
41.6k
  return p - data;
315
38.9k
}
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
5.75k
{
372
5.75k
  size_t      len;
373
5.75k
  ssize_t     slen;
374
5.75k
  uint8_t     option_len;
375
5.75k
  uint32_t    pen;
376
5.75k
  fr_pair_t   *vp;
377
5.75k
  fr_dict_attr_t const  *vendor;
378
5.75k
  uint8_t const   *end = data + data_len;
379
5.75k
  uint8_t const   *p = data;
380
5.75k
  fr_pair_list_t    list;
381
382
5.75k
  FR_PROTO_HEX_DUMP(data, data_len, "decode_vsa");
383
384
5.75k
  if (!fr_cond_assert_msg(parent->type == FR_TYPE_VSA,
385
5.75k
        "%s: Internal sanity check failed, attribute \"%s\" is not of type 'vsa'",
386
5.75k
        __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
387
388
5.75k
  fr_pair_list_init(&list);
389
390
6.65k
next:
391
  /*
392
   *  RFC 4243 Section 3 says that "the minimum length is 4 bytes."
393
   */
394
6.65k
  len = (size_t) (end - p);
395
6.65k
  if (len < (sizeof(uint32_t))) {
396
4.20k
fail:
397
4.20k
    fr_pair_list_free(&list);
398
4.20k
    return -1;
399
3.71k
  }
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.94k
  if (len == (sizeof(uint32_t) + 1)) {
406
    /*
407
     *  There's no more data, so this length field must be zero.
408
     */
409
272
    if (p[4] != 0) goto fail;
410
64
    goto done;
411
272
  }
412
413
2.66k
  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.66k
  vendor = fr_dict_attr_child_by_num(parent, pen);
424
2.66k
  if (!vendor) {
425
2.18k
    fr_dict_attr_t *n;
426
427
2.18k
    n = fr_dict_attr_unknown_vendor_afrom_num(ctx, parent, pen);
428
2.18k
    if (!n) {
429
0
    oom:
430
0
      fr_pair_list_free(&list);
431
0
      return PAIR_DECODE_OOM;
432
0
    }
433
2.18k
    vendor = n;
434
2.18k
  }
435
2.66k
  p += sizeof(uint32_t);
436
437
  /*
438
   *  No more data, we're done.
439
   */
440
2.66k
  if (p == end) goto done;
441
442
2.53k
  FR_PROTO_TRACE("decode context %s -> %s", parent->name, vendor->name);
443
444
2.53k
  option_len = p[0];
445
2.53k
  if ((p + 1 + option_len) > end) {
446
615
    slen = fr_pair_raw_from_network(ctx, out, vendor, p, end - p);
447
615
    if (slen < 0) goto fail;
448
449
615
    goto done;
450
615
  }
451
1.92k
  p++;
452
453
  /*
454
   *  Pathological case of no data.
455
   */
456
1.92k
  if (option_len == 0) goto next;
457
458
1.26k
  vp = fr_pair_find_by_da(out, NULL, vendor);
459
1.26k
  if (!vp) {
460
1.00k
    vp = fr_pair_afrom_da(ctx, vendor);
461
1.00k
    if (!vp) goto oom;
462
1.00k
    PAIR_ALLOCED(vp);
463
464
1.00k
    fr_pair_append(out, vp);
465
1.00k
  }
466
467
1.26k
  slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false);
468
1.26k
  if (slen < 0) goto fail;
469
470
989
  p += option_len;
471
989
  if (p < end) goto next;
472
473
  /*
474
   *  Tell the caller we read all of it, even if we didn't.
475
   */
476
1.55k
done:
477
1.55k
  fr_pair_list_append(out, &list);
478
1.55k
  return data_len;
479
989
}
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
36.6k
{
486
36.6k
  unsigned int      option;
487
36.6k
  size_t      len;
488
36.6k
  ssize_t     slen;
489
36.6k
  fr_dict_attr_t const  *da;
490
36.6k
  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
36.6k
  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
36.6k
  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
36.6k
  if (data_len < 2) {
517
0
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
518
0
    return -(data_len);
519
0
  }
520
521
36.6k
  option = data[0];
522
36.6k
  len = data[1];
523
36.6k
  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
36.6k
  da = fr_dict_attr_child_by_num(parent, option);
531
36.6k
  if (!da) {
532
3.44k
    da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option);
533
3.44k
    if (!da) return PAIR_DECODE_OOM;
534
535
3.44k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
536
537
33.1k
  } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
538
4.21k
    slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true);
539
540
28.9k
  } else if (da->flags.array) {
541
5.51k
    slen = fr_pair_array_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_value);
542
543
23.4k
  } else if (da->type == FR_TYPE_VSA) {
544
5.27k
    bool append = false;
545
5.27k
    fr_pair_t *vp;
546
547
5.27k
    vp = fr_pair_find_by_da(out, NULL, da);
548
5.27k
    if (!vp) {
549
3.15k
      vp = fr_pair_afrom_da(ctx, da);
550
3.15k
      if (!vp) return PAIR_DECODE_FATAL_ERROR;
551
3.15k
      PAIR_ALLOCED(vp);
552
553
3.15k
      append = true;
554
3.15k
    }
555
556
5.27k
    slen = decode_vsa(vp, &vp->vp_group, da, data + 2, len, decode_ctx);
557
5.27k
    if (append) {
558
3.15k
      if (slen < 0) {
559
2.74k
        TALLOC_FREE(vp);
560
2.74k
      } else {
561
409
        fr_pair_append(out, vp);
562
409
      }
563
3.15k
    }
564
565
18.1k
  } else if (da->type == FR_TYPE_TLV) {
566
2.50k
    slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, true);
567
568
15.6k
  } else {
569
15.6k
    slen = decode_value(ctx, out, da, data + 2, len, decode_ctx);
570
15.6k
  }
571
572
36.6k
  if (slen < 0) {
573
9.34k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
574
9.34k
    if (slen < 0) return slen;
575
9.34k
  }
576
577
36.6k
  return len + 2;
578
36.6k
}
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
42.6k
{
591
42.6k
  ssize_t     slen;
592
42.6k
  uint8_t const   *p = data, *end = data + data_len;
593
42.6k
  uint8_t const   *next;
594
42.6k
  fr_dhcpv4_ctx_t   *packet_ctx = decode_ctx;
595
596
42.6k
  FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);
597
598
42.6k
  if (data_len == 0) return 0;
599
600
42.6k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
601
602
  /*
603
   *  Padding / End of options
604
   */
605
42.6k
  if (p[0] == 0) {     /* 0x00 - Padding option    */
606
4.99k
    data_len = 1;     /* Walk over any consecutive 0x00 */
607
4.99k
    p++;        /* for efficiency     */
608
8.58k
    while ((p < end) && (p[0] == 0)) {
609
3.58k
      p++;
610
3.58k
      data_len ++;
611
3.58k
    }
612
4.99k
    return data_len;
613
4.99k
  }
614
37.6k
  if (p[0] == 255) return data_len;  /* 0xff - End of options signifier */
615
616
  /*
617
   *  Everything else should be real options
618
   */
619
37.2k
  if ((data_len < 2) || ((size_t) (data[1] + 2) > data_len)) {
620
2.13k
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
621
2.13k
    return -1;
622
2.13k
  }
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
35.1k
  next = data + 2 + data[1];
643
35.1k
  if ((data[1] > 0) && (next < end) && (next[0] == data[0])) {
644
2.76k
    uint8_t *q;
645
2.76k
    fr_dict_attr_t const *da;
646
647
2.76k
    q = concat_buffer;
648
649
8.76k
    for (next = data; next < end; next += 2 + next[1]) {
650
8.60k
      if (next[0] != data[0]) break;
651
6.17k
      if ((end - next) < 2) return -1;
652
6.11k
      if ((next + 2 + next[1]) > end) return -1;
653
654
5.99k
      if ((size_t) (q + next[1] - concat_buffer) > sizeof(concat_buffer)) return -1;
655
656
5.99k
      memcpy(q, next + 2, next[1]);
657
5.99k
      q += next[1];
658
5.99k
    }
659
660
2.58k
    da = fr_dict_attr_child_by_num(packet_ctx->root, p[0]);
661
2.58k
    if (!da) {
662
291
      da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, packet_ctx->root, p[0]);
663
291
      if (!da) return -1;
664
665
291
      slen = fr_pair_raw_from_network(ctx, out, da, concat_buffer, q - concat_buffer);
666
667
2.29k
    } else if (da->type == FR_TYPE_VSA) {
668
474
      slen = decode_vsa(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
669
670
1.82k
    } else if (da->type == FR_TYPE_TLV) {
671
454
      slen = fr_pair_tlvs_from_network(ctx, out, da, concat_buffer, q - concat_buffer,
672
454
               packet_ctx, decode_option, verify_tlvs, true);
673
674
1.37k
    } else if (da->flags.array) {
675
681
      slen = fr_pair_array_from_network(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx, decode_value);
676
689
    } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
677
54
      slen = fr_pair_dns_labels_from_network(ctx, out, da, concat_buffer, concat_buffer,
678
54
                     q - concat_buffer, NULL, true);
679
680
635
    } else {
681
635
      slen = decode_value(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
682
635
    }
683
2.58k
    if (slen < 0) return slen;
684
685
    /*
686
     *  The actual amount of data we decoded, including the various headers.
687
     */
688
2.47k
    FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %zu byte(s)", slen, (size_t) (next - data));
689
2.47k
    return next - data;
690
2.58k
  }
691
692
32.3k
  slen = decode_option(ctx, out, packet_ctx->root, data, data[1] + 2, decode_ctx);
693
32.3k
  if (slen < 0) return slen;
694
695
32.3k
  FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %u byte(s)", slen, (unsigned int) data[1] + 2);
696
32.3k
  return data[1] + 2;
697
32.3k
}
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
4.64k
{
702
4.64k
  ssize_t slen;
703
4.64k
  uint8_t const *attr, *end;
704
705
4.64k
  fr_dhcpv4_ctx_t decode_ctx = {
706
4.64k
    .root = fr_dict_root(dict_dhcpv4)
707
4.64k
  };
708
709
4.64k
  fr_assert(dict_dhcpv4 != NULL);
710
711
4.64k
  decode_ctx.tmp_ctx = talloc(ctx, uint8_t);
712
713
4.64k
  attr = data;
714
4.64k
  end = data + data_len;
715
716
38.5k
  while (attr < end) {
717
36.2k
    slen = fr_dhcpv4_decode_option(ctx, out, attr, (end - attr), &decode_ctx);
718
36.2k
    if (slen < 0) {
719
2.36k
      talloc_free(decode_ctx.tmp_ctx);
720
2.36k
      return slen;
721
2.36k
    }
722
723
    /*
724
     *  If slen is larger than the room in the packet,
725
     *  all kinds of bad things happen.
726
     */
727
33.9k
     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
33.9k
    attr += slen;
733
33.9k
    talloc_free_children(decode_ctx.tmp_ctx);
734
33.9k
  }
735
736
4.64k
  talloc_free(decode_ctx.tmp_ctx);
737
2.28k
  return data_len;
738
4.64k
}
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
1.23k
{
744
1.23k
  fr_dhcpv4_ctx_t *test_ctx;
745
746
1.23k
  test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t);
747
1.23k
  test_ctx->tmp_ctx = talloc(test_ctx, uint8_t);
748
1.23k
  test_ctx->root = root_da ? root_da : fr_dict_root(dict_dhcpv4);
749
750
1.23k
  *out = test_ctx;
751
752
1.23k
  return 0;
753
1.23k
}
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
1.23k
{
759
1.23k
  unsigned int  code;
760
761
1.23k
  if (!fr_dhcpv4_ok(data, data_len, NULL, NULL)) return -1;
762
763
200
  if (fr_dhcpv4_decode(ctx, out, data, data_len, &code) < 0) return -1;
764
765
125
  return data_len;
766
200
}
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
};