/src/freeradius-server/src/protocols/vmps/vmps.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program 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 |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; 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: 9ca073ffef31f13de986c2f6728d7407c9c6b323 $ |
19 | | * |
20 | | * @file src/protocols/vmps/vmps.c |
21 | | * @brief Functions to send/receive VQP packets. |
22 | | * |
23 | | * @copyright 2007 Alan DeKok (aland@deployingradius.com) |
24 | | */ |
25 | | |
26 | | RCSID("$Id: 9ca073ffef31f13de986c2f6728d7407c9c6b323 $") |
27 | | |
28 | | #include <freeradius-devel/io/test_point.h> |
29 | | #include <freeradius-devel/protocol/vmps/vmps.h> |
30 | | #include <freeradius-devel/util/dbuff.h> |
31 | | #include <freeradius-devel/util/proto.h> |
32 | | #include <freeradius-devel/util/udp.h> |
33 | | |
34 | | #include "vmps.h" |
35 | | #include "attrs.h" |
36 | | |
37 | | /** Used as the decoder ctx |
38 | | * |
39 | | */ |
40 | | typedef struct { |
41 | | int nothing; |
42 | | } fr_vmps_ctx_t; |
43 | | |
44 | | /* |
45 | | * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/tcpdump/print-vqp.c |
46 | | * |
47 | | * Chapter 12 of Hacking Cisco Networks Exposed (Vladimirov, Gavrilenko, |
48 | | * and Mikhailovsky, McGraw-Hill 2005) describes some of how it works. |
49 | | * |
50 | | * VLAN Query Protocol (VQP) |
51 | | * |
52 | | * 0 1 2 3 |
53 | | * 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 |
54 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
55 | | * | Version | Opcode | Response Code | Data Count | |
56 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
57 | | * | Transaction ID | |
58 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
59 | | * | Type (1) | |
60 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
61 | | * | Length | Data / |
62 | | * / / |
63 | | * / / |
64 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
65 | | * | Type (n) | |
66 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
67 | | * | Length | Data / |
68 | | * / / |
69 | | * / / |
70 | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
71 | | * |
72 | | * VQP is layered over UDP. The default destination port is 1589. |
73 | | * |
74 | | */ |
75 | | char const *fr_vmps_packet_names[FR_VMPS_CODE_MAX] = { |
76 | | [FR_PACKET_TYPE_VALUE_JOIN_REQUEST] = "Join-Request", |
77 | | [FR_PACKET_TYPE_VALUE_JOIN_RESPONSE] = "Join-Response", |
78 | | [FR_PACKET_TYPE_VALUE_RECONFIRM_REQUEST] = "Reconfirm-Request", |
79 | | [FR_PACKET_TYPE_VALUE_RECONFIRM_RESPONSE] = "Reconfirm-Response", |
80 | | }; |
81 | | |
82 | | |
83 | | bool fr_vmps_ok(uint8_t const *packet, size_t *packet_len) |
84 | 0 | { |
85 | 0 | uint8_t const *ptr; |
86 | 0 | ssize_t data_len; |
87 | 0 | int attrlen; |
88 | |
|
89 | 0 | if (*packet_len == FR_VQP_HDR_LEN) return true; |
90 | | |
91 | | /* |
92 | | * Skip the header. |
93 | | */ |
94 | 0 | ptr = packet + FR_VQP_HDR_LEN; |
95 | 0 | data_len = *packet_len - FR_VQP_HDR_LEN; |
96 | |
|
97 | 0 | while (data_len > 0) { |
98 | 0 | if (data_len < 7) { |
99 | 0 | fr_strerror_const("Packet contains malformed attribute"); |
100 | 0 | return false; |
101 | 0 | } |
102 | | |
103 | | /* |
104 | | * Attributes are 4 bytes |
105 | | * 0x00000c01 ... 0x00000c08 |
106 | | */ |
107 | 0 | if ((ptr[0] != 0) || (ptr[1] != 0) || |
108 | 0 | (ptr[2] != 0x0c) || (ptr[3] < 1) || (ptr[3] > 8)) { |
109 | 0 | fr_strerror_const("Packet contains invalid attribute"); |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | | /* |
114 | | * Length is 2 bytes |
115 | | */ |
116 | 0 | attrlen = fr_nbo_to_uint16(ptr + 4); |
117 | | |
118 | | /* |
119 | | * Total of attribute lengths shouldn't exceed *packet_len - header length, |
120 | | * which happens iff at some point, attrlen exceeds data_lan. |
121 | | */ |
122 | 0 | if (attrlen > data_len) { |
123 | 0 | fr_strerror_printf("Packet attributes cause total length " |
124 | 0 | "plus header length to exceed packet length %lx", |
125 | 0 | *packet_len); |
126 | 0 | return false; |
127 | 0 | } |
128 | | |
129 | | /* |
130 | | * We support short lengths, as there's no reason |
131 | | * for bigger lengths to exist... admins won't be |
132 | | * typing in a 32K vlan name. |
133 | | * |
134 | | * It's OK for ethernet frames to be longer. |
135 | | */ |
136 | 0 | if ((ptr[3] != 5) && (attrlen > 250)) { |
137 | 0 | fr_strerror_printf("Packet contains attribute with invalid length %02x %02x", ptr[4], ptr[5]); |
138 | 0 | return false; |
139 | 0 | } |
140 | | |
141 | 0 | ptr += 6 + attrlen; |
142 | 0 | data_len -= (6 + attrlen); |
143 | 0 | } |
144 | | |
145 | | /* |
146 | | * UDP reads may return too much data, so we truncate it. |
147 | | */ |
148 | 0 | *packet_len = ptr - packet; |
149 | |
|
150 | 0 | return true; |
151 | 0 | } |
152 | | |
153 | | |
154 | | int fr_vmps_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, unsigned int *code) |
155 | 406 | { |
156 | 406 | uint8_t const *ptr, *end; |
157 | 406 | int attr; |
158 | 406 | size_t attr_len; |
159 | 406 | fr_pair_t *vp; |
160 | | |
161 | 406 | if (data_len < FR_VQP_HDR_LEN) return -1; |
162 | | |
163 | 401 | vp = fr_pair_afrom_da(ctx, attr_packet_type); |
164 | 401 | if (!vp) { |
165 | 0 | oom: |
166 | 0 | fr_strerror_const("Out of Memory"); |
167 | 0 | return -1; |
168 | 0 | } |
169 | 401 | vp->vp_uint32 = data[1]; |
170 | 401 | if (code) *code = data[1]; |
171 | 401 | vp->vp_tainted = true; |
172 | 401 | fr_pair_append(out, vp); |
173 | | |
174 | 401 | vp = fr_pair_afrom_da(ctx, attr_error_code); |
175 | 401 | if (!vp) goto oom; |
176 | 401 | vp->vp_uint32 = data[2]; |
177 | 401 | vp->vp_tainted = true; |
178 | 401 | fr_pair_append(out, vp); |
179 | | |
180 | 401 | vp = fr_pair_afrom_da(ctx, attr_sequence_number); |
181 | 401 | if (!vp) goto oom; |
182 | | |
183 | 401 | vp->vp_uint32 = fr_nbo_to_uint32(data + 4); |
184 | 401 | vp->vp_tainted = true; |
185 | 401 | fr_pair_append(out, vp); |
186 | | |
187 | 401 | ptr = data + FR_VQP_HDR_LEN; |
188 | 401 | end = data + data_len; |
189 | | |
190 | | /* |
191 | | * Note that vmps_recv() MUST ensure that the packet is |
192 | | * formatted in a way we expect, and that vmps_recv() MUST |
193 | | * be called before vmps_decode(). |
194 | | */ |
195 | 1.39M | while (ptr < end) { |
196 | 1.39M | if ((end - ptr) < 6) { |
197 | 34 | fr_strerror_printf("Packet is too small. (%ld < 6)", (end - ptr)); |
198 | 34 | return -1; |
199 | 34 | } |
200 | | |
201 | 1.39M | attr = fr_nbo_to_uint16(ptr + 2); |
202 | 1.39M | attr_len = fr_nbo_to_uint16(ptr + 4); |
203 | 1.39M | ptr += 6; |
204 | | |
205 | | /* |
206 | | * fr_vmps_ok() should have checked this already, |
207 | | * but it doesn't hurt to do it again. |
208 | | */ |
209 | 1.39M | if (attr_len > (size_t) (end - ptr)) { |
210 | 44 | fr_strerror_const("Attribute length exceeds received data"); |
211 | 44 | return -1; |
212 | 44 | } |
213 | | |
214 | | /* |
215 | | * Create the VP. |
216 | | */ |
217 | 1.39M | vp = fr_pair_afrom_child_num(ctx, fr_dict_root(dict_vmps), attr); |
218 | 1.39M | if (!vp) { |
219 | 0 | fr_strerror_const("No memory"); |
220 | 0 | return -1; |
221 | 0 | } |
222 | | |
223 | | /* |
224 | | * Rely on value_box to do the work. |
225 | | * |
226 | | * @todo - if the attribute is malformed, create a "raw" one. |
227 | | */ |
228 | 1.39M | if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da, |
229 | 1.39M | &FR_DBUFF_TMP(ptr, attr_len), attr_len, true) < 0) { |
230 | 52 | talloc_free(vp); |
231 | 52 | return -1; |
232 | 52 | } |
233 | | |
234 | 1.39M | ptr += attr_len; |
235 | 1.39M | vp->vp_tainted = true; |
236 | 1.39M | fr_pair_append(out, vp); |
237 | 1.39M | } |
238 | | |
239 | | /* |
240 | | * FIXME: Map attributes to Calling-Station-Id, etc... |
241 | | */ |
242 | | |
243 | 271 | return data_len; |
244 | 401 | } |
245 | | |
246 | | #if 0 |
247 | | /* |
248 | | * These are the MUST HAVE contents for a VQP packet. |
249 | | * |
250 | | * We don't allow the caller to give less than these, because |
251 | | * it won't work. We don't encode more than these, because the |
252 | | * clients will ignore it. |
253 | | * |
254 | | * FIXME: Be more generous? Look for CISCO + VQP attributes? |
255 | | * |
256 | | * @todo - actually use these again... |
257 | | */ |
258 | | static int contents[5][VQP_MAX_ATTRIBUTES] = { |
259 | | { 0, 0, 0, 0, 0, 0 }, |
260 | | { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c05 }, /* Join request */ |
261 | | { 0x0c03, 0x0c08, 0, 0, 0, 0 }, /* Join Response */ |
262 | | { 0x0c01, 0x0c02, 0x0c03, 0x0c04, 0x0c07, 0x0c08 }, /* Reconfirm */ |
263 | | { 0x0c03, 0x0c08, 0, 0, 0, 0 } |
264 | | }; |
265 | | #endif |
266 | | |
267 | | ssize_t fr_vmps_encode(fr_dbuff_t *dbuff, uint8_t const *original, |
268 | | int code, uint32_t seq_no, fr_dcursor_t *cursor) |
269 | 271 | { |
270 | 271 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
271 | 271 | fr_pair_t *vp; |
272 | 271 | fr_dbuff_marker_t hdr, m; |
273 | 271 | uint8_t data_count = 0; |
274 | | |
275 | | /* |
276 | | * Let's keep a reference for packet header, and another |
277 | | * to let us write to to the encoding as needed. |
278 | | */ |
279 | 271 | fr_dbuff_marker(&hdr, &work_dbuff); |
280 | 271 | fr_dbuff_marker(&m, &work_dbuff); |
281 | | |
282 | | /* |
283 | | * Create the header |
284 | | */ |
285 | 271 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_VQP_VERSION, /* Version */ |
286 | 271 | code, /* Opcode */ |
287 | 271 | FR_ERROR_CODE_VALUE_NO_ERROR, /* Response Code */ |
288 | 271 | data_count); /* Data Count */ |
289 | | |
290 | 271 | if (original) { |
291 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, original + 4, 4); |
292 | 271 | } else { |
293 | 271 | FR_DBUFF_IN_RETURN(&work_dbuff, seq_no); |
294 | 271 | } |
295 | | |
296 | | /* |
297 | | * Encode the VP's. |
298 | | */ |
299 | 74.2k | while ((vp = fr_dcursor_current(cursor))) { |
300 | 74.0k | ssize_t slen; |
301 | | |
302 | 74.0k | if (vp->da == attr_packet_type) { |
303 | 1.18k | fr_dbuff_set(&m, fr_dbuff_current(&hdr) + 1); |
304 | 1.18k | FR_DBUFF_IN_RETURN(&m, (uint8_t)vp->vp_uint32); |
305 | 1.18k | fr_dcursor_next(cursor); |
306 | 1.18k | continue; |
307 | 1.18k | } |
308 | | |
309 | 72.8k | if (vp->da == attr_error_code) { |
310 | 1.20k | fr_dbuff_set(&m, fr_dbuff_current(&hdr) + 2); |
311 | 1.20k | FR_DBUFF_IN_RETURN(&m, vp->vp_uint8); |
312 | 1.20k | fr_dcursor_next(cursor); |
313 | 1.20k | continue; |
314 | 1.20k | } |
315 | | |
316 | 71.6k | if (!original && (vp->da == attr_sequence_number)) { |
317 | 361 | fr_dbuff_set(&m, fr_dbuff_current(&hdr) + 4); |
318 | 361 | FR_DBUFF_IN_RETURN(&m, vp->vp_uint32); |
319 | 361 | fr_dcursor_next(cursor); |
320 | 361 | continue; |
321 | 361 | } |
322 | | |
323 | 71.2k | if (!fr_type_is_leaf(vp->vp_type)) continue; |
324 | | |
325 | | /* |
326 | | * Type. Note that we look at only the lower 8 |
327 | | * bits, as the upper 8 bits have been hacked. |
328 | | * See also dictionary.vmps |
329 | | */ |
330 | | |
331 | | /* Type */ |
332 | 71.2k | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, 0x00, 0x00, 0x0c, (vp->da->attr & 0xff)); |
333 | | |
334 | | /* Length */ |
335 | 71.2k | fr_dbuff_set(&m, fr_dbuff_current(&work_dbuff)); |
336 | 71.2k | FR_DBUFF_IN_RETURN(&work_dbuff, (uint16_t) 0); |
337 | | |
338 | | /* Data */ |
339 | 71.2k | slen = fr_value_box_to_network(&work_dbuff, &vp->data); |
340 | 71.2k | if (slen < 0) return slen; |
341 | | |
342 | 71.2k | FR_DBUFF_IN_RETURN(&m, (uint16_t) slen); |
343 | | |
344 | 71.2k | data_count++; |
345 | | |
346 | 71.2k | fr_dcursor_next(cursor); |
347 | 71.2k | } |
348 | | |
349 | | /* |
350 | | * Update the Data Count |
351 | | */ |
352 | 230 | fr_dbuff_set(&m, fr_dbuff_current(&hdr) + 3); |
353 | 230 | FR_DBUFF_IN_RETURN(&m, data_count); |
354 | | |
355 | 230 | return fr_dbuff_set(dbuff, &work_dbuff); |
356 | 230 | } |
357 | | |
358 | | |
359 | | /** See how big of a packet is in the buffer. |
360 | | * |
361 | | * Packet is not 'const * const' because we may update data_len, if there's more data |
362 | | * in the UDP packet than in the VMPS packet. |
363 | | * |
364 | | * @param data pointer to the packet buffer |
365 | | * @param data_len length of the packet buffer |
366 | | * @return |
367 | | * <= 0 packet is bad. |
368 | | * >0 how much of the data is a packet (can be larger than data_len) |
369 | | */ |
370 | | ssize_t fr_vmps_packet_size(uint8_t const *data, size_t data_len) |
371 | 0 | { |
372 | 0 | int attributes; |
373 | 0 | uint8_t const *ptr, *end; |
374 | |
|
375 | 0 | if (data_len < FR_VQP_HDR_LEN) return FR_VQP_HDR_LEN; |
376 | | |
377 | | /* |
378 | | * No attributes. |
379 | | */ |
380 | 0 | if (data[3] == 0) return FR_VQP_HDR_LEN; |
381 | | |
382 | | /* |
383 | | * Too many attributes. Return an error indicating that |
384 | | * there's a problem with octet 3. |
385 | | */ |
386 | 0 | if (data[3] > 30) return -3; |
387 | | |
388 | | /* |
389 | | * Look for attributes. |
390 | | */ |
391 | 0 | ptr = data + FR_VQP_HDR_LEN; |
392 | 0 | attributes = data[3]; |
393 | |
|
394 | 0 | end = data + data_len; |
395 | |
|
396 | 0 | while (attributes > 0) { |
397 | 0 | size_t attr_len; |
398 | | |
399 | | /* |
400 | | * Not enough room for the attribute headers, we |
401 | | * want at least those. |
402 | | */ |
403 | 0 | if ((end - ptr) < 6) { |
404 | 0 | return 6 * attributes; |
405 | 0 | } |
406 | | |
407 | | /* |
408 | | * Length of the data NOT including the header. |
409 | | */ |
410 | 0 | attr_len = fr_nbo_to_uint16(ptr + 4); |
411 | |
|
412 | 0 | ptr += 6 + attr_len; |
413 | | |
414 | | /* |
415 | | * We don't want to read infinite amounts of data. |
416 | | * |
417 | | * Return an error indicating that there's a |
418 | | * problem with the final octet |
419 | | */ |
420 | 0 | if ((ptr - data) > 4096) { |
421 | 0 | return -(ptr - data); |
422 | 0 | } |
423 | | |
424 | | /* |
425 | | * This attribute has been checked. |
426 | | */ |
427 | 0 | attributes--; |
428 | | |
429 | | /* |
430 | | * The packet we want is larger than the input |
431 | | * buffer, so we return the length of the current |
432 | | * attribute, plus the length of the remaining |
433 | | * headers. |
434 | | */ |
435 | 0 | if (ptr > end) return (6 * attributes) + ptr - data; |
436 | 0 | } |
437 | | |
438 | | /* |
439 | | * We've reached the end of the packet. |
440 | | */ |
441 | 0 | return ptr - data; |
442 | 0 | } |
443 | | |
444 | | static void print_hex_data(uint8_t const *ptr, int attrlen, int depth) |
445 | 0 | { |
446 | 0 | int i; |
447 | 0 | static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; |
448 | |
|
449 | 0 | for (i = 0; i < attrlen; i++) { |
450 | 0 | if ((i > 0) && ((i & 0x0f) == 0x00)) |
451 | 0 | fprintf(fr_log_fp, "%.*s", depth, tabs); |
452 | 0 | fprintf(fr_log_fp, "%02x ", ptr[i]); |
453 | 0 | if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); |
454 | 0 | } |
455 | 0 | if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n"); |
456 | 0 | } |
457 | | |
458 | | |
459 | | /** Print a raw VMPS packet as hex. |
460 | | * |
461 | | */ |
462 | | void fr_vmps_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len) |
463 | 0 | { |
464 | 0 | uint32_t length; |
465 | 0 | uint8_t const *attr, *end; |
466 | 0 | uint32_t id; |
467 | |
|
468 | 0 | if (packet_len < 8) return; |
469 | | |
470 | 0 | fprintf(fp, " Version:\t\t%u\n", packet[0]); |
471 | |
|
472 | 0 | if ((packet[1] > 0) && (packet[1] < FR_VMPS_CODE_MAX) && fr_vmps_packet_names[packet[1]]) { |
473 | 0 | fprintf(fp, " OpCode:\t\t%s\n", fr_vmps_packet_names[packet[1]]); |
474 | 0 | } else { |
475 | 0 | fprintf(fp, " OpCode:\t\t%u\n", packet[1]); |
476 | 0 | } |
477 | |
|
478 | 0 | if ((packet[2] > 0) && (packet[2] < FR_VMPS_CODE_MAX) && fr_vmps_packet_names[packet[2]]) { |
479 | 0 | fprintf(fp, " OpCode:\t\t%s\n", fr_vmps_packet_names[packet[2]]); |
480 | 0 | } else { |
481 | 0 | fprintf(fp, " OpCode:\t\t%u\n", packet[2]); |
482 | 0 | } |
483 | |
|
484 | 0 | fprintf(fp, " Data Count:\t\t%u\n", packet[3]); |
485 | |
|
486 | 0 | memcpy(&id, packet + 4, 4); |
487 | 0 | id = ntohl(id); |
488 | |
|
489 | 0 | fprintf(fp, " ID:\t%08x\n", id); |
490 | |
|
491 | 0 | if (packet_len == 8) return; |
492 | | |
493 | 0 | for (attr = packet + 8, end = packet + packet_len; |
494 | 0 | attr < end; |
495 | 0 | attr += length) { |
496 | 0 | memcpy(&id, attr, 4); |
497 | 0 | id = ntohl(id); |
498 | |
|
499 | 0 | length = fr_nbo_to_uint16(attr + 4); |
500 | 0 | if (length > (end - attr)) break; |
501 | | |
502 | 0 | fprintf(fp, "\t\t%08x %04x ", id, length); |
503 | |
|
504 | 0 | print_hex_data(attr + 6, length - 6, 3); |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | | /* |
509 | | * Test points for protocol decode |
510 | | */ |
511 | | static ssize_t fr_vmps_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, |
512 | | uint8_t const *data, size_t data_len, void *proto_ctx) |
513 | 406 | { |
514 | 406 | return fr_vmps_decode(ctx, out, data, data_len, proto_ctx); |
515 | 406 | } |
516 | | |
517 | | static int _decode_test_ctx(UNUSED fr_vmps_ctx_t *proto_ctx) |
518 | 406 | { |
519 | 406 | fr_vmps_global_free(); |
520 | | |
521 | 406 | return 0; |
522 | 406 | } |
523 | | |
524 | | static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict, |
525 | | UNUSED fr_dict_attr_t const *root_da) |
526 | 406 | { |
527 | 406 | fr_vmps_ctx_t *test_ctx; |
528 | | |
529 | 406 | if (fr_vmps_global_init() < 0) return -1; |
530 | | |
531 | 406 | test_ctx = talloc_zero(ctx, fr_vmps_ctx_t); |
532 | 406 | if (!test_ctx) return -1; |
533 | | |
534 | 406 | talloc_set_destructor(test_ctx, _decode_test_ctx); |
535 | | |
536 | 406 | *out = test_ctx; |
537 | | |
538 | 406 | return 0; |
539 | 406 | } |
540 | | |
541 | | extern fr_test_point_proto_decode_t vmps_tp_decode_proto; |
542 | | fr_test_point_proto_decode_t vmps_tp_decode_proto = { |
543 | | .test_ctx = decode_test_ctx, |
544 | | .func = fr_vmps_decode_proto |
545 | | }; |
546 | | |
547 | | /* |
548 | | * Test points for protocol encode |
549 | | */ |
550 | | static ssize_t fr_vmps_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx) |
551 | 271 | { |
552 | 271 | fr_dcursor_t cursor; |
553 | | |
554 | 271 | fr_pair_dcursor_iter_init(&cursor, vps, fr_proto_next_encodable, dict_vmps); |
555 | | |
556 | 271 | return fr_vmps_encode(&FR_DBUFF_TMP(data, data_len), NULL, -1, -1, &cursor); |
557 | 271 | } |
558 | | |
559 | | static int _encode_test_ctx(UNUSED fr_vmps_ctx_t *proto_ctx) |
560 | 406 | { |
561 | 406 | fr_vmps_global_free(); |
562 | | |
563 | 406 | return 0; |
564 | 406 | } |
565 | | |
566 | | static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict, |
567 | | UNUSED fr_dict_attr_t const *root_da) |
568 | 406 | { |
569 | 406 | fr_vmps_ctx_t *test_ctx; |
570 | | |
571 | 406 | if (fr_vmps_global_init() < 0) return -1; |
572 | | |
573 | 406 | test_ctx = talloc_zero(ctx, fr_vmps_ctx_t); |
574 | 406 | if (!test_ctx) return -1; |
575 | | |
576 | 406 | talloc_set_destructor(test_ctx, _encode_test_ctx); |
577 | | |
578 | 406 | *out = test_ctx; |
579 | | |
580 | 406 | return 0; |
581 | 406 | } |
582 | | |
583 | | extern fr_test_point_proto_encode_t vmps_tp_encode_proto; |
584 | | fr_test_point_proto_encode_t vmps_tp_encode_proto = { |
585 | | .test_ctx = encode_test_ctx, |
586 | | .func = fr_vmps_encode_proto |
587 | | }; |