/src/freeradius-server/src/lib/util/struct.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 encode / decode structures on the wire |
18 | | * |
19 | | * @file src/lib/util/struct.c |
20 | | * |
21 | | * @copyright 2018 The FreeRADIUS server project |
22 | | * @copyright 2018 Alan DeKok (aland@freeradius.org) |
23 | | */ |
24 | | RCSID("$Id: 2d6c4ef4935c7ff5015b89802b5aaeec8ba9e9ec $") |
25 | | |
26 | | #include <freeradius-devel/util/struct.h> |
27 | | #include <freeradius-devel/io/pair.h> |
28 | | |
29 | | /** Convert a STRUCT to one or more VPs |
30 | | * |
31 | | */ |
32 | | ssize_t fr_struct_from_network(TALLOC_CTX *ctx, fr_pair_list_t *out, |
33 | | fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, |
34 | | void *decode_ctx, |
35 | | fr_pair_decode_value_t decode_value, fr_pair_decode_value_t decode_tlv) |
36 | 78.2k | { |
37 | 78.2k | unsigned int child_num; |
38 | 78.2k | uint8_t const *p = data, *end = data + data_len; |
39 | 78.2k | fr_dict_attr_t const *child, *substruct_da; |
40 | 78.2k | fr_pair_list_t child_list_head; |
41 | 78.2k | fr_pair_list_t *child_list; |
42 | 78.2k | fr_pair_t *vp, *key_vp, *struct_vp = NULL; |
43 | 78.2k | unsigned int offset = 0; |
44 | 78.2k | TALLOC_CTX *child_ctx; |
45 | 78.2k | ssize_t slen; |
46 | 78.2k | size_t child_length; |
47 | | |
48 | 78.2k | if (data_len == 0) { |
49 | 2.07k | fr_strerror_const("struct decoder was passed zero bytes of data"); |
50 | 2.07k | return -1; /* at least one byte of data */ |
51 | 2.07k | } |
52 | | |
53 | 76.2k | FR_PROTO_TRACE("Decoding struct %s", parent->name); |
54 | 76.2k | FR_PROTO_HEX_DUMP(data, data_len, "fr_struct_from_network"); |
55 | | |
56 | | /* |
57 | | * Start a child list. |
58 | | */ |
59 | 76.2k | fr_assert(parent->type == FR_TYPE_STRUCT); |
60 | | |
61 | 76.2k | struct_vp = fr_pair_afrom_da(ctx, parent); |
62 | 76.2k | if (!struct_vp) { |
63 | 0 | return PAIR_DECODE_OOM; |
64 | 0 | } |
65 | 76.2k | PAIR_ALLOCED(struct_vp); |
66 | | |
67 | 76.2k | fr_pair_list_init(&child_list_head); /* still used elsewhere */ |
68 | 76.2k | child_list = &struct_vp->vp_group; |
69 | 76.2k | child_ctx = struct_vp; |
70 | 76.2k | key_vp = NULL; |
71 | | |
72 | | /* |
73 | | * Simplify the code by having a generic decode routine. |
74 | | */ |
75 | 76.2k | if (!decode_value) decode_value = fr_pair_decode_value; |
76 | | |
77 | | /* |
78 | | * Decode structs with length prefixes. |
79 | | */ |
80 | 76.2k | if (da_is_length_field(parent)) { |
81 | 3.41k | size_t claimed_len, field_len, calc_len; |
82 | | |
83 | | /* |
84 | | * Set how many bytes there are in the "length" field. |
85 | | */ |
86 | 3.41k | if (da_is_length_field8(parent)) { |
87 | 2.07k | field_len = 1; |
88 | 2.07k | } else { |
89 | 1.33k | fr_assert(da_is_length_field16(parent)); |
90 | 1.33k | field_len = 2; |
91 | 1.33k | } |
92 | | |
93 | 3.41k | if ((size_t) (end - p) < field_len) { |
94 | 1 | FR_PROTO_TRACE("Insufficient room for length field"); |
95 | | |
96 | 986 | invalid_struct: |
97 | | /* |
98 | | * Some field could not be decoded. Nuke the entire struct, and just make the |
99 | | * whole thing "raw". |
100 | | */ |
101 | 986 | TALLOC_FREE(struct_vp); |
102 | | |
103 | 986 | slen = fr_pair_raw_from_network(ctx, out, parent, data, data_len); |
104 | 986 | if (slen < 0) return slen; |
105 | 99 | return data_len; |
106 | 986 | } |
107 | | |
108 | 3.41k | claimed_len = p[0]; |
109 | 3.41k | if (field_len > 1) { |
110 | 1.33k | claimed_len <<= 8; |
111 | 1.33k | claimed_len |= p[1]; |
112 | 1.33k | } |
113 | 3.41k | p += field_len; |
114 | | |
115 | 3.41k | if (claimed_len < da_length_offset(parent)) { |
116 | 8 | FR_PROTO_TRACE("Length header (%zu) is smaller than minimum value (%u)", |
117 | 8 | claimed_len, parent->flags.type_size); |
118 | 8 | goto invalid_struct; |
119 | 8 | } |
120 | | |
121 | | /* |
122 | | * Get the calculated length of the actual data. |
123 | | */ |
124 | 3.40k | calc_len = claimed_len - da_length_offset(parent); |
125 | | |
126 | 3.40k | if (calc_len > (size_t) (end - p)) { |
127 | 977 | FR_PROTO_TRACE("Length header (%zu) is larger than remaining data (%zu)", |
128 | 977 | claimed_len + field_len, (size_t) (end - p)); |
129 | 977 | goto invalid_struct; |
130 | 977 | } |
131 | | |
132 | | /* |
133 | | * Limit the size of the decoded structure to the correct length. |
134 | | */ |
135 | 2.42k | data_len = calc_len; |
136 | 2.42k | end = p + data_len; |
137 | 2.42k | } |
138 | | |
139 | | /* |
140 | | * @todo - If the struct is truncated on a MEMBER boundary, we silently omit |
141 | | * the trailing members. Maybe this should be an error? |
142 | | */ |
143 | 75.2k | for (child_num = 1; |
144 | 342k | (p < end) && (child = fr_dict_attr_child_by_num(parent, child_num)) != NULL; |
145 | 275k | child_num++) { |
146 | 275k | FR_PROTO_TRACE("Decoding struct %s child %s (%d)", parent->name, child->name, child->attr); |
147 | 275k | FR_PROTO_HEX_DUMP(p, (end - p), "fr_struct_from_network - remaining %zu", (size_t) (end - p)); |
148 | | |
149 | | /* |
150 | | * Check for bit fields. |
151 | | */ |
152 | 275k | if (da_is_bit_field(child)) { |
153 | 51.3k | uint8_t array[8]; |
154 | 51.3k | unsigned int num_bits; |
155 | 51.3k | uint64_t value; |
156 | | |
157 | 51.3k | num_bits = offset + child->flags.length; |
158 | 51.3k | if ((size_t)(end - p) < fr_bytes_from_bits(num_bits)) { |
159 | 694 | FR_PROTO_TRACE("not enough data for bit decoder?"); |
160 | 694 | goto remainder_raw; |
161 | 694 | } |
162 | | |
163 | 50.6k | memset(array, 0, sizeof(array)); |
164 | 50.6k | memcpy(&array[0], p, fr_bytes_from_bits(num_bits)); |
165 | | |
166 | 50.6k | if (offset > 0) array[0] &= (1 << (8 - offset)) - 1; /* mask off bits we don't care about */ |
167 | | |
168 | 50.6k | memcpy(&value, &array[0], sizeof(value)); |
169 | 50.6k | value = htonll(value); |
170 | 50.6k | value >>= (8 - offset); /* move it to the lower bits */ |
171 | 50.6k | value >>= (56 - child->flags.length); |
172 | | |
173 | 50.6k | vp = fr_pair_afrom_da(child_ctx, child); |
174 | 50.6k | if (!vp) { |
175 | 0 | FR_PROTO_TRACE("fr_struct_from_network - failed allocating child VP"); |
176 | 0 | oom: |
177 | 0 | talloc_free(struct_vp); |
178 | 0 | return PAIR_DECODE_OOM; |
179 | 0 | } |
180 | 50.6k | PAIR_ALLOCED(vp); |
181 | | |
182 | 50.6k | switch (child->type) { |
183 | 30.0k | case FR_TYPE_BOOL: |
184 | 30.0k | vp->vp_bool = value; |
185 | 30.0k | break; |
186 | | |
187 | 18.5k | case FR_TYPE_UINT8: |
188 | 18.5k | vp->vp_uint8 = value; |
189 | 18.5k | break; |
190 | | |
191 | 1.53k | case FR_TYPE_UINT16: |
192 | 1.53k | vp->vp_uint16 = value; |
193 | 1.53k | break; |
194 | | |
195 | 382 | case FR_TYPE_UINT32: |
196 | 382 | vp->vp_uint32 = value; |
197 | 382 | break; |
198 | | |
199 | 100 | case FR_TYPE_UINT64: |
200 | 100 | vp->vp_uint64 = value; |
201 | 100 | break; |
202 | | |
203 | 0 | default: |
204 | 0 | FR_PROTO_TRACE("Can't decode unknown type?"); |
205 | 0 | goto remainder_raw; |
206 | 50.6k | } |
207 | | |
208 | 50.6k | vp->vp_tainted = true; |
209 | 50.6k | fr_pair_append(child_list, vp); |
210 | | |
211 | 50.6k | p += (num_bits >> 3); /* go to the LAST bit, not the byte AFTER the last bit */ |
212 | 50.6k | offset = num_bits & 0x07; |
213 | 50.6k | continue; |
214 | 50.6k | } |
215 | | |
216 | 223k | fr_assert(offset == 0); |
217 | 223k | offset = 0; /* reset for non-bit-field attributes */ |
218 | | |
219 | | /* |
220 | | * The child is either unknown width, OR known width with a length that is too large for |
221 | | * the "length" field, OR is known width via some kind of protocol-specific length header. |
222 | | */ |
223 | 223k | if (!child->flags.length || child->flags.array) { |
224 | 57.4k | child_length = end - p; |
225 | | |
226 | 166k | } else { |
227 | 166k | child_length = child->flags.length; |
228 | | |
229 | | /* |
230 | | * If this field overflows the input, then *all* |
231 | | * of the input is suspect. |
232 | | */ |
233 | 166k | if (child_length > (size_t) (end - p)) { |
234 | 2.56k | child_length = (size_t) (end - p); |
235 | 2.56k | } |
236 | 166k | } |
237 | | |
238 | | /* |
239 | | * We only allow a limited number of data types |
240 | | * inside of a struct. |
241 | | */ |
242 | 223k | switch (child->type) { |
243 | 0 | case FR_TYPE_INTERNAL: |
244 | 0 | case FR_TYPE_NULL: |
245 | 0 | FR_PROTO_TRACE("fr_struct_from_network - unknown child type"); |
246 | 0 | goto remainder_raw; |
247 | | |
248 | 1.85k | case FR_TYPE_STRUCT: |
249 | 1.85k | case FR_TYPE_VSA: |
250 | 1.85k | case FR_TYPE_VENDOR: |
251 | 2.22k | case FR_TYPE_GROUP: |
252 | 218k | case FR_TYPE_LEAF: |
253 | 218k | break; |
254 | | |
255 | | /* |
256 | | * Decode child TLVs, according to the parent attribute. |
257 | | */ |
258 | 691 | case FR_TYPE_TLV: |
259 | 691 | fr_assert(!key_vp); |
260 | | |
261 | 691 | if (!decode_tlv) { |
262 | 0 | fr_strerror_const("Decoding TLVs requires a decode_tlv() function to be passed"); |
263 | 0 | talloc_free(struct_vp); |
264 | 0 | return -(p - data); |
265 | 0 | } |
266 | | |
267 | | /* |
268 | | * Decode all of the remaining data as |
269 | | * TLVs. Any malformed TLVs are appended |
270 | | * as raw VP. |
271 | | */ |
272 | 959 | while (p < end) { |
273 | 691 | slen = decode_tlv(child_ctx, child_list, child, p, end - p, decode_ctx); |
274 | 691 | if (slen < 0) { |
275 | 423 | FR_PROTO_TRACE("failed decoding TLV?"); |
276 | 423 | goto remainder_raw; |
277 | 423 | } |
278 | 268 | p += slen; |
279 | 268 | } |
280 | | |
281 | 268 | goto done; |
282 | | |
283 | | /* |
284 | | * The child is a union, it MUST be at the end of |
285 | | * the struct, and we must have seen a key before |
286 | | * we reach the union. See dict_tokenize. |
287 | | */ |
288 | 4.48k | case FR_TYPE_UNION: |
289 | | /* |
290 | | * Create the union wrapper, and reset the child_ctx and child_list to it. |
291 | | */ |
292 | 4.48k | vp = fr_pair_afrom_da(child_ctx, child); |
293 | 4.48k | if (!vp) goto oom; |
294 | 4.48k | PAIR_ALLOCED(vp); |
295 | | |
296 | 4.48k | fr_pair_append(child_list, vp); |
297 | 4.48k | substruct_da = child; |
298 | 4.48k | child_ctx = vp; |
299 | 4.48k | child_list = &vp->vp_group; |
300 | | |
301 | 4.48k | fr_assert(!fr_dict_attr_child_by_num(parent, child_num + 1)); /* has to be the last one */ |
302 | 4.48k | if (!key_vp) { |
303 | 1.11k | remainder_raw: |
304 | 1.11k | child_length = (size_t) (end - p); |
305 | 1.11k | goto raw; |
306 | 0 | } |
307 | | |
308 | 4.48k | goto substruct; |
309 | 223k | } |
310 | | |
311 | | /* |
312 | | * Magic values get the callback called. |
313 | | * |
314 | | * @todo - if this is an array of DNS labels, we |
315 | | * need to do decompression checks on the entire |
316 | | * block, and then decode each field |
317 | | * individually. |
318 | | */ |
319 | 218k | if (child->flags.array) { |
320 | 2.55k | slen = fr_pair_array_from_network(child_ctx, child_list, child, p, child_length, decode_ctx, decode_value); |
321 | 216k | } else { |
322 | 216k | slen = decode_value(child_ctx, child_list, child, p, child_length, decode_ctx); |
323 | 216k | } |
324 | 218k | if (slen < 0) { |
325 | 2.46k | FR_PROTO_TRACE("Failed decoding value"); |
326 | | |
327 | 3.58k | raw: |
328 | 3.58k | slen = fr_pair_raw_from_network(child_ctx, child_list, child, p, child_length); |
329 | 3.58k | if (slen < 0) { |
330 | 3.58k | talloc_free(struct_vp); |
331 | 3.58k | return slen; |
332 | 3.58k | } |
333 | 3.58k | } |
334 | | |
335 | 216k | p += slen; /* not always the same as child->flags.length */ |
336 | | |
337 | 216k | if (fr_dict_attr_is_key_field(child)) { |
338 | 4.88k | fr_assert(!key_vp); |
339 | 4.88k | key_vp = fr_pair_list_tail(child_list); |
340 | 4.88k | } |
341 | 216k | } |
342 | | |
343 | | /* |
344 | | * Is there a substructure after this one? If so, go |
345 | | * decode it. |
346 | | */ |
347 | 66.9k | if (key_vp) { |
348 | 393 | fr_dict_enum_value_t const *enumv; |
349 | | |
350 | 393 | substruct_da = key_vp->da; |
351 | | |
352 | 4.87k | substruct: |
353 | 4.87k | child = NULL; |
354 | | |
355 | 4.87k | FR_PROTO_TRACE("Key %s", key_vp->da->name); |
356 | 4.87k | FR_PROTO_HEX_DUMP(p, (end - p), "fr_struct_from_network - child structure"); |
357 | | |
358 | | /* |
359 | | * Nothing more to decode, don't decode it. |
360 | | */ |
361 | 4.87k | if (p >= end) { |
362 | 393 | FR_PROTO_TRACE("Expected substruct, but there is none. We're done decoding this structure"); |
363 | 393 | goto done; |
364 | 393 | } |
365 | | |
366 | 4.48k | enumv = fr_dict_enum_by_value(key_vp->da, &key_vp->data); |
367 | 4.48k | if (enumv) child = fr_dict_enum_attr_ref(enumv); |
368 | | |
369 | 4.48k | if (!child) { |
370 | | /* |
371 | | * Always encode the unknown child as attribute number 0. Since the unknown |
372 | | * children have no "real" number, and are all unique da's, they are |
373 | | * incomparable. And thus can all be given the same number. |
374 | | */ |
375 | 1.59k | uint64_t attr; |
376 | | |
377 | 1.59k | FR_PROTO_TRACE("No matching child structure found"); |
378 | | |
379 | 1.88k | unknown_child: |
380 | 1.88k | attr = 0; |
381 | | |
382 | | /* |
383 | | * But if we have a key field, the unknown attribute number is taken from the |
384 | | * from the key field. |
385 | | */ |
386 | 1.88k | if (fr_type_is_integer(key_vp->vp_type)) { |
387 | 1.88k | attr = fr_value_box_as_uint64(&key_vp->data); |
388 | 1.88k | } |
389 | | |
390 | 1.88k | child = fr_dict_attr_unknown_raw_afrom_num(child_ctx, substruct_da, attr); |
391 | 1.88k | if (!child) { |
392 | 0 | FR_PROTO_TRACE("failed allocating unknown child for key VP %s - %s", |
393 | 0 | key_vp->da->name, fr_strerror()); |
394 | 0 | goto oom; |
395 | 0 | } |
396 | | |
397 | 1.88k | slen = fr_pair_raw_from_network(child_ctx, child_list, child, p, end - p); |
398 | 1.88k | if (slen < 0) { |
399 | 0 | FR_PROTO_TRACE("Failed creating raw VP from malformed or unknown substruct for child %s", child->name); |
400 | 0 | fr_dict_attr_unknown_free(&child); |
401 | 0 | return slen; |
402 | 0 | } |
403 | | |
404 | 1.88k | p = end; |
405 | | |
406 | 2.88k | } else { |
407 | 2.88k | switch (child->type) { |
408 | 2.88k | case FR_TYPE_STRUCT: |
409 | 2.88k | FR_PROTO_TRACE("Decoding child structure %s", child->name); |
410 | 2.88k | slen = fr_struct_from_network(child_ctx, child_list, child, p, end - p, |
411 | 2.88k | decode_ctx, decode_value, decode_tlv); |
412 | 2.88k | break; |
413 | | |
414 | 0 | case FR_TYPE_TLV: |
415 | 0 | if (!decode_tlv) { |
416 | 0 | FR_PROTO_TRACE("Failed to pass decode_tlv() for child tlv %s", child->name); |
417 | 0 | goto unknown_child; |
418 | 0 | } |
419 | | |
420 | 0 | FR_PROTO_TRACE("Decoding child tlv %s", child->name); |
421 | |
|
422 | 0 | slen = decode_tlv(child_ctx, child_list, child, p, end - p, decode_ctx); |
423 | 0 | break; |
424 | | |
425 | 0 | case FR_TYPE_LEAF: |
426 | 0 | fr_assert(decode_value); |
427 | |
|
428 | 0 | FR_PROTO_TRACE("Decoding child %s", child->name); |
429 | | |
430 | | /* |
431 | | * @todo - unify this code with the code above, but for now copying is |
432 | | * easier. |
433 | | */ |
434 | | |
435 | | /* |
436 | | * The child is either unknown width, OR known width with a length that is too large for |
437 | | * the "length" field, OR is known width via some kind of protocol-specific length header. |
438 | | */ |
439 | 0 | if (!child->flags.length || child->flags.array) { |
440 | 0 | child_length = end - p; |
441 | |
|
442 | 0 | } else { |
443 | 0 | child_length = child->flags.length; |
444 | | |
445 | | /* |
446 | | * If this field overflows the input, then *all* |
447 | | * of the input is suspect. |
448 | | */ |
449 | 0 | if (child_length > (size_t) (end - p)) { |
450 | 0 | FR_PROTO_TRACE("fr_struct_from_network - child length %zu overflows buffer", child_length); |
451 | 0 | goto remainder_raw; |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | 0 | if (child->flags.array) { |
456 | 0 | slen = fr_pair_array_from_network(child_ctx, child_list, child, p, child_length, decode_ctx, decode_value); |
457 | 0 | } else { |
458 | 0 | slen = decode_value(child_ctx, child_list, child, p, child_length, decode_ctx); |
459 | 0 | } |
460 | 0 | break; |
461 | | |
462 | 0 | default: |
463 | 0 | FR_PROTO_TRACE("Unknown data type %s in child %s", fr_type_to_str(child->type), child->name); |
464 | 0 | goto unknown_child; |
465 | 2.88k | } |
466 | | |
467 | 2.88k | if (slen <= 0) { |
468 | 289 | FR_PROTO_TRACE("failed decoding child %s", child->name); |
469 | 289 | goto unknown_child; |
470 | 289 | } |
471 | 2.59k | p += slen; |
472 | 2.59k | } |
473 | | |
474 | 4.48k | fr_dict_attr_unknown_free(&child); |
475 | 4.48k | } |
476 | | |
477 | 71.6k | done: |
478 | 71.6k | fr_assert(struct_vp != NULL); |
479 | 71.6k | fr_pair_append(out, struct_vp); |
480 | | |
481 | 71.6k | FR_PROTO_TRACE("used %zu bytes", data_len); |
482 | 71.6k | return p - data; |
483 | 66.9k | } |
484 | | |
485 | | |
486 | | /** Put bits into an output dbuff |
487 | | * |
488 | | * @param dbuff where the bytes go |
489 | | * @param p where leftover bits go |
490 | | * @param start_bit start bit in the dbuff where the data goes, 0..7 |
491 | | * @param num_bits number of bits to write to the output, 0..55 |
492 | | * @param data data to write, all in the lower "num_bits" of the uint64_t variable |
493 | | * @return |
494 | | * >= 0 the next value to pass in for start_bit |
495 | | * < 0 no space or invalid start_bit or num_bits parameter |
496 | | */ |
497 | | static int put_bits_dbuff(fr_dbuff_t *dbuff, uint8_t *p, int start_bit, uint8_t num_bits, uint64_t data) |
498 | 0 | { |
499 | 0 | uint64_t used_bits; |
500 | |
|
501 | 0 | if (start_bit < 0 || start_bit > 7) return -1; |
502 | 0 | if (num_bits < 1 || num_bits > 56) return -1; |
503 | | |
504 | | /* Get bits buffered in *p */ |
505 | 0 | used_bits = *p & (-256 >> start_bit); |
506 | | |
507 | | /* Mask out all but the least significant num_bits bits of data */ |
508 | 0 | data &= (((uint64_t) 1) << num_bits) - 1; |
509 | | |
510 | | /* Move it towards the most significant end and put used_bits at the top */ |
511 | 0 | data <<= (64 - (start_bit + num_bits)); |
512 | 0 | data |= used_bits << 56; |
513 | |
|
514 | 0 | data = htonll(data); |
515 | |
|
516 | 0 | start_bit += num_bits; |
517 | 0 | if (start_bit > 7) FR_DBUFF_IN_MEMCPY_RETURN(dbuff, (uint8_t const *) &data, (size_t)(start_bit / 8)); |
518 | | |
519 | 0 | *p = ((uint8_t *) &data)[start_bit / 8]; |
520 | 0 | return start_bit % 8; |
521 | 0 | } |
522 | | |
523 | | static int8_t pair_sort_increasing(void const *a, void const *b) |
524 | 0 | { |
525 | 0 | fr_pair_t const *my_a = a; |
526 | 0 | fr_pair_t const *my_b = b; |
527 | 0 | int8_t ret; |
528 | | |
529 | | /* |
530 | | * Deeper attributes come later in the list. |
531 | | */ |
532 | 0 | ret = CMP_PREFER_SMALLER(my_a->da->depth, my_b->da->depth); |
533 | 0 | if (ret != 0) return ret; |
534 | | |
535 | 0 | return CMP_PREFER_SMALLER(my_a->da->attr, my_b->da->attr); |
536 | 0 | } |
537 | | |
538 | | static void *struct_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx) |
539 | 0 | { |
540 | 0 | fr_pair_t *c = current; |
541 | 0 | fr_dict_attr_t *parent = talloc_get_type_abort(uctx, fr_dict_attr_t); |
542 | |
|
543 | 0 | while ((c = fr_dlist_next(cursor->dlist, c))) { |
544 | 0 | PAIR_VERIFY(c); |
545 | |
|
546 | 0 | if (c->da->dict != parent->dict || c->da->flags.internal) continue; |
547 | 0 | break; |
548 | 0 | } |
549 | |
|
550 | 0 | return c; |
551 | 0 | } |
552 | | |
553 | | static ssize_t encode_tlv(fr_dbuff_t *dbuff, fr_dict_attr_t const *tlv, |
554 | | fr_da_stack_t *da_stack, unsigned int depth, |
555 | | fr_dcursor_t *cursor, void *encode_ctx, |
556 | | UNUSED fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair) |
557 | | |
558 | 0 | { |
559 | 0 | fr_pair_t *vp; |
560 | 0 | fr_dcursor_t child_cursor; |
561 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
562 | |
|
563 | 0 | if (!encode_pair) { |
564 | 0 | fr_strerror_printf("Asked to encode child attribute %s, but we were not passed an encoding function", |
565 | 0 | tlv->name); |
566 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
567 | 0 | } |
568 | | |
569 | 0 | vp = fr_dcursor_current(cursor); |
570 | 0 | if (!vp || (vp->da != tlv)) return 0; |
571 | | |
572 | 0 | vp = fr_pair_dcursor_init(&child_cursor, &vp->vp_group); |
573 | 0 | if (vp) { |
574 | 0 | ssize_t slen; |
575 | |
|
576 | 0 | FR_PROTO_TRACE("fr_struct_to_network trailing TLVs of %s", tlv->name); |
577 | 0 | fr_proto_da_stack_build(da_stack, vp->da); |
578 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
579 | |
|
580 | 0 | slen = fr_pair_cursor_to_network(&work_dbuff, da_stack, depth + 1, &child_cursor, encode_ctx, encode_pair); |
581 | 0 | if (slen < 0) return slen; |
582 | 0 | } |
583 | | |
584 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
585 | 0 | } |
586 | | |
587 | | static ssize_t encode_union(fr_dbuff_t *dbuff, fr_dict_attr_t const *wrapper, |
588 | | fr_dict_attr_t const *key_da, fr_pair_t const *key_vp, fr_dbuff_marker_t *key_m, |
589 | | fr_da_stack_t *da_stack, unsigned int depth, |
590 | | fr_dcursor_t *cursor, void *encode_ctx, |
591 | | UNUSED fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair) |
592 | | |
593 | 0 | { |
594 | 0 | ssize_t slen; |
595 | 0 | fr_pair_t *parent, *child, *found = NULL; |
596 | 0 | fr_dict_attr_t const *child_ref; |
597 | 0 | fr_dcursor_t child_cursor; |
598 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
599 | |
|
600 | 0 | parent = fr_dcursor_current(cursor); |
601 | 0 | if (!parent || (parent->da != wrapper)) return 0; |
602 | | |
603 | 0 | fr_assert(key_vp); /* @todo */ |
604 | |
|
605 | 0 | child = fr_pair_dcursor_init(&child_cursor, &parent->vp_group); |
606 | 0 | if (!child) { |
607 | | /* |
608 | | * @todo - do we want to skip encoding the entire parent structure? |
609 | | */ |
610 | 0 | FR_PROTO_TRACE("fr_struct_to_network union %s has no children", key_da->name); |
611 | 0 | return 0; |
612 | 0 | } |
613 | | |
614 | | /* |
615 | | * There's a key VP, we find the matching child struct, and then set the cursor to encode just |
616 | | * that child. |
617 | | */ |
618 | 0 | if (key_vp) { |
619 | 0 | fr_dict_enum_value_t const *enumv; |
620 | |
|
621 | 0 | enumv = fr_dict_enum_by_value(key_da, &key_vp->data); |
622 | 0 | if (enumv && ((child_ref = fr_dict_enum_attr_ref(enumv)) != NULL)) { |
623 | 0 | found = fr_pair_find_by_da(&parent->vp_group, NULL, child_ref); |
624 | 0 | if (found) { |
625 | 0 | (void) fr_dcursor_set_current(&child_cursor, found); |
626 | 0 | } |
627 | 0 | } |
628 | 0 | } |
629 | | |
630 | | /* |
631 | | * @todo - encode the key field based on the attribute number? |
632 | | * |
633 | | * However, we are likely better off just not doing that. |
634 | | * Which allows us to have the key and UNION contents |
635 | | * disagree. |
636 | | */ |
637 | 0 | if (!found && child->da->flags.is_unknown) { |
638 | 0 | fr_assert(child->da->type == FR_TYPE_OCTETS); |
639 | |
|
640 | 0 | goto encode; |
641 | 0 | } |
642 | | |
643 | | /* |
644 | | * No child matching the key vp was found. Either there's no key_vp, or the key_vp doesn't match |
645 | | * the chld we have. |
646 | | * |
647 | | * We then update the key field so that it corresponds to the child that we found. |
648 | | */ |
649 | 0 | if (!found) { |
650 | 0 | fr_dict_enum_value_t const *enumv; |
651 | 0 | fr_dict_enum_iter_t iter; |
652 | 0 | fr_dbuff_t key_dbuff; |
653 | | |
654 | | /* |
655 | | * Root through the enum values, looking for a child ref which matches the child we |
656 | | * found. |
657 | | */ |
658 | 0 | for (enumv = fr_dict_enum_iter_init(key_da, &iter); |
659 | 0 | enumv != NULL; |
660 | 0 | enumv = fr_dict_enum_iter_next(key_da, &iter)) { |
661 | 0 | child_ref = fr_dict_enum_attr_ref(enumv); |
662 | 0 | if (!child_ref) continue; |
663 | | |
664 | 0 | if (child_ref == child->da) break; |
665 | 0 | } |
666 | | |
667 | | /* |
668 | | * There's a child, but no matching enum. That's a fatal error of the dictionary |
669 | | * tokenizer. |
670 | | */ |
671 | 0 | if (!fr_cond_assert(enumv)) return PAIR_ENCODE_FATAL_ERROR; |
672 | | |
673 | | /* |
674 | | * Create a dbuff for the key, and encode the key. |
675 | | * |
676 | | * Note that enumv->value->vb_length is NOT set. That field is really only used for |
677 | | * string / octet data types. |
678 | | */ |
679 | 0 | fr_assert(key_da->flags.length >= 1); |
680 | 0 | fr_assert(key_da->flags.length <= 4); |
681 | |
|
682 | 0 | FR_DBUFF_INIT(&key_dbuff, fr_dbuff_current(key_m), (size_t) key_da->flags.length); |
683 | |
|
684 | 0 | FR_PROTO_TRACE("fr_struct_to_network union %s encoding key %s for child %s", |
685 | 0 | parent->da->name, key_da->name, child->da->name); |
686 | |
|
687 | 0 | if (fr_value_box_to_network(&key_dbuff, enumv->value) <= 0) return PAIR_ENCODE_FATAL_ERROR; |
688 | 0 | } |
689 | | |
690 | | /* |
691 | | * And finally encode the one child. |
692 | | */ |
693 | 0 | encode: |
694 | 0 | FR_PROTO_TRACE("fr_struct_to_network union %s encoding child %s", parent->da->name, child->da->name); |
695 | 0 | fr_proto_da_stack_build(da_stack, child->da); |
696 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
697 | |
|
698 | 0 | switch (child->da->type) { |
699 | 0 | case FR_TYPE_STRUCT: |
700 | 0 | slen = fr_struct_to_network(&work_dbuff, da_stack, depth + 2, |
701 | 0 | &child_cursor, encode_ctx, encode_value, encode_pair); |
702 | 0 | break; |
703 | | |
704 | 0 | case FR_TYPE_TLV: |
705 | 0 | slen = encode_tlv(&work_dbuff, child->da, da_stack, depth + 2, &child_cursor, encode_ctx, encode_value, encode_pair); |
706 | 0 | break; |
707 | | |
708 | 0 | case FR_TYPE_LEAF: |
709 | 0 | slen = encode_value(&work_dbuff, da_stack, depth + 2, &child_cursor, encode_ctx); |
710 | 0 | break; |
711 | | |
712 | 0 | default: |
713 | 0 | slen = 0; |
714 | 0 | break; |
715 | 0 | } |
716 | | |
717 | 0 | if (slen < 0) return slen; |
718 | | |
719 | | /* |
720 | | * @todo - if there is more than one child of the union, that's an error! |
721 | | */ |
722 | | |
723 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
724 | 0 | } |
725 | | |
726 | | static ssize_t encode_keyed_struct(fr_dbuff_t *dbuff, fr_pair_t const *vp, |
727 | | fr_da_stack_t *da_stack, unsigned int depth, |
728 | | fr_dcursor_t *cursor, void *encode_ctx, |
729 | | fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair) |
730 | 0 | { |
731 | 0 | FR_PROTO_TRACE("fr_struct_to_network encoding key %s", vp->da->name); |
732 | | |
733 | | /* |
734 | | * We usually have a keyed struct for the child. |
735 | | */ |
736 | 0 | if (vp->vp_type == FR_TYPE_STRUCT) { |
737 | 0 | fr_proto_da_stack_build(da_stack, vp->da); |
738 | 0 | return fr_struct_to_network(dbuff, da_stack, depth + 2, /* note + 2 !!! */ |
739 | 0 | cursor, encode_ctx, encode_value, encode_pair); |
740 | 0 | } |
741 | | |
742 | | /* |
743 | | * If it's not a real child, then it's a raw something. |
744 | | */ |
745 | 0 | fr_assert(vp->vp_type == FR_TYPE_OCTETS); |
746 | 0 | fr_assert(vp->da->flags.is_unknown); |
747 | |
|
748 | 0 | if (fr_value_box_to_network(dbuff, &vp->data) <= 0) return PAIR_ENCODE_FATAL_ERROR; |
749 | 0 | (void) fr_dcursor_next(cursor); |
750 | 0 | return 0; |
751 | 0 | } |
752 | | |
753 | | ssize_t fr_struct_to_network(fr_dbuff_t *dbuff, |
754 | | fr_da_stack_t *da_stack, unsigned int depth, |
755 | | fr_dcursor_t *parent_cursor, void *encode_ctx, |
756 | | fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair) |
757 | 0 | { |
758 | 0 | fr_dbuff_t work_dbuff; |
759 | 0 | fr_dbuff_marker_t hdr; |
760 | 0 | int offset = 0; |
761 | 0 | unsigned int child_num; |
762 | 0 | bool do_length = false; |
763 | 0 | uint8_t bit_buffer = 0; |
764 | 0 | fr_pair_t const *vp = fr_dcursor_current(parent_cursor); |
765 | 0 | fr_pair_t const *last = NULL; |
766 | 0 | fr_pair_t const *key_vp = NULL; |
767 | 0 | fr_dict_attr_t const *child, *parent, *key_da = NULL; |
768 | 0 | fr_dcursor_t child_cursor, *cursor; |
769 | 0 | size_t prefix_length = 0; |
770 | 0 | ssize_t slen; |
771 | 0 | fr_dbuff_marker_t key_m; |
772 | |
|
773 | 0 | if (!vp) { |
774 | 0 | fr_strerror_printf("%s: Can't encode empty struct", __FUNCTION__); |
775 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
776 | 0 | } |
777 | | |
778 | 0 | PAIR_VERIFY(vp); |
779 | 0 | parent = da_stack->da[depth]; |
780 | |
|
781 | 0 | if (parent->type != FR_TYPE_STRUCT) { |
782 | 0 | fr_strerror_printf("%s: Expected type \"struct\" got \"%s\"", __FUNCTION__, |
783 | 0 | fr_type_to_str(parent->type)); |
784 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
785 | 0 | } |
786 | | |
787 | | /* |
788 | | * If we get passed a struct VP, sort its children. |
789 | | */ |
790 | 0 | if (vp->vp_type == FR_TYPE_STRUCT) { |
791 | 0 | fr_pair_t *sorted = fr_dcursor_current(parent_cursor); /* NOT const */ |
792 | |
|
793 | 0 | fr_pair_list_sort(&sorted->vp_group, pair_sort_increasing); |
794 | 0 | fr_pair_dcursor_iter_init(&child_cursor, &sorted->vp_group, struct_next_encodable, parent); |
795 | | |
796 | | /* |
797 | | * Build the da_stack for the new structure. |
798 | | */ |
799 | 0 | vp = fr_dcursor_current(&child_cursor); |
800 | 0 | fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); |
801 | |
|
802 | 0 | FR_PROTO_TRACE("fr_struct_to_network encoding nested with parent %s", parent->name); |
803 | 0 | cursor = &child_cursor; |
804 | 0 | } else { |
805 | 0 | FR_PROTO_TRACE("fr_struct_to_network encoding flat"); |
806 | 0 | cursor = parent_cursor; |
807 | 0 | } |
808 | | |
809 | | /* |
810 | | * @todo - if we get a child which *eventually* has the |
811 | | * given parent, then allow encoding of that struct, too. |
812 | | * This allows us to encode structures automatically, |
813 | | * even if key fields are omitted. |
814 | | * |
815 | | * Note that this check catches TLVs which are "flat" and |
816 | | * not nested. We could fix that by adding a special |
817 | | * case, but it's better to just fix everything to handle |
818 | | * nested attributes. |
819 | | */ |
820 | 0 | if (vp && (vp->da->parent != parent)) { |
821 | 0 | fr_strerror_printf("%s: Asked to encode %s, but its parent %s is not the expected parent %s", |
822 | 0 | __FUNCTION__, vp->da->name, vp->da->parent->name, parent->name); |
823 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
824 | 0 | } |
825 | | |
826 | | /* |
827 | | * Some structs are prefixed by a 16-bit length. |
828 | | */ |
829 | 0 | if (!da_is_length_field(parent)) { |
830 | 0 | work_dbuff = FR_DBUFF(dbuff); |
831 | |
|
832 | 0 | } else if (da_is_length_field8(parent)) { |
833 | 0 | work_dbuff = FR_DBUFF_MAX(dbuff, UINT8_MAX); |
834 | 0 | fr_dbuff_marker(&hdr, &work_dbuff); |
835 | |
|
836 | 0 | FR_DBUFF_ADVANCE_RETURN(&work_dbuff, 1); |
837 | 0 | prefix_length = 1; |
838 | 0 | do_length = true; |
839 | |
|
840 | 0 | } else { |
841 | 0 | fr_assert(da_is_length_field16(parent)); |
842 | |
|
843 | 0 | work_dbuff = FR_DBUFF_MAX(dbuff, UINT16_MAX); |
844 | 0 | fr_dbuff_marker(&hdr, &work_dbuff); |
845 | |
|
846 | 0 | FR_DBUFF_ADVANCE_RETURN(&work_dbuff, 2); |
847 | 0 | prefix_length = 2; |
848 | 0 | do_length = true; |
849 | 0 | } |
850 | | |
851 | 0 | if (!encode_value) encode_value = fr_pair_encode_value; |
852 | | |
853 | | /* |
854 | | * Loop over all children. |
855 | | */ |
856 | 0 | for (child_num = 1; |
857 | 0 | (child = fr_dict_attr_child_by_num(parent, child_num)) != NULL; |
858 | 0 | child_num++) { |
859 | | /* |
860 | | * The child attributes should be in order. If |
861 | | * they're not, we fill the struct with zeroes. |
862 | | * |
863 | | * The caller will encode TLVs. |
864 | | */ |
865 | 0 | FR_PROTO_TRACE("fr_struct_to_network child %s", child->name); |
866 | | |
867 | | /* |
868 | | * If the caller specifies a member twice, then we only encode the first member. |
869 | | */ |
870 | 0 | while (last && vp && (last->da->parent == vp->da->parent) && (last->da->attr == vp->da->attr)) { |
871 | 0 | fr_assert(last != vp); |
872 | 0 | vp = fr_dcursor_next(cursor); |
873 | 0 | } |
874 | 0 | last = vp; |
875 | | |
876 | | /* |
877 | | * The MEMBER may be raw, in which case it is encoded as octets. |
878 | | * |
879 | | * This can happen for the last MEMBER of a struct, such as when the last member is a TLV |
880 | | * or GROUP, and the contents are malformed. |
881 | | * |
882 | | * It can also happen if a middle MEMBER has the right length, but the wrong contents. |
883 | | * e.g. when the contents have to be a well-formed IP prefix, but the prefix values are |
884 | | * out of the permitted range. |
885 | | */ |
886 | 0 | if (vp && (vp->da != child) && (vp->da->parent == parent) && (vp->da->attr == child_num)) { |
887 | 0 | fr_assert(vp->vp_raw); |
888 | 0 | fr_assert(vp->vp_type == FR_TYPE_OCTETS); |
889 | 0 | fr_assert(!da_is_bit_field(child)); |
890 | |
|
891 | 0 | goto encode_data; /* we may have a raw entry in an array :( */ |
892 | 0 | } |
893 | | |
894 | | /* |
895 | | * Remember the key field. Note that we ignore raw key fields. |
896 | | */ |
897 | 0 | if (fr_dict_attr_is_key_field(child)) { |
898 | 0 | fr_assert(!key_da); |
899 | |
|
900 | 0 | key_da = child; |
901 | 0 | key_vp = vp; |
902 | 0 | fr_dbuff_marker(&key_m, &work_dbuff); |
903 | 0 | } |
904 | | |
905 | | /* |
906 | | * Skipped a VP, or left one off at the end, fill the struct with zeros. |
907 | | */ |
908 | 0 | if (!vp || (vp->da != child)) { |
909 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), " no child %s", child->name); |
910 | | |
911 | | /* |
912 | | * Zero out the bit field. |
913 | | */ |
914 | 0 | if (da_is_bit_field(child)) { |
915 | 0 | offset = put_bits_dbuff(&work_dbuff, &bit_buffer, offset, child->flags.length, 0); |
916 | 0 | if (offset < 0) { |
917 | 0 | fr_strerror_printf("Failed encoding bit field %s", child->name); |
918 | 0 | return offset; |
919 | 0 | } |
920 | 0 | last = NULL; |
921 | 0 | continue; |
922 | 0 | } |
923 | | |
924 | | /* |
925 | | * A child TLV is missing, we're done, and we don't encode any data. |
926 | | * |
927 | | * @todo - mark up the TLVs as required? |
928 | | */ |
929 | 0 | if (child->type == FR_TYPE_TLV) goto encode_length; |
930 | | |
931 | | /* |
932 | | * Zero out the unused field. |
933 | | */ |
934 | 0 | FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, child->flags.length); |
935 | | |
936 | | /* |
937 | | * We didn't encode the current VP, so it's not the last one. |
938 | | */ |
939 | 0 | last = NULL; |
940 | 0 | continue; |
941 | 0 | } |
942 | | |
943 | | /* |
944 | | * The 'struct' encoder handles bit fields. |
945 | | * They're just integers, so there's no need to |
946 | | * call the protocol encoder. |
947 | | * |
948 | | * This limitation means that we can't have |
949 | | * encrypted bit fields, but that's fine. |
950 | | */ |
951 | 0 | if (da_is_bit_field(child)) { |
952 | 0 | uint64_t value; |
953 | |
|
954 | 0 | FR_PROTO_TRACE("child %s is a bit field", child->name); |
955 | |
|
956 | 0 | switch (child->type) { |
957 | 0 | case FR_TYPE_BOOL: |
958 | 0 | value = vp->vp_bool; |
959 | 0 | break; |
960 | | |
961 | 0 | case FR_TYPE_UINT8: |
962 | 0 | value = vp->vp_uint8; |
963 | 0 | break; |
964 | | |
965 | 0 | case FR_TYPE_UINT16: |
966 | 0 | value = vp->vp_uint16; |
967 | 0 | break; |
968 | | |
969 | 0 | case FR_TYPE_UINT32: |
970 | 0 | value = vp->vp_uint32; |
971 | 0 | break; |
972 | | |
973 | 0 | case FR_TYPE_UINT64: |
974 | 0 | value = vp->vp_uint64; |
975 | 0 | break; |
976 | | |
977 | 0 | default: |
978 | 0 | fr_strerror_const("Invalid bit field"); |
979 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
980 | 0 | } |
981 | | |
982 | 0 | offset = put_bits_dbuff(&work_dbuff, &bit_buffer, offset, child->flags.length, value); |
983 | 0 | if (offset < 0) { |
984 | 0 | fr_strerror_printf("Failed encoding bit field %s", child->name); |
985 | 0 | return offset; |
986 | 0 | } |
987 | | |
988 | | /* |
989 | | * We have to go to the next pair manually, as the protocol-specific |
990 | | * encode_value() function will normally go to the next cursor entry. |
991 | | */ |
992 | 0 | vp = fr_dcursor_next(cursor); |
993 | | /* We need to continue, there may be more fields to encode */ |
994 | |
|
995 | 0 | goto next; |
996 | 0 | } |
997 | | |
998 | | /* Not a bit field; insist that no buffered bits remain. */ |
999 | 0 | if (offset != 0) { |
1000 | 0 | leftover_bits: |
1001 | 0 | fr_strerror_const("leftover bits"); |
1002 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
1003 | 0 | } |
1004 | | |
1005 | | /* |
1006 | | * Encode child TLVs at the end of a struct. |
1007 | | * |
1008 | | * In order to encode the child TLVs, we need to |
1009 | | * know the length of "T" and "L", and we don't. |
1010 | | * So just let the caller do the work. |
1011 | | */ |
1012 | 0 | if (child->type == FR_TYPE_TLV) { |
1013 | 0 | fr_assert(!key_da); |
1014 | |
|
1015 | 0 | FR_PROTO_TRACE("child %s is a TLV field", child->name); |
1016 | 0 | slen = encode_tlv(&work_dbuff, child, da_stack, depth, cursor, |
1017 | 0 | encode_ctx, encode_value, encode_pair); |
1018 | 0 | if (slen < 0) return slen; |
1019 | 0 | goto encode_length; |
1020 | 0 | } |
1021 | | |
1022 | 0 | if (child->type == FR_TYPE_UNION) { |
1023 | 0 | FR_PROTO_TRACE("child %s is a UNION field", child->name); |
1024 | |
|
1025 | 0 | if (!key_da) { |
1026 | 0 | FR_PROTO_TRACE("structure %s is missing key_da", parent->name); |
1027 | 0 | goto encode_length; |
1028 | 0 | } |
1029 | | |
1030 | 0 | slen = encode_union(&work_dbuff, child, key_da, key_vp, &key_m, da_stack, depth, cursor, |
1031 | 0 | encode_ctx, encode_value, encode_pair); |
1032 | 0 | if (slen < 0) return slen; |
1033 | 0 | goto encode_length; |
1034 | 0 | } |
1035 | | |
1036 | 0 | FR_PROTO_TRACE("child %s encode_value", child->name); |
1037 | | |
1038 | | /* |
1039 | | * Call the protocol encoder for non-bit fields. |
1040 | | */ |
1041 | 0 | encode_data: |
1042 | 0 | fr_proto_da_stack_build(da_stack, child); |
1043 | |
|
1044 | 0 | if (child->flags.array) { |
1045 | 0 | slen = fr_pair_array_to_network(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx, encode_value); |
1046 | 0 | } else { |
1047 | 0 | slen = encode_value(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx); |
1048 | 0 | } |
1049 | 0 | if (slen < 0) return slen; |
1050 | 0 | vp = fr_dcursor_current(cursor); |
1051 | |
|
1052 | 0 | next: |
1053 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "fr_struct_to_network after child %s", child->name); |
1054 | 0 | } |
1055 | | |
1056 | | /* Check for leftover bits */ |
1057 | 0 | if (offset != 0) goto leftover_bits; |
1058 | | |
1059 | | /* |
1060 | | * Check for keyed data to encode. |
1061 | | */ |
1062 | 0 | if (vp && key_da) { |
1063 | 0 | fr_assert((vp->da->parent->type == FR_TYPE_UNION) || (vp->da->parent == key_da) || vp->da->flags.is_unknown || vp->da->flags.is_raw); |
1064 | |
|
1065 | 0 | slen = encode_keyed_struct(&work_dbuff, vp, da_stack, depth, |
1066 | 0 | cursor, encode_ctx, encode_value, encode_pair); |
1067 | 0 | if (slen < 0) return slen; |
1068 | 0 | } |
1069 | | |
1070 | 0 | encode_length: |
1071 | 0 | if (do_length) { |
1072 | 0 | size_t length = fr_dbuff_used(&work_dbuff); |
1073 | |
|
1074 | | #ifdef __COVERITY__ |
1075 | | /* |
1076 | | * Coverity somehow can't infer that length |
1077 | | * is at least as long as the prefix, instead |
1078 | | * thinkings it's zero so that it underflows. |
1079 | | * We therefore add a Coverity-only check to |
1080 | | * reassure it. |
1081 | | */ |
1082 | | if (length < prefix_length) return PAIR_ENCODE_FATAL_ERROR; |
1083 | | #endif |
1084 | 0 | if (da_is_length_field8(parent)) { |
1085 | 0 | length -= prefix_length; |
1086 | |
|
1087 | 0 | length += da_length_offset(parent); |
1088 | |
|
1089 | 0 | if (length > UINT8_MAX) return PAIR_ENCODE_FATAL_ERROR; |
1090 | | |
1091 | 0 | (void) fr_dbuff_in(&hdr, (uint8_t) length); |
1092 | 0 | } else { |
1093 | 0 | length -= prefix_length; |
1094 | |
|
1095 | 0 | length += da_length_offset(parent); |
1096 | |
|
1097 | 0 | if (length > UINT16_MAX) return PAIR_ENCODE_FATAL_ERROR; |
1098 | | |
1099 | 0 | (void) fr_dbuff_in(&hdr, (uint16_t) length); |
1100 | 0 | } |
1101 | 0 | } |
1102 | | |
1103 | | /* |
1104 | | * We've encoded the children, so tell the parent cursor |
1105 | | * that we've encoded the parent. |
1106 | | */ |
1107 | 0 | if (cursor != parent_cursor) (void) fr_dcursor_next(parent_cursor); |
1108 | |
|
1109 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done fr_struct_to_network"); |
1110 | |
|
1111 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
1112 | 0 | } |