Coverage Report

Created: 2026-01-17 07:10

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: b1ea167299a685e0a27776663cbedc1f91b58187 $
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
7.75k
{
43
7.75k
  uint8_t const *p = data;
44
7.75k
  uint8_t const *end = data + data_len;
45
46
18.6k
  while (p < end) {
47
13.2k
    if ((end - p) < 2) return false;
48
49
11.9k
    if ((p + p[1]) > end) return false;
50
51
10.8k
    p += 2 + p[1];
52
10.8k
  }
53
54
5.38k
  return true;
55
7.75k
}
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.51k
{
61
1.51k
  return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, verify_tlvs, true);
62
1.51k
}
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
34.2k
{
74
34.2k
  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
34.2k
  if ((parent->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(parent)) {
80
927
    return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false);
81
927
  }
82
83
33.3k
  return decode_value(ctx, out, parent, data, data_len, decode_ctx);
84
34.2k
}
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
94.9k
{
92
94.9k
  ssize_t slen;
93
94.9k
  fr_pair_t *vp;
94
94.9k
  uint8_t const *p = data;
95
94.9k
  uint8_t const *end = data + data_len;
96
94.9k
  bool exact = !da->flags.array;
97
98
94.9k
  FR_PROTO_TRACE("%s called to parse %zu bytes from %s", __FUNCTION__, data_len, da->name);
99
94.9k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
100
101
  /*
102
   *  Structs create their own VP wrapper.
103
   */
104
94.9k
  if (da->type == FR_TYPE_STRUCT) {
105
23.5k
    slen = fr_struct_from_network(ctx, out, da, data, data_len,
106
23.5k
                decode_ctx, decode_value_trampoline, decode_tlv_trampoline);
107
23.5k
    if (slen < 0) return slen;
108
109
15.5k
    if (!exact) return slen;
110
111
7.32k
    return data_len;
112
15.5k
  }
113
114
  /*
115
   *  These are always raw.
116
   */
117
71.4k
  if (da->flags.is_unknown) {
118
0
    return fr_pair_raw_from_network(ctx, out, da, data, data_len);
119
0
  }
120
121
71.4k
  vp = fr_pair_afrom_da(ctx, da);
122
71.4k
  if (!vp) return PAIR_DECODE_OOM;
123
71.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
71.4k
  if (data_len == 0) {
130
8.50k
    if (da->type == FR_TYPE_BOOL) {
131
737
      vp->vp_bool = true;
132
737
      goto finish;
133
737
    }
134
135
7.77k
    if ((da->type == FR_TYPE_OCTETS) || (da->type == FR_TYPE_STRING)) {
136
3.29k
      goto finish;
137
3.29k
    }
138
139
7.77k
    talloc_free(vp);
140
4.47k
    return fr_pair_raw_from_network(ctx, out, da, data, 0);
141
7.77k
  }
142
143
62.9k
  switch (vp->vp_type) {
144
10.6k
  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
10.6k
    end = p + 1;
153
154
10.6k
    fr_assert(da->parent->flags.is_root);
155
156
10.6k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da->parent,
157
10.6k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
158
10.6k
    if (slen <= 0) goto raw;
159
160
10.6k
    p++;
161
10.6k
    break;
162
163
  /*
164
   *  Doesn't include scope, whereas the generic format can.
165
   */
166
1.60k
  case FR_TYPE_IPV6_ADDR:
167
1.60k
    slen = fr_value_box_ipaddr_from_network(&vp->data, da->type, da,
168
1.60k
              128, p, (size_t) (end - p),
169
1.60k
              exact, true);
170
1.60k
    if (slen < 0) goto raw;
171
744
    fr_assert(slen == sizeof(vp->vp_ipv6addr));
172
173
744
    p += sizeof(vp->vp_ipv6addr);
174
744
    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
8.01k
  case FR_TYPE_IPV4_PREFIX:
198
8.01k
    fr_value_box_init(&vp->data, FR_TYPE_IPV4_PREFIX, vp->da, true);
199
8.01k
    vp->vp_ip.af = AF_INET;
200
201
    /*
202
     *  4 octets of address
203
     *  4 octets of mask
204
     */
205
8.01k
    if (fr_dhcpv4_flag_prefix_split(da)) {
206
2.55k
      uint32_t ipaddr, mask;
207
208
2.55k
      if (data_len < 8) goto raw;
209
210
1.54k
      ipaddr = fr_nbo_to_uint32(p);
211
1.54k
      mask = fr_nbo_to_uint32(p + 4);
212
1.54k
      p += 8;
213
214
      /*
215
       *  0/0 means a prefix of 0, too.
216
       */
217
1.54k
      if (!mask) {
218
287
        break;
219
287
      }
220
221
      /*
222
       *  Try to figure out the prefix value from the mask.
223
       */
224
37.1k
      while (mask) {
225
35.8k
        vp->vp_ip.prefix++;
226
35.8k
        mask <<= 1;
227
35.8k
      }
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
1.25k
      mask = ~(uint32_t) 0;
235
1.25k
      mask <<= (32 - vp->vp_ip.prefix);
236
237
1.25k
      vp->vp_ipv4addr = htonl(ipaddr & mask);
238
1.25k
      break;
239
1.54k
    }
240
241
5.45k
    if (fr_dhcpv4_flag_prefix_bits(vp->da)) {
242
5.45k
      size_t needs;
243
244
5.45k
      if ((data_len == 0) || (*p > 32)) goto raw;
245
246
4.54k
      needs = 1 + ((*p + 0x07) >> 3);
247
4.54k
      if (data_len < needs) goto raw;
248
249
      /*
250
       *  Don't do exact checks here, as the content is variable-sized.
251
       */
252
253
3.62k
      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
3.62k
      if (vp->vp_ip.prefix) {
259
2.15k
        uint32_t ipaddr, mask;
260
261
2.15k
        mask = ~(uint32_t) 0;
262
2.15k
        mask <<= (32 - vp->vp_ip.prefix);
263
264
2.15k
        if (*p > 24) {
265
353
          ipaddr = fr_nbo_to_uint32(p + 1);
266
267
1.79k
        } else if (*p > 16) {
268
416
          ipaddr = fr_nbo_to_uint24(p + 1);
269
416
          ipaddr <<= 8;
270
271
1.38k
        } else if (*p > 8) {
272
367
          ipaddr = fr_nbo_to_uint16(p + 1);
273
367
          ipaddr <<= 16;
274
275
1.01k
        } else { /* 1..8 */
276
1.01k
          ipaddr = p[1];
277
1.01k
          ipaddr <<= 24;
278
1.01k
        }
279
280
2.15k
        vp->vp_ipv4addr = htonl(ipaddr & mask);
281
2.15k
      } /* else *p==0, and we leave ipaddr set to zero */
282
283
3.62k
      p += needs;
284
3.62k
      break;
285
4.54k
    }
286
287
0
    FALL_THROUGH;
288
289
42.6k
  default:
290
42.6k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, da,
291
42.6k
             &FR_DBUFF_TMP(p, end - p), end - p, true);
292
42.6k
    if (slen < 0) {
293
14.8k
    raw:
294
14.8k
      FR_PROTO_TRACE("decoding as unknown type");
295
14.8k
      if (fr_pair_raw_afrom_pair(vp, p, (end - p)) < 0) {
296
0
        return -1;
297
0
      }
298
14.8k
      p = end;
299
14.8k
      break;
300
14.8k
    }
301
302
31.6k
    if (exact && (slen != (end - p))) {
303
98
      goto raw;
304
98
    }
305
306
31.5k
    p += (size_t) slen;
307
31.5k
    break;
308
62.9k
  }
