/src/freeradius-server/src/protocols/dns/encode.c
Line | Count | Source (jump to first uncovered line) |
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: 7007cca77cf00bc2916b078683003bd36e32913a $ |
19 | | * |
20 | | * @file protocols/dns/encode.c |
21 | | * @brief Functions to encode DNS packets |
22 | | * |
23 | | * @author Alan DeKok (aland@freeradius.org) |
24 | | * |
25 | | * @copyright 2021 NetworkRADIUS SARL (legal@networkradius.com) |
26 | | */ |
27 | | #include <freeradius-devel/io/test_point.h> |
28 | | #include <freeradius-devel/util/dbuff.h> |
29 | | #include <freeradius-devel/util/dns.h> |
30 | | #include <freeradius-devel/util/proto.h> |
31 | | #include <freeradius-devel/util/struct.h> |
32 | | #include <freeradius-devel/util/encode.h> |
33 | | |
34 | | #include "dns.h" |
35 | | #include "attrs.h" |
36 | | |
37 | 0 | #define DNS_OPT_HDR_LEN (4) |
38 | | |
39 | | static ssize_t encode_value(fr_dbuff_t *dbuff, |
40 | | fr_da_stack_t *da_stack, unsigned int depth, |
41 | | fr_dcursor_t *cursor, void *encode_ctx); |
42 | | |
43 | | static ssize_t encode_rfc(fr_dbuff_t *dbuff, |
44 | | fr_da_stack_t *da_stack, unsigned int depth, |
45 | | fr_dcursor_t *cursor, void *encode_ctx); |
46 | | |
47 | | static ssize_t encode_tlv(fr_dbuff_t *dbuff, |
48 | | fr_da_stack_t *da_stack, unsigned int depth, |
49 | | fr_dcursor_t *cursor, void *encode_ctx); |
50 | | |
51 | | static ssize_t encode_child(fr_dbuff_t *dbuff, |
52 | | fr_da_stack_t *da_stack, unsigned int depth, |
53 | | fr_dcursor_t *cursor, void *encode_ctx); |
54 | | |
55 | | /** Macro-like function for encoding an option header |
56 | | * |
57 | | * 0 1 2 3 |
58 | | * 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 |
59 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
60 | | * | option-code | option-len | |
61 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
62 | | * |
63 | | * @param[out] m Where to write the 4 byte option header. |
64 | | * @param[in] option The option number (host byte order). |
65 | | * @param[in] data_len The length of the option (host byte order). |
66 | | * @return |
67 | | * - <0 How much data would have been required as a negative value. |
68 | | * - 4 The length of data written. |
69 | | */ |
70 | | static inline ssize_t encode_option_hdr(fr_dbuff_marker_t *m, uint16_t option, size_t data_len) |
71 | 0 | { |
72 | 0 | FR_DBUFF_IN_RETURN(m, option); |
73 | 0 | FR_DBUFF_IN_RETURN(m, (uint16_t) data_len); |
74 | | |
75 | 0 | return sizeof(option) + sizeof(uint16_t); |
76 | 0 | } |
77 | | |
78 | | |
79 | | static ssize_t encode_value(fr_dbuff_t *dbuff, |
80 | | fr_da_stack_t *da_stack, unsigned int depth, |
81 | | fr_dcursor_t *cursor, void *encode_ctx) |
82 | 0 | { |
83 | 0 | ssize_t slen; |
84 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
85 | 0 | fr_pair_t const *vp = fr_dcursor_current(cursor); |
86 | 0 | fr_dict_attr_t const *da = da_stack->da[depth]; |
87 | 0 | fr_dns_ctx_t *packet_ctx = encode_ctx; |
88 | |
|
89 | 0 | PAIR_VERIFY(vp); |
90 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
91 | | |
92 | | /* |
93 | | * Nested structs |
94 | | */ |
95 | 0 | if (vp->vp_type == FR_TYPE_STRUCT) { |
96 | 0 | fr_dcursor_t child_cursor; |
97 | |
|
98 | 0 | fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor); |
99 | |
|
100 | 0 | slen = fr_struct_to_network(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx, encode_value, encode_child); |
101 | 0 | if (slen < 0) return slen; |
102 | | |
103 | | /* |
104 | | * Rebuild the da_stack for the next option. |
105 | | */ |
106 | 0 | vp = fr_dcursor_next(cursor); |
107 | 0 | fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); |
108 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
109 | 0 | } |
110 | | |
111 | | /* |
112 | | * Flat-list |
113 | | */ |
114 | 0 | if (da->type == FR_TYPE_STRUCT) { |
115 | 0 | slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_child); |
116 | 0 | if (slen <= 0) return slen; |
117 | | |
118 | | /* |
119 | | * Rebuild the da_stack for the next option. |
120 | | */ |
121 | 0 | vp = fr_dcursor_current(cursor); |
122 | 0 | fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); |
123 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
124 | 0 | } |
125 | | /* |
126 | | * If it's not a TLV, it should be a value type RFC |
127 | | * attribute make sure that it is. |
128 | | */ |
129 | 0 | if (da_stack->da[depth + 1] != NULL) { |
130 | 0 | fr_strerror_printf("%s: Encoding value but not at top of stack", __FUNCTION__); |
131 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
132 | 0 | } |
133 | | |
134 | 0 | if (vp->da != da) { |
135 | 0 | fr_strerror_printf("%s: Top of stack does not match vp->da", __FUNCTION__); |
136 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
137 | 0 | } |
138 | | |
139 | 0 | switch (vp->vp_type) { |
140 | 0 | case FR_TYPE_TLV: |
141 | 0 | case FR_TYPE_VENDOR: |
142 | 0 | case FR_TYPE_VSA: |
143 | 0 | case FR_TYPE_GROUP: |
144 | 0 | fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__, |
145 | 0 | fr_type_to_str(da->type)); |
146 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
147 | | |
148 | 0 | case FR_TYPE_STRING: |
149 | | /* |
150 | | * DNS labels get a special encoder. |
151 | | */ |
152 | 0 | if (!da->flags.extra) { |
153 | 0 | fr_dbuff_marker_t last_byte, src; |
154 | |
|
155 | 0 | fr_assert((da->flags.subtype == FLAG_ENCODE_DNS_LABEL) || |
156 | 0 | (da->flags.subtype == FLAG_ENCODE_DNS_LABEL_UNCOMPRESSED)); |
157 | |
|
158 | 0 | fr_dbuff_marker(&last_byte, &work_dbuff); |
159 | 0 | fr_dbuff_marker(&src, &work_dbuff); |
160 | 0 | FR_PROTO_TRACE("encode DNS label %s", vp->vp_strvalue); |
161 | 0 | slen = fr_dns_label_from_value_box_dbuff(&work_dbuff, (da->flags.subtype == FLAG_ENCODE_DNS_LABEL), |
162 | 0 | &vp->data, packet_ctx->lb); |
163 | 0 | if (slen < 0) return slen; |
164 | 0 | break; |
165 | 0 | } |
166 | 0 | goto to_network; |
167 | | |
168 | | /* |
169 | | * Common encoder might add scope byte, so we just copy the address portion |
170 | | */ |
171 | 0 | case FR_TYPE_IPV6_ADDR: |
172 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr)); |
173 | 0 | break; |
174 | | |
175 | 0 | case FR_TYPE_IPV4_PREFIX: |
176 | 0 | fr_strerror_const("invalid data type - ipv4prefix"); |
177 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
178 | | |
179 | 0 | case FR_TYPE_IPV6_PREFIX: |
180 | 0 | fr_strerror_const("invalid data type - ipv4prefix"); |
181 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
182 | | |
183 | 0 | case FR_TYPE_BOOL: |
184 | | /* |
185 | | * Don't encode anything! The mere existence of |
186 | | * the attribute signifies a "true" value. |
187 | | */ |
188 | 0 | break; |
189 | | |
190 | | /* |
191 | | * The value_box functions will take care of fixed-width |
192 | | * "string" and "octets" options. |
193 | | */ |
194 | 0 | to_network: |
195 | 0 | case FR_TYPE_OCTETS: |
196 | | /* |
197 | | * Hack until we find all places that don't set data.enumv |
198 | | */ |
199 | 0 | if (vp->da->flags.length && (vp->data.enumv != vp->da)) { |
200 | 0 | fr_dict_attr_t const * const *c = &vp->data.enumv; |
201 | 0 | fr_dict_attr_t **u; |
202 | |
|
203 | 0 | memcpy(&u, &c, sizeof(c)); /* const issues */ |
204 | 0 | memcpy(u, &vp->da, sizeof(vp->da)); |
205 | 0 | } |
206 | 0 | FALL_THROUGH; |
207 | |
|
208 | 0 | default: |
209 | 0 | slen = fr_value_box_to_network(&work_dbuff, &vp->data); |
210 | 0 | if (slen < 0) return PAIR_ENCODE_FATAL_ERROR; |
211 | 0 | break; |
212 | 0 | } |
213 | | |
214 | | /* |
215 | | * Rebuilds the TLV stack for encoding the next attribute |
216 | | */ |
217 | 0 | vp = fr_dcursor_next(cursor); |
218 | 0 | fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); |
219 | |
|
220 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "done value"); |
221 | |
|
222 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
223 | 0 | } |
224 | | |
225 | | static ssize_t encode_child(fr_dbuff_t *dbuff, |
226 | | fr_da_stack_t *da_stack, unsigned int depth, |
227 | | fr_dcursor_t *cursor, void *encode_ctx) |
228 | 0 | { |
229 | 0 | ssize_t len; |
230 | 0 | fr_pair_t *vp = fr_dcursor_current(cursor); |
231 | 0 | fr_dcursor_t child_cursor; |
232 | 0 | fr_dbuff_t work_dbuff; |
233 | |
|
234 | 0 | if (da_stack->da[depth]) { |
235 | | /* |
236 | | * Determine the nested type and call the appropriate encoder |
237 | | */ |
238 | 0 | switch (da_stack->da[depth]->type) { |
239 | 0 | case FR_TYPE_TLV: |
240 | 0 | if (!da_stack->da[depth + 1]) break; |
241 | | |
242 | 0 | return encode_tlv(dbuff, da_stack, depth, cursor, encode_ctx); |
243 | | |
244 | 0 | case FR_TYPE_GROUP: |
245 | 0 | if (!da_stack->da[depth + 1]) break; |
246 | 0 | FALL_THROUGH; |
247 | | |
248 | 0 | default: |
249 | 0 | return encode_rfc(dbuff, da_stack, depth, cursor, encode_ctx); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | 0 | fr_assert(fr_type_is_structural(vp->vp_type)); |
254 | |
|
255 | 0 | fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor); |
256 | 0 | work_dbuff = FR_DBUFF(dbuff); |
257 | |
|
258 | 0 | while ((vp = fr_dcursor_current(&child_cursor)) != NULL) { |
259 | 0 | fr_proto_da_stack_build(da_stack, vp->da); |
260 | |
|
261 | 0 | switch (da_stack->da[depth]->type) { |
262 | 0 | case FR_TYPE_TLV: |
263 | 0 | len = encode_tlv(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx); |
264 | 0 | break; |
265 | | |
266 | 0 | default: |
267 | 0 | len = encode_rfc(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx); |
268 | 0 | break; |
269 | 0 | } |
270 | | |
271 | 0 | if (len <= 0) return len; |
272 | 0 | } |
273 | | |
274 | | /* |
275 | | * Skip over the attribute we just encoded. |
276 | | */ |
277 | 0 | vp = fr_dcursor_next(cursor); |
278 | 0 | fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL); |
279 | |
|
280 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
281 | 0 | } |
282 | | |
283 | | /** Encode an RFC format TLV. |
284 | | * |
285 | | * This could be a standard attribute, or a TLV data type. |
286 | | * If it's a standard attribute, then vp->da->attr == attribute. |
287 | | * Otherwise, attribute may be something else. |
288 | | */ |
289 | | static ssize_t encode_rfc(fr_dbuff_t *dbuff, |
290 | | fr_da_stack_t *da_stack, unsigned int depth, |
291 | | fr_dcursor_t *cursor, void *encode_ctx) |
292 | 0 | { |
293 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
294 | 0 | fr_dbuff_marker_t hdr; |
295 | 0 | fr_dict_attr_t const *da = da_stack->da[depth]; |
296 | 0 | ssize_t len; |
297 | |
|
298 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
299 | 0 | fr_dbuff_marker(&hdr, &work_dbuff); |
300 | | |
301 | | /* |
302 | | * Make space for the header... |
303 | | */ |
304 | 0 | FR_DBUFF_EXTEND_LOWAT_OR_RETURN(&work_dbuff, DNS_OPT_HDR_LEN); |
305 | 0 | fr_dbuff_advance(&work_dbuff, DNS_OPT_HDR_LEN); |
306 | | |
307 | | /* |
308 | | * Write out the option's value |
309 | | */ |
310 | 0 | if (da->flags.array) { |
311 | 0 | len = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value); |
312 | 0 | } else { |
313 | 0 | len = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx); |
314 | 0 | } |
315 | 0 | if (len < 0) return len; |
316 | | |
317 | | /* |
318 | | * Write out the option number and length (before the value we just wrote) |
319 | | */ |
320 | 0 | (void) encode_option_hdr(&hdr, (uint16_t)da->attr, (uint16_t) (fr_dbuff_used(&work_dbuff) - DNS_OPT_HDR_LEN)); |
321 | |
|
322 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done RFC header"); |
323 | |
|
324 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
325 | 0 | } |
326 | | |
327 | | static ssize_t encode_tlv(fr_dbuff_t *dbuff, |
328 | | fr_da_stack_t *da_stack, unsigned int depth, |
329 | | fr_dcursor_t *cursor, void *encode_ctx) |
330 | 0 | { |
331 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
332 | 0 | fr_dbuff_marker_t hdr; |
333 | 0 | fr_dict_attr_t const *da = da_stack->da[depth]; |
334 | 0 | ssize_t len; |
335 | |
|
336 | 0 | fr_dbuff_marker(&hdr, &work_dbuff); |
337 | 0 | PAIR_VERIFY(fr_dcursor_current(cursor)); |
338 | 0 | FR_PROTO_STACK_PRINT(da_stack, depth); |
339 | |
|
340 | 0 | if (da_stack->da[depth]->type != FR_TYPE_TLV) { |
341 | 0 | fr_strerror_printf("%s: Expected type \"tlv\" got \"%s\"", __FUNCTION__, |
342 | 0 | fr_type_to_str(da_stack->da[depth]->type)); |
343 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
344 | 0 | } |
345 | | |
346 | 0 | if (!da_stack->da[depth + 1]) { |
347 | 0 | fr_assert(0); |
348 | 0 | fr_strerror_printf("%s: Can't encode empty TLV", __FUNCTION__); |
349 | 0 | return PAIR_ENCODE_FATAL_ERROR; |
350 | 0 | } |
351 | | |
352 | 0 | FR_DBUFF_ADVANCE_RETURN(&work_dbuff, DNS_OPT_HDR_LEN); /* Make room for option header */ |
353 | | |
354 | 0 | len = fr_pair_cursor_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_child); |
355 | 0 | if (len < 0) return len; |
356 | | |
357 | | /* |
358 | | * 0 1 2 3 |
359 | | * 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 |
360 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
361 | | * | option-code | option-len | |
362 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
363 | | */ |
364 | 0 | (void) encode_option_hdr(&hdr, (uint16_t)da->attr, (uint16_t) (fr_dbuff_used(&work_dbuff) - DNS_OPT_HDR_LEN)); |
365 | |
|
366 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done TLV header"); |
367 | |
|
368 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
369 | 0 | } |
370 | | |
371 | | |
372 | | /** Encode a Dns option and any sub-options. |
373 | | * |
374 | | * @param[out] dbuff Where to write encoded DHCP attributes. |
375 | | * @param[in] cursor with current VP set to the option to be encoded. |
376 | | * Will be advanced to the next option to encode. |
377 | | * @param[in] encode_ctx containing parameters for the encoder. |
378 | | * @return |
379 | | * - > 0 length of data written. |
380 | | * - < 0 error. |
381 | | */ |
382 | | static ssize_t fr_dns_encode_rr(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx) |
383 | 0 | { |
384 | 0 | ssize_t slen; |
385 | 0 | fr_pair_t *vp; |
386 | 0 | fr_da_stack_t da_stack; |
387 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF_MAX(dbuff, UINT16_MAX); |
388 | |
|
389 | 0 | fr_proto_da_stack_build(&da_stack, attr_dns_rr); |
390 | 0 | FR_PROTO_STACK_PRINT(&da_stack, 0); |
391 | |
|
392 | 0 | FR_PROTO_TRACE("encode_rr -- remaining %zd", fr_dbuff_remaining(&work_dbuff)); |
393 | |
|
394 | 0 | vp = fr_dcursor_current(cursor); |
395 | 0 | if (vp->vp_type == FR_TYPE_STRUCT) { |
396 | 0 | fr_dcursor_t child_cursor; |
397 | |
|
398 | 0 | fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor); |
399 | |
|
400 | 0 | slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &child_cursor, encode_ctx, encode_value, encode_child); |
401 | 0 | if (slen <= 0) return slen; |
402 | 0 | (void) fr_dcursor_next(cursor); |
403 | |
|
404 | 0 | } else { |
405 | 0 | slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, cursor, encode_ctx, encode_value, encode_child); |
406 | 0 | if (slen <= 0) return slen; |
407 | 0 | } |
408 | | |
409 | 0 | FR_PROTO_TRACE("Complete rr is %zu byte(s)", fr_dbuff_used(&work_dbuff)); |
410 | 0 | FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), NULL); |
411 | |
|
412 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
413 | 0 | } |
414 | | |
415 | | static ssize_t encode_record(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, fr_pair_list_t *vps, |
416 | | fr_dict_attr_t const *attr, fr_dns_ctx_t *packet_ctx, uint8_t *counter) |
417 | 0 | { |
418 | 0 | int count; |
419 | 0 | fr_pair_t *vp; |
420 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
421 | 0 | fr_dcursor_t cursor; |
422 | |
|
423 | 0 | vp = fr_pair_dcursor_by_da_init(&cursor, vps, attr); |
424 | 0 | if (!vp) { |
425 | 0 | FR_PROTO_TRACE(" %s not found in list", attr->name); |
426 | 0 | return 0; |
427 | 0 | } |
428 | | |
429 | 0 | fr_proto_da_stack_build(da_stack, attr); |
430 | |
|
431 | 0 | count = 0; |
432 | 0 | while (count < 65535) { |
433 | 0 | ssize_t slen; |
434 | 0 | fr_dcursor_t child_cursor; |
435 | |
|
436 | 0 | fr_pair_dcursor_init(&child_cursor, &vp->vp_group); |
437 | 0 | slen = fr_struct_to_network(&work_dbuff, da_stack, 0, &child_cursor, packet_ctx, encode_value, encode_child); |
438 | 0 | if (slen <= 0) return slen; |
439 | | |
440 | 0 | count++; |
441 | 0 | vp = fr_dcursor_next(&cursor); |
442 | 0 | if (!vp) break; |
443 | 0 | } |
444 | | |
445 | 0 | fr_nbo_from_uint16(counter, count); |
446 | 0 | FR_PROTO_TRACE(" %s encoded %d records", attr->name, count); |
447 | |
|
448 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
449 | 0 | } |
450 | | |
451 | | /** Encode a DNS packet |
452 | | * |
453 | | */ |
454 | | ssize_t fr_dns_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_dns_ctx_t *packet_ctx) |
455 | 0 | { |
456 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
457 | 0 | ssize_t slen; |
458 | 0 | uint8_t *packet; |
459 | 0 | fr_pair_t *vp; |
460 | 0 | fr_dcursor_t cursor, child_cursor; |
461 | 0 | fr_da_stack_t da_stack; |
462 | |
|
463 | 0 | packet = fr_dbuff_current(&work_dbuff); |
464 | 0 | fr_assert(packet == packet_ctx->packet); |
465 | | |
466 | | /* |
467 | | * @todo - find maximum packet length, and limit work_dbuff to that. |
468 | | */ |
469 | 0 | vp = fr_pair_dcursor_by_da_init(&cursor, vps, attr_dns_packet); |
470 | 0 | if (!vp) { |
471 | 0 | fr_pair_list_debug(vps); |
472 | |
|
473 | 0 | fr_strerror_const("attribute list does not include DNS packet header"); |
474 | 0 | return -1; |
475 | 0 | } |
476 | | |
477 | | /* |
478 | | * Encode the header. |
479 | | */ |
480 | 0 | fr_pair_dcursor_init(&child_cursor, &vp->vp_group); |
481 | 0 | fr_proto_da_stack_build(&da_stack, attr_dns_packet); |
482 | |
|
483 | 0 | slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, packet_ctx, encode_value, NULL); |
484 | 0 | if (slen <= 0) return slen; |
485 | | |
486 | 0 | fr_assert(slen == DNS_HDR_LEN); |
487 | | |
488 | | /* |
489 | | * Encode questions |
490 | | */ |
491 | 0 | slen = encode_record(&work_dbuff, &da_stack, vps, attr_dns_question, packet_ctx, packet + 4); |
492 | 0 | if (slen < 0) return slen - (fr_dbuff_current(&work_dbuff) - packet); |
493 | | |
494 | | /* |
495 | | * Encode answers |
496 | | */ |
497 | 0 | slen = encode_record(&work_dbuff, &da_stack, vps, attr_dns_rr, packet_ctx, packet + 6); |
498 | 0 | if (slen < 0) return slen - (fr_dbuff_current(&work_dbuff) - packet); |
499 | | |
500 | | /* |
501 | | * Encode NS records |
502 | | */ |
503 | 0 | slen = encode_record(&work_dbuff, &da_stack, vps, attr_dns_ns, packet_ctx, packet + 8); |
504 | 0 | if (slen < 0) return slen - (fr_dbuff_current(&work_dbuff) - packet); |
505 | | |
506 | | /* |
507 | | * Encode additional records |
508 | | */ |
509 | 0 | slen = encode_record(&work_dbuff, &da_stack, vps, attr_dns_ar, packet_ctx, packet + 10); |
510 | 0 | if (slen < 0) return slen - (fr_dbuff_current(&work_dbuff) - packet); |
511 | | |
512 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
513 | 0 | } |
514 | | |
515 | | static int encode_test_ctx(void **out, TALLOC_CTX *ctx) |
516 | 0 | { |
517 | 0 | fr_dns_ctx_t *test_ctx; |
518 | |
|
519 | 0 | test_ctx = talloc_zero(ctx, fr_dns_ctx_t); |
520 | 0 | if (!test_ctx) return -1; |
521 | | |
522 | 0 | test_ctx->tmp_ctx = talloc(test_ctx, uint8_t); |
523 | |
|
524 | 0 | *out = test_ctx; |
525 | |
|
526 | 0 | return 0; |
527 | 0 | } |
528 | | |
529 | | static ssize_t fr_dns_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, void *proto_ctx) |
530 | 0 | { |
531 | 0 | ssize_t slen; |
532 | 0 | fr_dns_ctx_t *packet_ctx = (fr_dns_ctx_t *) proto_ctx; |
533 | |
|
534 | 0 | packet_ctx->packet = data; |
535 | 0 | packet_ctx->packet_len = data_len; |
536 | 0 | packet_ctx->lb = fr_dns_labels_get(data, data_len, false); |
537 | 0 | fr_assert(packet_ctx->lb != NULL); |
538 | |
|
539 | 0 | slen = fr_dns_encode(&FR_DBUFF_TMP(data, data_len), vps, packet_ctx); |
540 | |
|
541 | 0 | #ifndef NDEBUG |
542 | 0 | if (slen <= 0) return slen; |
543 | | |
544 | 0 | if (fr_debug_lvl > 2) { |
545 | | // fr_dns_print_hex(stdout, data, slen); |
546 | 0 | } |
547 | 0 | #endif |
548 | |
|
549 | 0 | return slen; |
550 | 0 | } |
551 | | |
552 | | /* |
553 | | * Test points |
554 | | */ |
555 | | extern fr_test_point_pair_encode_t dns_tp_encode_pair; |
556 | | fr_test_point_pair_encode_t dns_tp_encode_pair = { |
557 | | .test_ctx = encode_test_ctx, |
558 | | .func = fr_dns_encode_rr, |
559 | | }; |
560 | | |
561 | | extern fr_test_point_proto_encode_t dns_tp_encode_proto; |
562 | | fr_test_point_proto_encode_t dns_tp_encode_proto = { |
563 | | .test_ctx = encode_test_ctx, |
564 | | .func = fr_dns_encode_proto |
565 | | }; |