309
310
66.9k
finish:
311
66.9k
  FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", (size_t) (p - data));
312
66.9k
  fr_pair_append(out, vp);
313
314
66.9k
  return p - data;
315
62.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
7.56k
{
372
7.56k
  ssize_t     len;
373
7.56k
  uint8_t     option_len;
374
7.56k
  uint32_t    pen;
375
7.56k
  fr_pair_t   *vp;
376
7.56k
  fr_dict_attr_t const  *vendor;
377
7.56k
  uint8_t const   *end = data + data_len;
378
7.56k
  uint8_t const   *p = data;
379
380
7.56k
  FR_PROTO_HEX_DUMP(data, data_len, "decode_vsa");
381
382
7.56k
  if (!fr_cond_assert_msg(parent->type == FR_TYPE_VSA,
383
7.56k
        "%s: Internal sanity check failed, attribute \"%s\" is not of type 'vsa'",
384
7.56k
        __FUNCTION__, parent->name)) return PAIR_DECODE_FATAL_ERROR;
385
386
9.54k
next:
387
  /*
388
   *  We need at least 4 (PEN) + 1 (data-len) + 1 (vendor option num) to be able to decode vendor
389
   *  specific attributes.  If we don't have that, then we return an error.  The caller will free
390
   *  the VSA, and create a "raw.VSA" attribute.
391
   */
392
9.54k
  if ((size_t)(end - p) < (sizeof(uint32_t) + 1 + 1)) {
393
5.09k
    return -1;
394
5.09k
  }
395
396
4.45k
  pen = fr_nbo_to_uint32(p);
397
398
  /*
399
   *  Verify that the parent (which should be a VSA)
400
   *  contains a fake attribute representing the vendor.
401
   *
402
   *  If it doesn't then this vendor is unknown, but we know
403
   *  vendor attributes have a standard format, so we can
404
   *  decode the data anyway.
405
   */
406
4.45k
  vendor = fr_dict_attr_child_by_num(parent, pen);
407
4.45k
  if (!vendor) {
408
3.66k
    fr_dict_attr_t *n;
409
410
3.66k
    n = fr_dict_attr_unknown_vendor_afrom_num(ctx, parent, pen);
411
3.66k
    if (!n) return PAIR_DECODE_OOM;
412
3.66k
    vendor = n;
413
3.66k
  }
414
4.45k
  p += sizeof(uint32_t);
415
416
4.45k
  FR_PROTO_TRACE("decode context %s -> %s", parent->name, vendor->name);
417
418
4.45k
  option_len = p[0];
419
4.45k
  if ((p + 1 + option_len) > end) {
420
969
    len = fr_pair_raw_from_network(ctx, out, vendor, p, end - p);
421
969
    if (len < 0) return len;
422
423
969
    return data_len + 2; /* decoded the whole thing */
424
969
  }
425
3.48k
  p++;
426
427
  /*
428
   *  Pathological case of no data.
429
   */
430
3.48k
  if (option_len == 0) goto next;
431
432
2.40k
  vp = fr_pair_find_by_da(out, NULL, vendor);
433
2.40k
  if (!vp) {
434
2.00k
    vp = fr_pair_afrom_da(ctx, vendor);
435
2.00k
    if (!vp) return PAIR_DECODE_FATAL_ERROR;
436
2.00k
    PAIR_ALLOCED(vp);
437
438
2.00k
    fr_pair_append(out, vp);
439
2.00k
  }
440
441
2.40k
  len = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, p, option_len, decode_ctx, decode_option, verify_tlvs, false);
442
2.40k
  if (len < 0) return len;
443
444
1.86k
  p += option_len;
445
1.86k
  if (p < end) goto next;
446
447
  /*
448
   *  Tell the caller we read all of it, even if we didn't.
449
   */
450
952
  return data_len + 2;
451
1.86k
}
452
453
454
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
455
            fr_dict_attr_t const *parent,
456
            uint8_t const *data, size_t const data_len, void *decode_ctx)
457
65.6k
{
458
65.6k
  unsigned int      option;
459
65.6k
  size_t      len;
460
65.6k
  ssize_t     slen;
461
65.6k
  fr_dict_attr_t const  *da;
462
65.6k
  fr_dhcpv4_ctx_t   *packet_ctx = decode_ctx;
463
464
#ifdef STATIC_ANALYZER
465
  if (!packet_ctx || !packet_ctx->tmp_ctx) return PAIR_DECODE_FATAL_ERROR;
466
#endif
467
468
65.6k
  fr_assert(parent != NULL);
469
470
  /*
471
   *      RFC 3046 is very specific about not allowing termination
472
   *      with a 255 sub-option. But it's required for decoding
473
   *      option 43, and vendors will probably screw it up
474
   *      anyway.
475
   *
476
   *      Similarly, option 0 is sometimes treated as
477
   *      "end of options".
478
   *
479
   *  @todo - this check is likely correct only when at the
480
   *  dhcpv4 root, OR inside of option 43.  It could be
481
   *  argued that it's wrong for all other TLVs.
482
   */
483
65.6k
  if ((data_len == 1) && ((data[0] == 0) || (data[1] == 255))) return data_len;
484
485
  /*
486
   *  Must have at least an option header.
487
   */
488
65.6k
  if (data_len < 2) {
489
0
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
490
0
    return -(data_len);
491
0
  }
492
493
65.6k
  option = data[0];
494
65.6k
  len = data[1];
495
65.6k
  if (len > (data_len - 2)) {
496
760
    fr_strerror_printf("%s: Option overflows input.  "
497
760
           "Optional length must be less than %zu bytes, got %zu bytes",
498
760
           __FUNCTION__, data_len - 2, len);
499
760
    return PAIR_DECODE_FATAL_ERROR;
500
760
  }
501
502
64.8k
  da = fr_dict_attr_child_by_num(parent, option);
503
64.8k
  if (!da) {
504
6.76k
    da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option);
505
6.76k
    if (!da) return PAIR_DECODE_OOM;
506
507
6.76k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
508
509
58.0k
  } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv4_flag_dns_label(da)) {
510
7.77k
    slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 2, data + 2, len, NULL, true);
511
512
50.3k
  } else if (da->flags.array) {
513
8.38k
    slen = fr_pair_array_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_value);
514
515
41.9k
  } else if (da->type == FR_TYPE_VSA) {
516
6.90k
    bool append = false;
517
6.90k
    fr_pair_t *vp;
518
519
6.90k
    vp = fr_pair_find_by_da(out, NULL, da);
520
6.90k
    if (!vp) {
521
4.23k
      vp = fr_pair_afrom_da(ctx, da);
522
4.23k
      if (!vp) return PAIR_DECODE_FATAL_ERROR;
523
4.23k
      PAIR_ALLOCED(vp);
524
525
4.23k
      append = true;
526
4.23k
    }
527
528
6.90k
    slen = decode_vsa(vp, &vp->vp_group, da, data + 2, len, decode_ctx);
529
6.90k
    if (append) {
530
4.23k
      if (slen < 0) {
531
3.74k
        TALLOC_FREE(vp);
532
3.74k
      } else {
533
487
        fr_pair_append(out, vp);
534
487
      }
535
4.23k
    }
536
537
35.0k
  } else if (da->type == FR_TYPE_TLV) {
538
3.77k
    slen = fr_pair_tlvs_from_network(ctx, out, da, data + 2, len, decode_ctx, decode_option, verify_tlvs, true);
539
540
31.2k
  } else {
541
31.2k
    slen = decode_value(ctx, out, da, data + 2, len, decode_ctx);
542
31.2k
  }
543
544
64.8k
  if (slen < 0) {
545
14.2k
    slen = fr_pair_raw_from_network(ctx, out, da, data + 2, len);
546
14.2k
    if (slen < 0) return slen;
547
14.2k
  }
548
549
64.8k
  return len + 2;
550
64.8k
}
551
552
/** Decode DHCP option
553
 *
554
 * @param[in] ctx context to alloc new attributes in.
555
 * @param[out] out    Where to write the decoded options.
556
 * @param[in] data    to parse.
557
 * @param[in] data_len    of data to parse.
558
 * @param[in] decode_ctx  Unused.
559
 */
560
ssize_t fr_dhcpv4_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
561
              uint8_t const *data, size_t data_len, void *decode_ctx)
562
71.7k
{
563
71.7k
  ssize_t     slen;
564
71.7k
  uint8_t const   *p = data, *end = data + data_len;
565
71.7k
  uint8_t const   *next;
566
71.7k
  fr_dhcpv4_ctx_t   *packet_ctx = decode_ctx;
567
568
71.7k
  FR_PROTO_TRACE("%s called to parse %zu byte(s)", __FUNCTION__, data_len);
569
570
71.7k
  if (data_len == 0) return 0;
571
572
71.7k
  FR_PROTO_HEX_DUMP(data, data_len, NULL);
573
574
  /*
575
   *  Padding / End of options
576
   */
577
71.7k
  if (p[0] == 0) {     /* 0x00 - Padding option    */
578
8.94k
    data_len = 1;     /* Walk over any consecutive 0x00 */
579
8.94k
    p++;        /* for efficiency     */
580
14.3k
    while ((p < end) && (p[0] == 0)) {
581
5.37k
      p++;
582
5.37k
      data_len ++;
583
5.37k
    }
584
8.94k
    return data_len;
585
8.94k
  }
586
62.7k
  if (p[0] == 255) return data_len;  /* 0xff - End of options signifier */
587
588
  /*
589
   *  Everything else should be real options
590
   */
591
61.4k
  if ((data_len < 2) || ((size_t) (data[1] + 2) > data_len)) {
592
2.18k
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
593
2.18k
    return -1;
594
2.18k
  }
595
596
  /*
597
   *  Check for multiple options of the same type, and concatenate their values together.
598
   *
599
   *  RFC 2131 Section 4.1 says:
600
   *
601
   *    The client concatenates the values of multiple
602
   *    instances of the same option into a single parameter
603
   *    list for configuration.
604
   *
605
   *  which presumably also means the same for the server on reception.
606
   *
607
   *  We therefore peek ahead, and concatenate the values into a temporary buffer.  The buffer is
608
   *  allocated only if necessary, and is re-used for the entire packet.
609
   *
610
   *  If the options are *not* consecutive, then we don't concatenate them.  Too bad for you!
611
   *
612
   *  Note that we don't (yet) do this for TLVs.
613
   */
614
59.2k
  next = data + 2 + data[1];
615
59.2k
  if ((data[1] > 0) && (next < end) && (next[0] == data[0])) {
616
3.74k
    uint8_t *q;
617
3.74k
    fr_dict_attr_t const *da;
618
619
3.74k
    q = concat_buffer;
620
621
12.6k
    for (next = data; next < end; next += 2 + next[1]) {
622
12.3k
      if (next >= end) return -1;
623
12.3k
      if (next[0] != data[0]) break;
624
9.09k
      if ((end - next) < 2) return -1;
625
9.03k
      if ((next + 2 + next[1]) > end) return -1;
626
627
8.88k
      if ((size_t) (q + next[1] - concat_buffer) > sizeof(concat_buffer)) return -1;
628
629
8.88k
      memcpy(q, next + 2, next[1]);
630
8.88k
      q += next[1];
631
8.88k
    }
632
633
3.52k
    if (q == concat_buffer) return 0;
634
635
3.52k
    da = fr_dict_attr_child_by_num(packet_ctx->root, p[0]);
636
3.52k
    if (!da) {
637
455
      da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, packet_ctx->root, p[0]);
638
455
      if (!da) return -1;
639
640
455
      slen = fr_pair_raw_from_network(ctx, out, da, concat_buffer, q - concat_buffer);
641
642
3.07k
    } else if (da->type == FR_TYPE_VSA) {
643
659
      slen = decode_vsa(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
644
645
2.41k
    } else if (da->type == FR_TYPE_TLV) {
646
567
      slen = fr_pair_tlvs_from_network(ctx, out, da, concat_buffer, q - concat_buffer,
647
567
               packet_ctx, decode_option, verify_tlvs, true);
648
649
1.84k
    } else if (da->flags.array) {
650
855
      slen = fr_pair_array_from_network(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx, decode_value);
651
652
989
    } else {
653
989
      slen = decode_value(ctx, out, da, concat_buffer, q - concat_buffer, packet_ctx);
654
989
    }
655
3.52k
    if (slen < 0) return slen;
656
657
    /*
658
     *  The actual amount of data we decoded, including the various headers.
659
     */
660
3.39k
    FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %zu byte(s)", slen, (size_t) (next - data));
661
3.39k
    return next - data;
662
3.52k
  }
663
664
55.5k
  slen = decode_option(ctx, out, packet_ctx->root, data, data[1] + 2, decode_ctx);
665
55.5k
  if (slen < 0) return slen;
666
667
55.5k
  FR_PROTO_TRACE("decoding option complete, %zd decoded, returning %u byte(s)", slen, (unsigned int) data[1] + 2);
668
55.5k
  return data[1] + 2;
669
55.5k
}
670
671
ssize_t fr_dhcpv4_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out,
672
         uint8_t const *data, size_t data_len)
673
3.95k
{
674
3.95k
  ssize_t slen;
675
3.95k
  uint8_t const *attr, *end;
676
677
3.95k
  fr_dhcpv4_ctx_t decode_ctx = {
678
3.95k
    .root = fr_dict_root(dict_dhcpv4)
679
3.95k
  };
680
681
3.95k
  fr_assert(dict_dhcpv4 != NULL);
682
683
3.95k
  decode_ctx.tmp_ctx = talloc(ctx, uint8_t);
684
685
3.95k
  attr = data;
686
3.95k
  end = data + data_len;
687
688
33.8k
  while (attr < end) {
689
32.0k
    slen = fr_dhcpv4_decode_option(ctx, out, attr, (end - attr), &decode_ctx);
690
32.0k
    if (slen < 0) {
691
2.15k
      talloc_free(decode_ctx.tmp_ctx);
692
2.15k
      return slen;
693
2.15k
    }
694
695
    /*
696
     *  If slen is larger than the room in the packet,
697
     *  all kinds of bad things happen.
698
     */
699
29.8k
     if (!fr_cond_assert(slen <= (end - attr))) {
700
0
      talloc_free(decode_ctx.tmp_ctx);
701
0
       return -slen - (attr - data);
702
0
     }
703
704
29.8k
    attr += slen;
705
29.8k
    talloc_free_children(decode_ctx.tmp_ctx);
706
29.8k
  }
707
708
3.95k
  talloc_free(decode_ctx.tmp_ctx);
709
1.80k
  return data_len;
710
3.95k
}
711
712
713
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
714
         fr_dict_attr_t const *root_da)
715
1.18k
{
716
1.18k
  fr_dhcpv4_ctx_t *test_ctx;
717
718
1.18k
  test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t);
719
1.18k
  test_ctx->tmp_ctx = talloc(test_ctx, uint8_t);
720
1.18k
  test_ctx->root = root_da ? root_da : fr_dict_root(dict_dhcpv4);
721
722
1.18k
  *out = test_ctx;
723
724
1.18k
  return 0;
725
1.18k
}
726
727
728
static ssize_t fr_dhcpv4_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out,
729
              uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
730
1.18k
{
731
1.18k
  unsigned int  code;
732
733
1.18k
  if (!fr_dhcpv4_ok(data, data_len, NULL, NULL)) return -1;
734
735
1.03k
  if (fr_dhcpv4_decode(ctx, out, data, data_len, &code) < 0) return -1;
736
737
639
  return data_len;
738
1.03k
}
739
740
static ssize_t decode_option_wrapper(TALLOC_CTX *ctx, fr_pair_list_t *out, NDEBUG_UNUSED fr_dict_attr_t const *parent,
741
              uint8_t const *data, size_t data_len, void *decode_ctx)
742
0
{
743
0
  fr_assert(parent == fr_dict_root(dict_dhcpv4));
744
745
0
  return fr_dhcpv4_decode_option(ctx, out, data, data_len, decode_ctx);
746
0
}
747
748
/*
749
 *  Test points
750
 */
751
extern fr_test_point_pair_decode_t dhcpv4_tp_decode_pair;
752
fr_test_point_pair_decode_t dhcpv4_tp_decode_pair = {
753
  .test_ctx = decode_test_ctx,
754
  .func   = decode_option_wrapper
755
};
756
757
extern fr_test_point_proto_decode_t dhcpv4_tp_decode_proto;
758
fr_test_point_proto_decode_t dhcpv4_tp_decode_proto = {
759
  .test_ctx = decode_test_ctx,
760
  .func   = fr_dhcpv4_decode_proto
761
};