Commit | Line | Data |
---|---|---|
984263bc MD |
1 | |
2 | /* | |
3 | * ng_parse.c | |
4 | * | |
5 | * Copyright (c) 1999 Whistle Communications, Inc. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Subject to the following obligations and disclaimer of warranty, use and | |
9 | * redistribution of this software, in source or object code forms, with or | |
10 | * without modifications are expressly permitted by Whistle Communications; | |
11 | * provided, however, that: | |
12 | * 1. Any and all reproductions of the source or object code must include the | |
13 | * copyright notice above and the following disclaimer of warranties; and | |
14 | * 2. No rights are granted, in any manner or form, to use Whistle | |
15 | * Communications, Inc. trademarks, including the mark "WHISTLE | |
16 | * COMMUNICATIONS" on advertising, endorsements, or otherwise except as | |
17 | * such appears in the above copyright notice or in the software. | |
18 | * | |
19 | * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND | |
20 | * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO | |
21 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, | |
22 | * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF | |
23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. | |
24 | * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY | |
25 | * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS | |
26 | * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. | |
27 | * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES | |
28 | * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING | |
29 | * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
30 | * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR | |
31 | * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY | |
32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
34 | * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY | |
35 | * OF SUCH DAMAGE. | |
36 | * | |
37 | * Author: Archie Cobbs <archie@freebsd.org> | |
38 | * | |
39 | * $Whistle: ng_parse.c,v 1.3 1999/11/29 01:43:48 archie Exp $ | |
40 | * $FreeBSD: src/sys/netgraph/ng_parse.c,v 1.3.2.8 2002/07/02 23:44:02 archie Exp $ | |
41 | */ | |
42 | ||
43 | #include <sys/types.h> | |
44 | #include <sys/param.h> | |
45 | #include <sys/systm.h> | |
46 | #include <sys/errno.h> | |
47 | #include <sys/malloc.h> | |
48 | #include <sys/ctype.h> | |
49 | ||
50 | #include <netinet/in.h> | |
51 | ||
52 | #include <netgraph/ng_message.h> | |
53 | #include <netgraph/netgraph.h> | |
d2438d69 | 54 | #include <netgraph/ng_parse.h> |
984263bc MD |
55 | |
56 | /* Compute alignment for primitive integral types */ | |
57 | struct int16_temp { | |
58 | char x; | |
59 | int16_t y; | |
60 | }; | |
61 | ||
62 | struct int32_temp { | |
63 | char x; | |
64 | int32_t y; | |
65 | }; | |
66 | ||
67 | struct int64_temp { | |
68 | char x; | |
69 | int64_t y; | |
70 | }; | |
71 | ||
72 | #define INT8_ALIGNMENT 1 | |
3f8371af SW |
73 | #define INT16_ALIGNMENT ((uintptr_t)&((struct int16_temp *)0)->y) |
74 | #define INT32_ALIGNMENT ((uintptr_t)&((struct int32_temp *)0)->y) | |
75 | #define INT64_ALIGNMENT ((uintptr_t)&((struct int64_temp *)0)->y) | |
984263bc MD |
76 | |
77 | /* Output format for integral types */ | |
78 | #define INT_UNSIGNED 0 | |
79 | #define INT_SIGNED 1 | |
80 | #define INT_HEX 2 | |
81 | ||
82 | /* Type of composite object: struct, array, or fixedarray */ | |
83 | enum comptype { | |
84 | CT_STRUCT, | |
85 | CT_ARRAY, | |
86 | CT_FIXEDARRAY, | |
87 | }; | |
88 | ||
89 | /* Composite types helper functions */ | |
90 | static int ng_parse_composite(const struct ng_parse_type *type, | |
91 | const char *s, int *off, const u_char *start, | |
92 | u_char *const buf, int *buflen, enum comptype ctype); | |
93 | static int ng_unparse_composite(const struct ng_parse_type *type, | |
94 | const u_char *data, int *off, char *cbuf, int cbuflen, | |
95 | enum comptype ctype); | |
96 | static int ng_get_composite_elem_default(const struct ng_parse_type *type, | |
97 | int index, const u_char *start, u_char *buf, | |
98 | int *buflen, enum comptype ctype); | |
99 | static int ng_get_composite_len(const struct ng_parse_type *type, | |
100 | const u_char *start, const u_char *buf, | |
101 | enum comptype ctype); | |
102 | static const struct ng_parse_type *ng_get_composite_etype(const struct | |
103 | ng_parse_type *type, int index, enum comptype ctype); | |
104 | static int ng_parse_get_elem_pad(const struct ng_parse_type *type, | |
105 | int index, enum comptype ctype, int posn); | |
106 | ||
107 | /* Parsing helper functions */ | |
108 | static int ng_parse_skip_value(const char *s, int off, int *lenp); | |
109 | ||
110 | /* Poor man's virtual method calls */ | |
111 | #define METHOD(t,m) (ng_get_ ## m ## _method(t)) | |
112 | #define INVOKE(t,m) (*METHOD(t,m)) | |
113 | ||
114 | static ng_parse_t *ng_get_parse_method(const struct ng_parse_type *t); | |
115 | static ng_unparse_t *ng_get_unparse_method(const struct ng_parse_type *t); | |
116 | static ng_getDefault_t *ng_get_getDefault_method(const | |
117 | struct ng_parse_type *t); | |
118 | static ng_getAlign_t *ng_get_getAlign_method(const struct ng_parse_type *t); | |
119 | ||
120 | #define ALIGNMENT(t) (METHOD(t, getAlign) == NULL ? \ | |
121 | 0 : INVOKE(t, getAlign)(t)) | |
122 | ||
123 | /* For converting binary to string */ | |
124 | #define NG_PARSE_APPEND(fmt, args...) \ | |
125 | do { \ | |
126 | int len; \ | |
127 | \ | |
f8c7a42d | 128 | len = ksnprintf((cbuf), (cbuflen), \ |
984263bc MD |
129 | fmt , ## args); \ |
130 | if (len >= (cbuflen)) \ | |
131 | return (ERANGE); \ | |
132 | (cbuf) += len; \ | |
133 | (cbuflen) -= len; \ | |
134 | } while (0) | |
135 | ||
136 | /************************************************************************ | |
137 | PUBLIC FUNCTIONS | |
138 | ************************************************************************/ | |
139 | ||
140 | /* | |
141 | * Convert an ASCII string to binary according to the supplied type descriptor | |
142 | */ | |
143 | int | |
144 | ng_parse(const struct ng_parse_type *type, | |
145 | const char *string, int *off, u_char *buf, int *buflen) | |
146 | { | |
147 | return INVOKE(type, parse)(type, string, off, buf, buf, buflen); | |
148 | } | |
149 | ||
150 | /* | |
151 | * Convert binary to an ASCII string according to the supplied type descriptor | |
152 | */ | |
153 | int | |
154 | ng_unparse(const struct ng_parse_type *type, | |
155 | const u_char *data, char *cbuf, int cbuflen) | |
156 | { | |
157 | int off = 0; | |
158 | ||
159 | return INVOKE(type, unparse)(type, data, &off, cbuf, cbuflen); | |
160 | } | |
161 | ||
162 | /* | |
163 | * Fill in the default value according to the supplied type descriptor | |
164 | */ | |
165 | int | |
166 | ng_parse_getDefault(const struct ng_parse_type *type, u_char *buf, int *buflen) | |
167 | { | |
168 | ng_getDefault_t *const func = METHOD(type, getDefault); | |
169 | ||
170 | if (func == NULL) | |
171 | return (EOPNOTSUPP); | |
172 | return (*func)(type, buf, buf, buflen); | |
173 | } | |
174 | ||
175 | ||
176 | /************************************************************************ | |
177 | STRUCTURE TYPE | |
178 | ************************************************************************/ | |
179 | ||
180 | static int | |
181 | ng_struct_parse(const struct ng_parse_type *type, | |
182 | const char *s, int *off, const u_char *const start, | |
183 | u_char *const buf, int *buflen) | |
184 | { | |
185 | return ng_parse_composite(type, s, off, start, buf, buflen, CT_STRUCT); | |
186 | } | |
187 | ||
188 | static int | |
189 | ng_struct_unparse(const struct ng_parse_type *type, | |
190 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
191 | { | |
192 | return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_STRUCT); | |
193 | } | |
194 | ||
195 | static int | |
196 | ng_struct_getDefault(const struct ng_parse_type *type, | |
197 | const u_char *const start, u_char *buf, int *buflen) | |
198 | { | |
199 | int off = 0; | |
200 | ||
201 | return ng_parse_composite(type, | |
202 | "{}", &off, start, buf, buflen, CT_STRUCT); | |
203 | } | |
204 | ||
205 | static int | |
206 | ng_struct_getAlign(const struct ng_parse_type *type) | |
207 | { | |
208 | const struct ng_parse_struct_field *field; | |
209 | int align = 0; | |
210 | ||
211 | for (field = type->info; field->name != NULL; field++) { | |
212 | int falign = ALIGNMENT(field->type); | |
213 | ||
214 | if (falign > align) | |
215 | align = falign; | |
216 | } | |
217 | return align; | |
218 | } | |
219 | ||
220 | const struct ng_parse_type ng_parse_struct_type = { | |
221 | NULL, | |
222 | NULL, | |
223 | NULL, | |
224 | ng_struct_parse, | |
225 | ng_struct_unparse, | |
226 | ng_struct_getDefault, | |
227 | ng_struct_getAlign | |
228 | }; | |
229 | ||
230 | /************************************************************************ | |
231 | FIXED LENGTH ARRAY TYPE | |
232 | ************************************************************************/ | |
233 | ||
234 | static int | |
235 | ng_fixedarray_parse(const struct ng_parse_type *type, | |
236 | const char *s, int *off, const u_char *const start, | |
237 | u_char *const buf, int *buflen) | |
238 | { | |
239 | return ng_parse_composite(type, | |
240 | s, off, start, buf, buflen, CT_FIXEDARRAY); | |
241 | } | |
242 | ||
243 | static int | |
244 | ng_fixedarray_unparse(const struct ng_parse_type *type, | |
245 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
246 | { | |
247 | return ng_unparse_composite(type, | |
248 | data, off, cbuf, cbuflen, CT_FIXEDARRAY); | |
249 | } | |
250 | ||
251 | static int | |
252 | ng_fixedarray_getDefault(const struct ng_parse_type *type, | |
253 | const u_char *const start, u_char *buf, int *buflen) | |
254 | { | |
255 | int off = 0; | |
256 | ||
257 | return ng_parse_composite(type, | |
258 | "[]", &off, start, buf, buflen, CT_FIXEDARRAY); | |
259 | } | |
260 | ||
261 | static int | |
262 | ng_fixedarray_getAlign(const struct ng_parse_type *type) | |
263 | { | |
264 | const struct ng_parse_fixedarray_info *fi = type->info; | |
265 | ||
266 | return ALIGNMENT(fi->elementType); | |
267 | } | |
268 | ||
269 | const struct ng_parse_type ng_parse_fixedarray_type = { | |
270 | NULL, | |
271 | NULL, | |
272 | NULL, | |
273 | ng_fixedarray_parse, | |
274 | ng_fixedarray_unparse, | |
275 | ng_fixedarray_getDefault, | |
276 | ng_fixedarray_getAlign | |
277 | }; | |
278 | ||
279 | /************************************************************************ | |
280 | VARIABLE LENGTH ARRAY TYPE | |
281 | ************************************************************************/ | |
282 | ||
283 | static int | |
284 | ng_array_parse(const struct ng_parse_type *type, | |
285 | const char *s, int *off, const u_char *const start, | |
286 | u_char *const buf, int *buflen) | |
287 | { | |
288 | return ng_parse_composite(type, s, off, start, buf, buflen, CT_ARRAY); | |
289 | } | |
290 | ||
291 | static int | |
292 | ng_array_unparse(const struct ng_parse_type *type, | |
293 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
294 | { | |
295 | return ng_unparse_composite(type, data, off, cbuf, cbuflen, CT_ARRAY); | |
296 | } | |
297 | ||
298 | static int | |
299 | ng_array_getDefault(const struct ng_parse_type *type, | |
300 | const u_char *const start, u_char *buf, int *buflen) | |
301 | { | |
302 | int off = 0; | |
303 | ||
304 | return ng_parse_composite(type, | |
305 | "[]", &off, start, buf, buflen, CT_ARRAY); | |
306 | } | |
307 | ||
308 | static int | |
309 | ng_array_getAlign(const struct ng_parse_type *type) | |
310 | { | |
311 | const struct ng_parse_array_info *ai = type->info; | |
312 | ||
313 | return ALIGNMENT(ai->elementType); | |
314 | } | |
315 | ||
316 | const struct ng_parse_type ng_parse_array_type = { | |
317 | NULL, | |
318 | NULL, | |
319 | NULL, | |
320 | ng_array_parse, | |
321 | ng_array_unparse, | |
322 | ng_array_getDefault, | |
323 | ng_array_getAlign | |
324 | }; | |
325 | ||
326 | /************************************************************************ | |
327 | INT8 TYPE | |
328 | ************************************************************************/ | |
329 | ||
330 | static int | |
331 | ng_int8_parse(const struct ng_parse_type *type, | |
332 | const char *s, int *off, const u_char *const start, | |
333 | u_char *const buf, int *buflen) | |
334 | { | |
335 | long val; | |
336 | int8_t val8; | |
337 | char *eptr; | |
338 | ||
339 | val = strtol(s + *off, &eptr, 0); | |
340 | if (val < (int8_t)0x80 || val > (u_int8_t)0xff || eptr == s + *off) | |
341 | return (EINVAL); | |
342 | *off = eptr - s; | |
343 | val8 = (int8_t)val; | |
344 | bcopy(&val8, buf, sizeof(int8_t)); | |
345 | *buflen = sizeof(int8_t); | |
346 | return (0); | |
347 | } | |
348 | ||
349 | static int | |
350 | ng_int8_unparse(const struct ng_parse_type *type, | |
351 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
352 | { | |
353 | const char *fmt; | |
354 | int fval; | |
355 | int8_t val; | |
356 | ||
357 | bcopy(data + *off, &val, sizeof(int8_t)); | |
973c11b9 | 358 | switch ((int)(intptr_t)type->info) { |
984263bc MD |
359 | case INT_SIGNED: |
360 | fmt = "%d"; | |
361 | fval = val; | |
362 | break; | |
363 | case INT_UNSIGNED: | |
364 | fmt = "%u"; | |
365 | fval = (u_int8_t)val; | |
366 | break; | |
367 | case INT_HEX: | |
368 | fmt = "0x%x"; | |
369 | fval = (u_int8_t)val; | |
370 | break; | |
371 | default: | |
9eb57b71 | 372 | panic("%s: unknown type", __func__); |
984263bc MD |
373 | } |
374 | NG_PARSE_APPEND(fmt, fval); | |
375 | *off += sizeof(int8_t); | |
376 | return (0); | |
377 | } | |
378 | ||
379 | static int | |
380 | ng_int8_getDefault(const struct ng_parse_type *type, | |
381 | const u_char *const start, u_char *buf, int *buflen) | |
382 | { | |
383 | int8_t val; | |
384 | ||
385 | if (*buflen < sizeof(int8_t)) | |
386 | return (ERANGE); | |
387 | val = 0; | |
388 | bcopy(&val, buf, sizeof(int8_t)); | |
389 | *buflen = sizeof(int8_t); | |
390 | return (0); | |
391 | } | |
392 | ||
393 | static int | |
394 | ng_int8_getAlign(const struct ng_parse_type *type) | |
395 | { | |
396 | return INT8_ALIGNMENT; | |
397 | } | |
398 | ||
399 | const struct ng_parse_type ng_parse_int8_type = { | |
400 | NULL, | |
401 | (void *)INT_SIGNED, | |
402 | NULL, | |
403 | ng_int8_parse, | |
404 | ng_int8_unparse, | |
405 | ng_int8_getDefault, | |
406 | ng_int8_getAlign | |
407 | }; | |
408 | ||
409 | const struct ng_parse_type ng_parse_uint8_type = { | |
410 | &ng_parse_int8_type, | |
411 | (void *)INT_UNSIGNED | |
412 | }; | |
413 | ||
414 | const struct ng_parse_type ng_parse_hint8_type = { | |
415 | &ng_parse_int8_type, | |
416 | (void *)INT_HEX | |
417 | }; | |
418 | ||
419 | /************************************************************************ | |
420 | INT16 TYPE | |
421 | ************************************************************************/ | |
422 | ||
423 | static int | |
424 | ng_int16_parse(const struct ng_parse_type *type, | |
425 | const char *s, int *off, const u_char *const start, | |
426 | u_char *const buf, int *buflen) | |
427 | { | |
428 | long val; | |
429 | int16_t val16; | |
430 | char *eptr; | |
431 | ||
432 | val = strtol(s + *off, &eptr, 0); | |
433 | if (val < (int16_t)0x8000 | |
434 | || val > (u_int16_t)0xffff || eptr == s + *off) | |
435 | return (EINVAL); | |
436 | *off = eptr - s; | |
437 | val16 = (int16_t)val; | |
438 | bcopy(&val16, buf, sizeof(int16_t)); | |
439 | *buflen = sizeof(int16_t); | |
440 | return (0); | |
441 | } | |
442 | ||
443 | static int | |
444 | ng_int16_unparse(const struct ng_parse_type *type, | |
445 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
446 | { | |
447 | const char *fmt; | |
448 | int fval; | |
449 | int16_t val; | |
450 | ||
451 | bcopy(data + *off, &val, sizeof(int16_t)); | |
973c11b9 | 452 | switch ((int)(intptr_t)type->info) { |
984263bc MD |
453 | case INT_SIGNED: |
454 | fmt = "%d"; | |
455 | fval = val; | |
456 | break; | |
457 | case INT_UNSIGNED: | |
458 | fmt = "%u"; | |
459 | fval = (u_int16_t)val; | |
460 | break; | |
461 | case INT_HEX: | |
462 | fmt = "0x%x"; | |
463 | fval = (u_int16_t)val; | |
464 | break; | |
465 | default: | |
9eb57b71 | 466 | panic("%s: unknown type", __func__); |
984263bc MD |
467 | } |
468 | NG_PARSE_APPEND(fmt, fval); | |
469 | *off += sizeof(int16_t); | |
470 | return (0); | |
471 | } | |
472 | ||
473 | static int | |
474 | ng_int16_getDefault(const struct ng_parse_type *type, | |
475 | const u_char *const start, u_char *buf, int *buflen) | |
476 | { | |
477 | int16_t val; | |
478 | ||
479 | if (*buflen < sizeof(int16_t)) | |
480 | return (ERANGE); | |
481 | val = 0; | |
482 | bcopy(&val, buf, sizeof(int16_t)); | |
483 | *buflen = sizeof(int16_t); | |
484 | return (0); | |
485 | } | |
486 | ||
487 | static int | |
488 | ng_int16_getAlign(const struct ng_parse_type *type) | |
489 | { | |
490 | return INT16_ALIGNMENT; | |
491 | } | |
492 | ||
493 | const struct ng_parse_type ng_parse_int16_type = { | |
494 | NULL, | |
495 | (void *)INT_SIGNED, | |
496 | NULL, | |
497 | ng_int16_parse, | |
498 | ng_int16_unparse, | |
499 | ng_int16_getDefault, | |
500 | ng_int16_getAlign | |
501 | }; | |
502 | ||
503 | const struct ng_parse_type ng_parse_uint16_type = { | |
504 | &ng_parse_int16_type, | |
505 | (void *)INT_UNSIGNED | |
506 | }; | |
507 | ||
508 | const struct ng_parse_type ng_parse_hint16_type = { | |
509 | &ng_parse_int16_type, | |
510 | (void *)INT_HEX | |
511 | }; | |
512 | ||
513 | /************************************************************************ | |
514 | INT32 TYPE | |
515 | ************************************************************************/ | |
516 | ||
517 | static int | |
518 | ng_int32_parse(const struct ng_parse_type *type, | |
519 | const char *s, int *off, const u_char *const start, | |
520 | u_char *const buf, int *buflen) | |
521 | { | |
522 | long val; /* assumes long is at least 32 bits */ | |
523 | int32_t val32; | |
524 | char *eptr; | |
525 | ||
526 | val = strtol(s + *off, &eptr, 0); | |
527 | if (val < (int32_t)0x80000000 | |
528 | || val > (u_int32_t)0xffffffff || eptr == s + *off) | |
529 | return (EINVAL); | |
530 | *off = eptr - s; | |
531 | val32 = (int32_t)val; | |
532 | bcopy(&val32, buf, sizeof(int32_t)); | |
533 | *buflen = sizeof(int32_t); | |
534 | return (0); | |
535 | } | |
536 | ||
537 | static int | |
538 | ng_int32_unparse(const struct ng_parse_type *type, | |
539 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
540 | { | |
541 | const char *fmt; | |
542 | long fval; | |
543 | int32_t val; | |
544 | ||
545 | bcopy(data + *off, &val, sizeof(int32_t)); | |
973c11b9 | 546 | switch ((int)(intptr_t)type->info) { |
984263bc MD |
547 | case INT_SIGNED: |
548 | fmt = "%ld"; | |
549 | fval = val; | |
550 | break; | |
551 | case INT_UNSIGNED: | |
552 | fmt = "%lu"; | |
553 | fval = (u_int32_t)val; | |
554 | break; | |
555 | case INT_HEX: | |
556 | fmt = "0x%lx"; | |
557 | fval = (u_int32_t)val; | |
558 | break; | |
559 | default: | |
9eb57b71 | 560 | panic("%s: unknown type", __func__); |
984263bc MD |
561 | } |
562 | NG_PARSE_APPEND(fmt, fval); | |
563 | *off += sizeof(int32_t); | |
564 | return (0); | |
565 | } | |
566 | ||
567 | static int | |
568 | ng_int32_getDefault(const struct ng_parse_type *type, | |
569 | const u_char *const start, u_char *buf, int *buflen) | |
570 | { | |
571 | int32_t val; | |
572 | ||
573 | if (*buflen < sizeof(int32_t)) | |
574 | return (ERANGE); | |
575 | val = 0; | |
576 | bcopy(&val, buf, sizeof(int32_t)); | |
577 | *buflen = sizeof(int32_t); | |
578 | return (0); | |
579 | } | |
580 | ||
581 | static int | |
582 | ng_int32_getAlign(const struct ng_parse_type *type) | |
583 | { | |
584 | return INT32_ALIGNMENT; | |
585 | } | |
586 | ||
587 | const struct ng_parse_type ng_parse_int32_type = { | |
588 | NULL, | |
589 | (void *)INT_SIGNED, | |
590 | NULL, | |
591 | ng_int32_parse, | |
592 | ng_int32_unparse, | |
593 | ng_int32_getDefault, | |
594 | ng_int32_getAlign | |
595 | }; | |
596 | ||
597 | const struct ng_parse_type ng_parse_uint32_type = { | |
598 | &ng_parse_int32_type, | |
599 | (void *)INT_UNSIGNED | |
600 | }; | |
601 | ||
602 | const struct ng_parse_type ng_parse_hint32_type = { | |
603 | &ng_parse_int32_type, | |
604 | (void *)INT_HEX | |
605 | }; | |
606 | ||
607 | /************************************************************************ | |
608 | INT64 TYPE | |
609 | ************************************************************************/ | |
610 | ||
611 | static int | |
612 | ng_int64_parse(const struct ng_parse_type *type, | |
613 | const char *s, int *off, const u_char *const start, | |
614 | u_char *const buf, int *buflen) | |
615 | { | |
616 | quad_t val; | |
617 | int64_t val64; | |
618 | char *eptr; | |
619 | ||
620 | val = strtoq(s + *off, &eptr, 0); | |
621 | if (eptr == s + *off) | |
622 | return (EINVAL); | |
623 | *off = eptr - s; | |
624 | val64 = (int64_t)val; | |
625 | bcopy(&val64, buf, sizeof(int64_t)); | |
626 | *buflen = sizeof(int64_t); | |
627 | return (0); | |
628 | } | |
629 | ||
630 | static int | |
631 | ng_int64_unparse(const struct ng_parse_type *type, | |
632 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
633 | { | |
634 | const char *fmt; | |
635 | long long fval; | |
636 | int64_t val; | |
637 | ||
638 | bcopy(data + *off, &val, sizeof(int64_t)); | |
973c11b9 | 639 | switch ((int)(intptr_t)type->info) { |
984263bc MD |
640 | case INT_SIGNED: |
641 | fmt = "%lld"; | |
642 | fval = val; | |
643 | break; | |
644 | case INT_UNSIGNED: | |
645 | fmt = "%llu"; | |
646 | fval = (u_int64_t)val; | |
647 | break; | |
648 | case INT_HEX: | |
649 | fmt = "0x%llx"; | |
650 | fval = (u_int64_t)val; | |
651 | break; | |
652 | default: | |
9eb57b71 | 653 | panic("%s: unknown type", __func__); |
984263bc MD |
654 | } |
655 | NG_PARSE_APPEND(fmt, fval); | |
656 | *off += sizeof(int64_t); | |
657 | return (0); | |
658 | } | |
659 | ||
660 | static int | |
661 | ng_int64_getDefault(const struct ng_parse_type *type, | |
662 | const u_char *const start, u_char *buf, int *buflen) | |
663 | { | |
664 | int64_t val; | |
665 | ||
666 | if (*buflen < sizeof(int64_t)) | |
667 | return (ERANGE); | |
668 | val = 0; | |
669 | bcopy(&val, buf, sizeof(int64_t)); | |
670 | *buflen = sizeof(int64_t); | |
671 | return (0); | |
672 | } | |
673 | ||
674 | static int | |
675 | ng_int64_getAlign(const struct ng_parse_type *type) | |
676 | { | |
677 | return INT64_ALIGNMENT; | |
678 | } | |
679 | ||
680 | const struct ng_parse_type ng_parse_int64_type = { | |
681 | NULL, | |
682 | (void *)INT_SIGNED, | |
683 | NULL, | |
684 | ng_int64_parse, | |
685 | ng_int64_unparse, | |
686 | ng_int64_getDefault, | |
687 | ng_int64_getAlign | |
688 | }; | |
689 | ||
690 | const struct ng_parse_type ng_parse_uint64_type = { | |
691 | &ng_parse_int64_type, | |
692 | (void *)INT_UNSIGNED | |
693 | }; | |
694 | ||
695 | const struct ng_parse_type ng_parse_hint64_type = { | |
696 | &ng_parse_int64_type, | |
697 | (void *)INT_HEX | |
698 | }; | |
699 | ||
700 | /************************************************************************ | |
701 | STRING TYPE | |
702 | ************************************************************************/ | |
703 | ||
704 | static int | |
705 | ng_string_parse(const struct ng_parse_type *type, | |
706 | const char *s, int *off, const u_char *const start, | |
707 | u_char *const buf, int *buflen) | |
708 | { | |
709 | char *sval; | |
710 | int len; | |
711 | ||
712 | if ((sval = ng_get_string_token(s, off, &len)) == NULL) | |
713 | return (EINVAL); | |
714 | *off += len; | |
715 | len = strlen(sval) + 1; | |
716 | bcopy(sval, buf, len); | |
884717e1 | 717 | kfree(sval, M_NETGRAPH); |
984263bc MD |
718 | *buflen = len; |
719 | return (0); | |
720 | } | |
721 | ||
722 | static int | |
723 | ng_string_unparse(const struct ng_parse_type *type, | |
724 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
725 | { | |
726 | const char *const raw = (const char *)data + *off; | |
727 | char *const s = ng_encode_string(raw); | |
728 | ||
729 | if (s == NULL) | |
730 | return (ENOMEM); | |
731 | NG_PARSE_APPEND("%s", s); | |
732 | *off += strlen(raw) + 1; | |
884717e1 | 733 | kfree(s, M_NETGRAPH); |
984263bc MD |
734 | return (0); |
735 | } | |
736 | ||
737 | static int | |
738 | ng_string_getDefault(const struct ng_parse_type *type, | |
739 | const u_char *const start, u_char *buf, int *buflen) | |
740 | { | |
741 | ||
742 | if (*buflen < 1) | |
743 | return (ERANGE); | |
744 | buf[0] = (u_char)'\0'; | |
745 | *buflen = 1; | |
746 | return (0); | |
747 | } | |
748 | ||
749 | const struct ng_parse_type ng_parse_string_type = { | |
750 | NULL, | |
751 | NULL, | |
752 | NULL, | |
753 | ng_string_parse, | |
754 | ng_string_unparse, | |
755 | ng_string_getDefault, | |
756 | NULL | |
757 | }; | |
758 | ||
759 | /************************************************************************ | |
760 | FIXED BUFFER STRING TYPE | |
761 | ************************************************************************/ | |
762 | ||
763 | static int | |
764 | ng_fixedstring_parse(const struct ng_parse_type *type, | |
765 | const char *s, int *off, const u_char *const start, | |
766 | u_char *const buf, int *buflen) | |
767 | { | |
768 | const struct ng_parse_fixedstring_info *const fi = type->info; | |
769 | char *sval; | |
770 | int len; | |
771 | ||
772 | if ((sval = ng_get_string_token(s, off, &len)) == NULL) | |
773 | return (EINVAL); | |
774 | if (strlen(sval) + 1 > fi->bufSize) | |
775 | return (E2BIG); | |
776 | *off += len; | |
777 | len = strlen(sval) + 1; | |
778 | bcopy(sval, buf, len); | |
884717e1 | 779 | kfree(sval, M_NETGRAPH); |
984263bc MD |
780 | bzero(buf + len, fi->bufSize - len); |
781 | *buflen = fi->bufSize; | |
782 | return (0); | |
783 | } | |
784 | ||
785 | static int | |
786 | ng_fixedstring_unparse(const struct ng_parse_type *type, | |
787 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
788 | { | |
789 | const struct ng_parse_fixedstring_info *const fi = type->info; | |
790 | int error, temp = *off; | |
791 | ||
792 | if ((error = ng_string_unparse(type, data, &temp, cbuf, cbuflen)) != 0) | |
793 | return (error); | |
794 | *off += fi->bufSize; | |
795 | return (0); | |
796 | } | |
797 | ||
798 | static int | |
799 | ng_fixedstring_getDefault(const struct ng_parse_type *type, | |
800 | const u_char *const start, u_char *buf, int *buflen) | |
801 | { | |
802 | const struct ng_parse_fixedstring_info *const fi = type->info; | |
803 | ||
804 | if (*buflen < fi->bufSize) | |
805 | return (ERANGE); | |
806 | bzero(buf, fi->bufSize); | |
807 | *buflen = fi->bufSize; | |
808 | return (0); | |
809 | } | |
810 | ||
811 | const struct ng_parse_type ng_parse_fixedstring_type = { | |
812 | NULL, | |
813 | NULL, | |
814 | NULL, | |
815 | ng_fixedstring_parse, | |
816 | ng_fixedstring_unparse, | |
817 | ng_fixedstring_getDefault, | |
818 | NULL | |
819 | }; | |
820 | ||
821 | const struct ng_parse_fixedstring_info ng_parse_nodebuf_info = { | |
f15ba1a8 | 822 | NG_NODESIZ |
984263bc MD |
823 | }; |
824 | const struct ng_parse_type ng_parse_nodebuf_type = { | |
825 | &ng_parse_fixedstring_type, | |
826 | &ng_parse_nodebuf_info | |
827 | }; | |
828 | ||
829 | const struct ng_parse_fixedstring_info ng_parse_hookbuf_info = { | |
f15ba1a8 | 830 | NG_HOOKSIZ |
984263bc MD |
831 | }; |
832 | const struct ng_parse_type ng_parse_hookbuf_type = { | |
833 | &ng_parse_fixedstring_type, | |
834 | &ng_parse_hookbuf_info | |
835 | }; | |
836 | ||
837 | const struct ng_parse_fixedstring_info ng_parse_pathbuf_info = { | |
f15ba1a8 | 838 | NG_PATHSIZ |
984263bc MD |
839 | }; |
840 | const struct ng_parse_type ng_parse_pathbuf_type = { | |
841 | &ng_parse_fixedstring_type, | |
842 | &ng_parse_pathbuf_info | |
843 | }; | |
844 | ||
845 | const struct ng_parse_fixedstring_info ng_parse_typebuf_info = { | |
f15ba1a8 | 846 | NG_TYPESIZ |
984263bc MD |
847 | }; |
848 | const struct ng_parse_type ng_parse_typebuf_type = { | |
849 | &ng_parse_fixedstring_type, | |
850 | &ng_parse_typebuf_info | |
851 | }; | |
852 | ||
853 | const struct ng_parse_fixedstring_info ng_parse_cmdbuf_info = { | |
f15ba1a8 | 854 | NG_CMDSTRSIZ |
984263bc MD |
855 | }; |
856 | const struct ng_parse_type ng_parse_cmdbuf_type = { | |
857 | &ng_parse_fixedstring_type, | |
858 | &ng_parse_cmdbuf_info | |
859 | }; | |
860 | ||
861 | /************************************************************************ | |
862 | IP ADDRESS TYPE | |
863 | ************************************************************************/ | |
864 | ||
865 | static int | |
866 | ng_ipaddr_parse(const struct ng_parse_type *type, | |
867 | const char *s, int *off, const u_char *const start, | |
868 | u_char *const buf, int *buflen) | |
869 | { | |
870 | int i, error; | |
871 | ||
872 | for (i = 0; i < 4; i++) { | |
873 | if ((error = ng_int8_parse(&ng_parse_int8_type, | |
874 | s, off, start, buf + i, buflen)) != 0) | |
875 | return (error); | |
876 | if (i < 3 && s[*off] != '.') | |
877 | return (EINVAL); | |
878 | (*off)++; | |
879 | } | |
880 | *buflen = 4; | |
881 | return (0); | |
882 | } | |
883 | ||
884 | static int | |
885 | ng_ipaddr_unparse(const struct ng_parse_type *type, | |
886 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
887 | { | |
888 | struct in_addr ip; | |
889 | ||
890 | bcopy(data + *off, &ip, sizeof(ip)); | |
891 | NG_PARSE_APPEND("%d.%d.%d.%d", ((u_char *)&ip)[0], | |
892 | ((u_char *)&ip)[1], ((u_char *)&ip)[2], ((u_char *)&ip)[3]); | |
893 | *off += sizeof(ip); | |
894 | return (0); | |
895 | } | |
896 | ||
897 | static int | |
898 | ng_ipaddr_getDefault(const struct ng_parse_type *type, | |
899 | const u_char *const start, u_char *buf, int *buflen) | |
900 | { | |
901 | struct in_addr ip = { 0 }; | |
902 | ||
903 | if (*buflen < sizeof(ip)) | |
904 | return (ERANGE); | |
905 | bcopy(&ip, buf, sizeof(ip)); | |
906 | *buflen = sizeof(ip); | |
907 | return (0); | |
908 | } | |
909 | ||
910 | const struct ng_parse_type ng_parse_ipaddr_type = { | |
911 | NULL, | |
912 | NULL, | |
913 | NULL, | |
914 | ng_ipaddr_parse, | |
915 | ng_ipaddr_unparse, | |
916 | ng_ipaddr_getDefault, | |
917 | ng_int32_getAlign | |
918 | }; | |
919 | ||
920 | /************************************************************************ | |
921 | BYTE ARRAY TYPE | |
922 | ************************************************************************/ | |
923 | ||
924 | /* Get the length of a byte array */ | |
925 | static int | |
926 | ng_parse_bytearray_subtype_getLength(const struct ng_parse_type *type, | |
927 | const u_char *start, const u_char *buf) | |
928 | { | |
929 | ng_parse_array_getLength_t *const getLength = type->private; | |
930 | ||
931 | return (*getLength)(type, start, buf); | |
932 | } | |
933 | ||
934 | /* Byte array element type is hex int8 */ | |
935 | static const struct ng_parse_array_info ng_parse_bytearray_subtype_info = { | |
936 | &ng_parse_hint8_type, | |
937 | &ng_parse_bytearray_subtype_getLength, | |
938 | NULL | |
939 | }; | |
940 | static const struct ng_parse_type ng_parse_bytearray_subtype = { | |
941 | &ng_parse_array_type, | |
942 | &ng_parse_bytearray_subtype_info | |
943 | }; | |
944 | ||
945 | static int | |
946 | ng_bytearray_parse(const struct ng_parse_type *type, | |
947 | const char *s, int *off, const u_char *const start, | |
948 | u_char *const buf, int *buflen) | |
949 | { | |
950 | char *str; | |
951 | int toklen; | |
952 | ||
953 | /* We accept either an array of bytes or a string constant */ | |
954 | if ((str = ng_get_string_token(s, off, &toklen)) != NULL) { | |
955 | ng_parse_array_getLength_t *const getLength = type->info; | |
956 | int arraylen, slen; | |
957 | ||
958 | arraylen = (*getLength)(type, start, buf); | |
959 | if (arraylen > *buflen) { | |
884717e1 | 960 | kfree(str, M_NETGRAPH); |
984263bc MD |
961 | return (ERANGE); |
962 | } | |
963 | slen = strlen(str) + 1; | |
964 | if (slen > arraylen) { | |
884717e1 | 965 | kfree(str, M_NETGRAPH); |
984263bc MD |
966 | return (E2BIG); |
967 | } | |
968 | bcopy(str, buf, slen); | |
969 | bzero(buf + slen, arraylen - slen); | |
884717e1 | 970 | kfree(str, M_NETGRAPH); |
984263bc MD |
971 | *off += toklen; |
972 | *buflen = arraylen; | |
973 | return (0); | |
974 | } else { | |
975 | struct ng_parse_type subtype; | |
976 | ||
977 | subtype = ng_parse_bytearray_subtype; | |
cebef8bb | 978 | *(const void **)(void *)&subtype.private = type->info; |
984263bc MD |
979 | return ng_array_parse(&subtype, s, off, start, buf, buflen); |
980 | } | |
981 | } | |
982 | ||
983 | static int | |
984 | ng_bytearray_unparse(const struct ng_parse_type *type, | |
985 | const u_char *data, int *off, char *cbuf, int cbuflen) | |
986 | { | |
987 | struct ng_parse_type subtype; | |
988 | ||
989 | subtype = ng_parse_bytearray_subtype; | |
cebef8bb | 990 | *(const void **)(void *)&subtype.private = type->info; |
984263bc MD |
991 | return ng_array_unparse(&subtype, data, off, cbuf, cbuflen); |
992 | } | |
993 | ||
994 | static int | |
995 | ng_bytearray_getDefault(const struct ng_parse_type *type, | |
996 | const u_char *const start, u_char *buf, int *buflen) | |
997 | { | |
998 | struct ng_parse_type subtype; | |
999 | ||
1000 | subtype = ng_parse_bytearray_subtype; | |
cebef8bb | 1001 | *(const void **)(void *)&subtype.private = type->info; |
984263bc MD |
1002 | return ng_array_getDefault(&subtype, start, buf, buflen); |
1003 | } | |
1004 | ||
1005 | const struct ng_parse_type ng_parse_bytearray_type = { | |
1006 | NULL, | |
1007 | NULL, | |
1008 | NULL, | |
1009 | ng_bytearray_parse, | |
1010 | ng_bytearray_unparse, | |
1011 | ng_bytearray_getDefault, | |
1012 | NULL | |
1013 | }; | |
1014 | ||
1015 | /************************************************************************ | |
1016 | STRUCT NG_MESG TYPE | |
1017 | ************************************************************************/ | |
1018 | ||
1019 | /* Get msg->header.arglen when "buf" is pointing to msg->data */ | |
1020 | static int | |
1021 | ng_parse_ng_mesg_getLength(const struct ng_parse_type *type, | |
1022 | const u_char *start, const u_char *buf) | |
1023 | { | |
1024 | const struct ng_mesg *msg; | |
1025 | ||
1026 | msg = (const struct ng_mesg *)(buf - sizeof(*msg)); | |
1027 | return msg->header.arglen; | |
1028 | } | |
1029 | ||
1030 | /* Type for the variable length data portion of a struct ng_mesg */ | |
1031 | static const struct ng_parse_type ng_msg_data_type = { | |
1032 | &ng_parse_bytearray_type, | |
1033 | &ng_parse_ng_mesg_getLength | |
1034 | }; | |
1035 | ||
1036 | /* Type for the entire struct ng_mesg header with data section */ | |
1037 | static const struct ng_parse_struct_field ng_parse_ng_mesg_type_fields[] | |
1038 | = NG_GENERIC_NG_MESG_INFO(&ng_msg_data_type); | |
1039 | const struct ng_parse_type ng_parse_ng_mesg_type = { | |
1040 | &ng_parse_struct_type, | |
1041 | &ng_parse_ng_mesg_type_fields, | |
1042 | }; | |
1043 | ||
1044 | /************************************************************************ | |
1045 | COMPOSITE HELPER ROUTINES | |
1046 | ************************************************************************/ | |
1047 | ||
1048 | /* | |
1049 | * Convert a structure or array from ASCII to binary | |
1050 | */ | |
1051 | static int | |
1052 | ng_parse_composite(const struct ng_parse_type *type, const char *s, | |
1053 | int *off, const u_char *const start, u_char *const buf, int *buflen, | |
1054 | const enum comptype ctype) | |
1055 | { | |
1056 | const int num = ng_get_composite_len(type, start, buf, ctype); | |
1057 | int nextIndex = 0; /* next implicit array index */ | |
1058 | u_int index; /* field or element index */ | |
1059 | int *foff; /* field value offsets in string */ | |
1060 | int align, len, blen, error = 0; | |
1061 | ||
1062 | /* Initialize */ | |
884717e1 | 1063 | foff = kmalloc(num * sizeof(*foff), M_NETGRAPH, M_NOWAIT | M_ZERO); |
984263bc MD |
1064 | if (foff == NULL) { |
1065 | error = ENOMEM; | |
1066 | goto done; | |
1067 | } | |
984263bc MD |
1068 | |
1069 | /* Get opening brace/bracket */ | |
1070 | if (ng_parse_get_token(s, off, &len) | |
1071 | != (ctype == CT_STRUCT ? T_LBRACE : T_LBRACKET)) { | |
1072 | error = EINVAL; | |
1073 | goto done; | |
1074 | } | |
1075 | *off += len; | |
1076 | ||
1077 | /* Get individual element value positions in the string */ | |
1078 | for (;;) { | |
1079 | enum ng_parse_token tok; | |
1080 | ||
1081 | /* Check for closing brace/bracket */ | |
1082 | tok = ng_parse_get_token(s, off, &len); | |
1083 | if (tok == (ctype == CT_STRUCT ? T_RBRACE : T_RBRACKET)) { | |
1084 | *off += len; | |
1085 | break; | |
1086 | } | |
1087 | ||
1088 | /* For arrays, the 'name' (ie, index) is optional, so | |
1089 | distinguish name from values by seeing if the next | |
1090 | token is an equals sign */ | |
1091 | if (ctype != CT_STRUCT) { | |
1092 | int len2, off2; | |
1093 | char *eptr; | |
1094 | ||
1095 | /* If an opening brace/bracket, index is implied */ | |
1096 | if (tok == T_LBRACE || tok == T_LBRACKET) { | |
1097 | index = nextIndex++; | |
1098 | goto gotIndex; | |
1099 | } | |
1100 | ||
1101 | /* Might be an index, might be a value, either way... */ | |
1102 | if (tok != T_WORD) { | |
1103 | error = EINVAL; | |
1104 | goto done; | |
1105 | } | |
1106 | ||
1107 | /* If no equals sign follows, index is implied */ | |
1108 | off2 = *off + len; | |
1109 | if (ng_parse_get_token(s, &off2, &len2) != T_EQUALS) { | |
1110 | index = nextIndex++; | |
1111 | goto gotIndex; | |
1112 | } | |
1113 | ||
1114 | /* Index was specified explicitly; parse it */ | |
1115 | index = (u_int)strtoul(s + *off, &eptr, 0); | |
1116 | if (index < 0 || eptr - (s + *off) != len) { | |
1117 | error = EINVAL; | |
1118 | goto done; | |
1119 | } | |
1120 | nextIndex = index + 1; | |
1121 | *off += len + len2; | |
1122 | gotIndex: | |
6b08710e | 1123 | ; /* avoid gcc3.x warning */ |
984263bc MD |
1124 | } else { /* a structure field */ |
1125 | const struct ng_parse_struct_field *const | |
1126 | fields = type->info; | |
1127 | ||
1128 | /* Find the field by name (required) in field list */ | |
1129 | if (tok != T_WORD) { | |
1130 | error = EINVAL; | |
1131 | goto done; | |
1132 | } | |
1133 | for (index = 0; index < num; index++) { | |
1134 | const struct ng_parse_struct_field *const | |
1135 | field = &fields[index]; | |
1136 | ||
1137 | if (strncmp(&s[*off], field->name, len) == 0 | |
1138 | && field->name[len] == '\0') | |
1139 | break; | |
1140 | } | |
1141 | if (index == num) { | |
1142 | error = ENOENT; | |
1143 | goto done; | |
1144 | } | |
1145 | *off += len; | |
1146 | ||
1147 | /* Get equals sign */ | |
1148 | if (ng_parse_get_token(s, off, &len) != T_EQUALS) { | |
1149 | error = EINVAL; | |
1150 | goto done; | |
1151 | } | |
1152 | *off += len; | |
1153 | } | |
1154 | ||
1155 | /* Check array index */ | |
1156 | if (index >= num) { | |
1157 | error = E2BIG; | |
1158 | goto done; | |
1159 | } | |
1160 | ||
1161 | /* Save value's position and skip over it for now */ | |
1162 | if (foff[index] != 0) { | |
1163 | error = EALREADY; /* duplicate */ | |
1164 | goto done; | |
1165 | } | |
1166 | while (isspace(s[*off])) | |
1167 | (*off)++; | |
1168 | foff[index] = *off; | |
1169 | if ((error = ng_parse_skip_value(s, *off, &len)) != 0) | |
1170 | goto done; | |
1171 | *off += len; | |
1172 | } | |
1173 | ||
1174 | /* Now build binary structure from supplied values and defaults */ | |
1175 | for (blen = index = 0; index < num; index++) { | |
1176 | const struct ng_parse_type *const | |
1177 | etype = ng_get_composite_etype(type, index, ctype); | |
1178 | int k, pad, vlen; | |
1179 | ||
1180 | /* Zero-pad any alignment bytes */ | |
1181 | pad = ng_parse_get_elem_pad(type, index, ctype, blen); | |
1182 | for (k = 0; k < pad; k++) { | |
1183 | if (blen >= *buflen) { | |
1184 | error = ERANGE; | |
1185 | goto done; | |
1186 | } | |
1187 | buf[blen++] = 0; | |
1188 | } | |
1189 | ||
1190 | /* Get value */ | |
1191 | vlen = *buflen - blen; | |
1192 | if (foff[index] == 0) { /* use default value */ | |
1193 | error = ng_get_composite_elem_default(type, index, | |
1194 | start, buf + blen, &vlen, ctype); | |
1195 | } else { /* parse given value */ | |
1196 | *off = foff[index]; | |
1197 | error = INVOKE(etype, parse)(etype, | |
1198 | s, off, start, buf + blen, &vlen); | |
1199 | } | |
1200 | if (error != 0) | |
1201 | goto done; | |
1202 | blen += vlen; | |
1203 | } | |
1204 | ||
1205 | /* Make total composite structure size a multiple of its alignment */ | |
1206 | if ((align = ALIGNMENT(type)) != 0) { | |
1207 | while (blen % align != 0) { | |
1208 | if (blen >= *buflen) { | |
1209 | error = ERANGE; | |
1210 | goto done; | |
1211 | } | |
1212 | buf[blen++] = 0; | |
1213 | } | |
1214 | } | |
1215 | ||
1216 | /* Done */ | |
1217 | *buflen = blen; | |
1218 | done: | |
1219 | if (foff != NULL) | |
884717e1 | 1220 | kfree(foff, M_NETGRAPH); |
984263bc MD |
1221 | return (error); |
1222 | } | |
1223 | ||
1224 | /* | |
1225 | * Convert an array or structure from binary to ASCII | |
1226 | */ | |
1227 | static int | |
1228 | ng_unparse_composite(const struct ng_parse_type *type, const u_char *data, | |
1229 | int *off, char *cbuf, int cbuflen, const enum comptype ctype) | |
1230 | { | |
1231 | const struct ng_mesg *const hdr | |
1232 | = (const struct ng_mesg *)(data - sizeof(*hdr)); | |
1233 | const int num = ng_get_composite_len(type, data, data + *off, ctype); | |
1234 | const int workSize = 20 * 1024; /* XXX hard coded constant */ | |
1235 | int nextIndex = 0, didOne = 0; | |
1236 | int error, index; | |
1237 | u_char *workBuf; | |
1238 | ||
1239 | /* Get workspace for checking default values */ | |
884717e1 | 1240 | workBuf = kmalloc(workSize, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1241 | if (workBuf == NULL) |
1242 | return (ENOMEM); | |
1243 | ||
1244 | /* Opening brace/bracket */ | |
1245 | NG_PARSE_APPEND("%c", (ctype == CT_STRUCT) ? '{' : '['); | |
1246 | ||
1247 | /* Do each item */ | |
1248 | for (index = 0; index < num; index++) { | |
1249 | const struct ng_parse_type *const | |
1250 | etype = ng_get_composite_etype(type, index, ctype); | |
1251 | ||
1252 | /* Skip any alignment pad bytes */ | |
1253 | *off += ng_parse_get_elem_pad(type, index, ctype, *off); | |
1254 | ||
1255 | /* | |
1256 | * See if element is equal to its default value; skip if so. | |
1257 | * Copy struct ng_mesg header for types that peek into it. | |
1258 | */ | |
1259 | if (sizeof(*hdr) + *off < workSize) { | |
1260 | int tempsize = workSize - sizeof(*hdr) - *off; | |
1261 | ||
1262 | bcopy(hdr, workBuf, sizeof(*hdr) + *off); | |
1263 | if (ng_get_composite_elem_default(type, index, workBuf | |
1264 | + sizeof(*hdr), workBuf + sizeof(*hdr) + *off, | |
1265 | &tempsize, ctype) == 0 | |
1266 | && bcmp(workBuf + sizeof(*hdr) + *off, | |
1267 | data + *off, tempsize) == 0) { | |
1268 | *off += tempsize; | |
1269 | continue; | |
1270 | } | |
1271 | } | |
1272 | ||
1273 | /* Print name= */ | |
1274 | NG_PARSE_APPEND(" "); | |
1275 | if (ctype != CT_STRUCT) { | |
1276 | if (index != nextIndex) { | |
1277 | nextIndex = index; | |
1278 | NG_PARSE_APPEND("%d=", index); | |
1279 | } | |
1280 | nextIndex++; | |
1281 | } else { | |
1282 | const struct ng_parse_struct_field *const | |
1283 | fields = type->info; | |
1284 | ||
1285 | NG_PARSE_APPEND("%s=", fields[index].name); | |
1286 | } | |
1287 | ||
1288 | /* Print value */ | |
1289 | if ((error = INVOKE(etype, unparse) | |
1290 | (etype, data, off, cbuf, cbuflen)) != 0) { | |
884717e1 | 1291 | kfree(workBuf, M_NETGRAPH); |
984263bc MD |
1292 | return (error); |
1293 | } | |
1294 | cbuflen -= strlen(cbuf); | |
1295 | cbuf += strlen(cbuf); | |
1296 | didOne = 1; | |
1297 | } | |
884717e1 | 1298 | kfree(workBuf, M_NETGRAPH); |
984263bc MD |
1299 | |
1300 | /* Closing brace/bracket */ | |
1301 | NG_PARSE_APPEND("%s%c", | |
1302 | didOne ? " " : "", (ctype == CT_STRUCT) ? '}' : ']'); | |
1303 | return (0); | |
1304 | } | |
1305 | ||
1306 | /* | |
1307 | * Generate the default value for an element of an array or structure | |
1308 | * Returns EOPNOTSUPP if default value is unspecified. | |
1309 | */ | |
1310 | static int | |
1311 | ng_get_composite_elem_default(const struct ng_parse_type *type, | |
1312 | int index, const u_char *const start, u_char *buf, int *buflen, | |
1313 | const enum comptype ctype) | |
1314 | { | |
1315 | const struct ng_parse_type *etype; | |
1316 | ng_getDefault_t *func; | |
1317 | ||
1318 | switch (ctype) { | |
1319 | case CT_STRUCT: | |
1320 | break; | |
1321 | case CT_ARRAY: | |
1322 | { | |
1323 | const struct ng_parse_array_info *const ai = type->info; | |
1324 | ||
1325 | if (ai->getDefault != NULL) { | |
1326 | return (*ai->getDefault)(type, | |
1327 | index, start, buf, buflen); | |
1328 | } | |
1329 | break; | |
1330 | } | |
1331 | case CT_FIXEDARRAY: | |
1332 | { | |
1333 | const struct ng_parse_fixedarray_info *const fi = type->info; | |
1334 | ||
1335 | if (*fi->getDefault != NULL) { | |
1336 | return (*fi->getDefault)(type, | |
1337 | index, start, buf, buflen); | |
1338 | } | |
1339 | break; | |
1340 | } | |
1341 | default: | |
9eb57b71 | 1342 | panic("%s", __func__); |
984263bc MD |
1343 | } |
1344 | ||
1345 | /* Default to element type default */ | |
1346 | etype = ng_get_composite_etype(type, index, ctype); | |
1347 | func = METHOD(etype, getDefault); | |
1348 | if (func == NULL) | |
1349 | return (EOPNOTSUPP); | |
1350 | return (*func)(etype, start, buf, buflen); | |
1351 | } | |
1352 | ||
1353 | /* | |
1354 | * Get the number of elements in a struct, variable or fixed array. | |
1355 | */ | |
1356 | static int | |
1357 | ng_get_composite_len(const struct ng_parse_type *type, | |
1358 | const u_char *const start, const u_char *buf, | |
1359 | const enum comptype ctype) | |
1360 | { | |
1361 | switch (ctype) { | |
1362 | case CT_STRUCT: | |
1363 | { | |
1364 | const struct ng_parse_struct_field *const fields = type->info; | |
1365 | int numFields = 0; | |
1366 | ||
1367 | for (numFields = 0; ; numFields++) { | |
1368 | const struct ng_parse_struct_field *const | |
1369 | fi = &fields[numFields]; | |
1370 | ||
1371 | if (fi->name == NULL) | |
1372 | break; | |
1373 | } | |
1374 | return (numFields); | |
1375 | } | |
1376 | case CT_ARRAY: | |
1377 | { | |
1378 | const struct ng_parse_array_info *const ai = type->info; | |
1379 | ||
1380 | return (*ai->getLength)(type, start, buf); | |
1381 | } | |
1382 | case CT_FIXEDARRAY: | |
1383 | { | |
1384 | const struct ng_parse_fixedarray_info *const fi = type->info; | |
1385 | ||
1386 | return fi->length; | |
1387 | } | |
1388 | default: | |
9eb57b71 | 1389 | panic("%s", __func__); |
984263bc MD |
1390 | } |
1391 | return (0); | |
1392 | } | |
1393 | ||
1394 | /* | |
1395 | * Return the type of the index'th element of a composite structure | |
1396 | */ | |
1397 | static const struct ng_parse_type * | |
1398 | ng_get_composite_etype(const struct ng_parse_type *type, | |
1399 | int index, const enum comptype ctype) | |
1400 | { | |
1401 | const struct ng_parse_type *etype = NULL; | |
1402 | ||
1403 | switch (ctype) { | |
1404 | case CT_STRUCT: | |
1405 | { | |
1406 | const struct ng_parse_struct_field *const fields = type->info; | |
1407 | ||
1408 | etype = fields[index].type; | |
1409 | break; | |
1410 | } | |
1411 | case CT_ARRAY: | |
1412 | { | |
1413 | const struct ng_parse_array_info *const ai = type->info; | |
1414 | ||
1415 | etype = ai->elementType; | |
1416 | break; | |
1417 | } | |
1418 | case CT_FIXEDARRAY: | |
1419 | { | |
1420 | const struct ng_parse_fixedarray_info *const fi = type->info; | |
1421 | ||
1422 | etype = fi->elementType; | |
1423 | break; | |
1424 | } | |
1425 | default: | |
9eb57b71 | 1426 | panic("%s", __func__); |
984263bc MD |
1427 | } |
1428 | return (etype); | |
1429 | } | |
1430 | ||
1431 | /* | |
1432 | * Get the number of bytes to skip to align for the next | |
1433 | * element in a composite structure. | |
1434 | */ | |
1435 | static int | |
1436 | ng_parse_get_elem_pad(const struct ng_parse_type *type, | |
1437 | int index, enum comptype ctype, int posn) | |
1438 | { | |
1439 | const struct ng_parse_type *const | |
1440 | etype = ng_get_composite_etype(type, index, ctype); | |
1441 | int align; | |
1442 | ||
1443 | /* Get element's alignment, and possibly override */ | |
1444 | align = ALIGNMENT(etype); | |
1445 | if (ctype == CT_STRUCT) { | |
1446 | const struct ng_parse_struct_field *const fields = type->info; | |
1447 | ||
1448 | if (fields[index].alignment != 0) | |
1449 | align = fields[index].alignment; | |
1450 | } | |
1451 | ||
1452 | /* Return number of bytes to skip to align */ | |
1453 | return (align ? (align - (posn % align)) % align : 0); | |
1454 | } | |
1455 | ||
1456 | /************************************************************************ | |
1457 | PARSING HELPER ROUTINES | |
1458 | ************************************************************************/ | |
1459 | ||
1460 | /* | |
1461 | * Skip over a value | |
1462 | */ | |
1463 | static int | |
1464 | ng_parse_skip_value(const char *s, int off0, int *lenp) | |
1465 | { | |
1466 | int len, nbracket, nbrace; | |
1467 | int off = off0; | |
1468 | ||
1469 | len = nbracket = nbrace = 0; | |
1470 | do { | |
1471 | switch (ng_parse_get_token(s, &off, &len)) { | |
1472 | case T_LBRACKET: | |
1473 | nbracket++; | |
1474 | break; | |
1475 | case T_LBRACE: | |
1476 | nbrace++; | |
1477 | break; | |
1478 | case T_RBRACKET: | |
1479 | if (nbracket-- == 0) | |
1480 | return (EINVAL); | |
1481 | break; | |
1482 | case T_RBRACE: | |
1483 | if (nbrace-- == 0) | |
1484 | return (EINVAL); | |
1485 | break; | |
1486 | case T_EOF: | |
1487 | return (EINVAL); | |
1488 | default: | |
1489 | break; | |
1490 | } | |
1491 | off += len; | |
1492 | } while (nbracket > 0 || nbrace > 0); | |
1493 | *lenp = off - off0; | |
1494 | return (0); | |
1495 | } | |
1496 | ||
1497 | /* | |
1498 | * Find the next token in the string, starting at offset *startp. | |
1499 | * Returns the token type, with *startp pointing to the first char | |
1500 | * and *lenp the length. | |
1501 | */ | |
1502 | enum ng_parse_token | |
1503 | ng_parse_get_token(const char *s, int *startp, int *lenp) | |
1504 | { | |
1505 | char *t; | |
1506 | int i; | |
1507 | ||
1508 | while (isspace(s[*startp])) | |
1509 | (*startp)++; | |
1510 | switch (s[*startp]) { | |
1511 | case '\0': | |
1512 | *lenp = 0; | |
1513 | return T_EOF; | |
1514 | case '{': | |
1515 | *lenp = 1; | |
1516 | return T_LBRACE; | |
1517 | case '}': | |
1518 | *lenp = 1; | |
1519 | return T_RBRACE; | |
1520 | case '[': | |
1521 | *lenp = 1; | |
1522 | return T_LBRACKET; | |
1523 | case ']': | |
1524 | *lenp = 1; | |
1525 | return T_RBRACKET; | |
1526 | case '=': | |
1527 | *lenp = 1; | |
1528 | return T_EQUALS; | |
1529 | case '"': | |
1530 | if ((t = ng_get_string_token(s, startp, lenp)) == NULL) | |
1531 | return T_ERROR; | |
884717e1 | 1532 | kfree(t, M_NETGRAPH); |
984263bc MD |
1533 | return T_STRING; |
1534 | default: | |
1535 | for (i = *startp + 1; s[i] != '\0' && !isspace(s[i]) | |
1536 | && s[i] != '{' && s[i] != '}' && s[i] != '[' | |
1537 | && s[i] != ']' && s[i] != '=' && s[i] != '"'; i++) | |
1538 | ; | |
1539 | *lenp = i - *startp; | |
1540 | return T_WORD; | |
1541 | } | |
1542 | } | |
1543 | ||
1544 | /* | |
1545 | * Get a string token, which must be enclosed in double quotes. | |
1546 | * The normal C backslash escapes are recognized. | |
1547 | */ | |
1548 | char * | |
1549 | ng_get_string_token(const char *s, int *startp, int *lenp) | |
1550 | { | |
1551 | char *cbuf, *p; | |
1552 | int start, off; | |
1553 | ||
1554 | while (isspace(s[*startp])) | |
1555 | (*startp)++; | |
1556 | start = *startp; | |
1557 | if (s[*startp] != '"') | |
1558 | return (NULL); | |
884717e1 | 1559 | cbuf = kmalloc(strlen(s + start), M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1560 | if (cbuf == NULL) |
1561 | return (NULL); | |
1562 | strcpy(cbuf, s + start + 1); | |
1563 | for (off = 1, p = cbuf; *p != '\0'; off++, p++) { | |
1564 | if (*p == '"') { | |
1565 | *p = '\0'; | |
1566 | *lenp = off + 1; | |
1567 | return (cbuf); | |
1568 | } else if (p[0] == '\\' && p[1] != '\0') { | |
1569 | int x, k; | |
1570 | char *v; | |
1571 | ||
1572 | strcpy(p, p + 1); | |
1573 | v = p; | |
1574 | switch (*p) { | |
1575 | case 't': | |
1576 | *v = '\t'; | |
1577 | off++; | |
1578 | continue; | |
1579 | case 'n': | |
1580 | *v = '\n'; | |
1581 | off++; | |
1582 | continue; | |
1583 | case 'r': | |
1584 | *v = '\r'; | |
1585 | off++; | |
1586 | continue; | |
1587 | case 'v': | |
1588 | *v = '\v'; | |
1589 | off++; | |
1590 | continue; | |
1591 | case 'f': | |
1592 | *v = '\f'; | |
1593 | off++; | |
1594 | continue; | |
1595 | case '"': | |
1596 | *v = '"'; | |
1597 | off++; | |
1598 | continue; | |
1599 | case '0': case '1': case '2': case '3': | |
1600 | case '4': case '5': case '6': case '7': | |
1601 | for (x = k = 0; | |
1602 | k < 3 && *v >= '0' && *v <= '7'; v++) { | |
1603 | x = (x << 3) + (*v - '0'); | |
1604 | off++; | |
1605 | } | |
1606 | *--v = (char)x; | |
1607 | break; | |
1608 | case 'x': | |
1609 | for (v++, x = k = 0; | |
1610 | k < 2 && isxdigit(*v); v++) { | |
1611 | x = (x << 4) + (isdigit(*v) ? | |
1612 | (*v - '0') : | |
1613 | (tolower(*v) - 'a' + 10)); | |
1614 | off++; | |
1615 | } | |
1616 | *--v = (char)x; | |
1617 | break; | |
1618 | default: | |
1619 | continue; | |
1620 | } | |
1621 | strcpy(p, v); | |
1622 | } | |
1623 | } | |
1624 | return (NULL); /* no closing quote */ | |
1625 | } | |
1626 | ||
1627 | /* | |
1628 | * Encode a string so it can be safely put in double quotes. | |
1629 | * Caller must free the result. | |
1630 | */ | |
1631 | char * | |
1632 | ng_encode_string(const char *raw) | |
1633 | { | |
1634 | char *cbuf; | |
1635 | int off = 0; | |
1636 | ||
884717e1 | 1637 | cbuf = kmalloc(strlen(raw) * 4 + 3, M_NETGRAPH, M_NOWAIT); |
984263bc MD |
1638 | if (cbuf == NULL) |
1639 | return (NULL); | |
1640 | cbuf[off++] = '"'; | |
1641 | for ( ; *raw != '\0'; raw++) { | |
1642 | switch (*raw) { | |
1643 | case '\t': | |
1644 | cbuf[off++] = '\\'; | |
1645 | cbuf[off++] = 't'; | |
1646 | break; | |
1647 | case '\f': | |
1648 | cbuf[off++] = '\\'; | |
1649 | cbuf[off++] = 'f'; | |
1650 | break; | |
1651 | case '\n': | |
1652 | cbuf[off++] = '\\'; | |
1653 | cbuf[off++] = 'n'; | |
1654 | break; | |
1655 | case '\r': | |
1656 | cbuf[off++] = '\\'; | |
1657 | cbuf[off++] = 'r'; | |
1658 | break; | |
1659 | case '\v': | |
1660 | cbuf[off++] = '\\'; | |
1661 | cbuf[off++] = 'v'; | |
1662 | break; | |
1663 | case '"': | |
1664 | case '\\': | |
1665 | cbuf[off++] = '\\'; | |
1666 | cbuf[off++] = *raw; | |
1667 | break; | |
1668 | default: | |
1669 | if (*raw < 0x20 || *raw > 0x7e) { | |
f8c7a42d | 1670 | off += ksprintf(cbuf + off, |
984263bc MD |
1671 | "\\x%02x", (u_char)*raw); |
1672 | break; | |
1673 | } | |
1674 | cbuf[off++] = *raw; | |
1675 | break; | |
1676 | } | |
1677 | } | |
1678 | cbuf[off++] = '"'; | |
1679 | cbuf[off] = '\0'; | |
1680 | return (cbuf); | |
1681 | } | |
1682 | ||
1683 | /************************************************************************ | |
1684 | VIRTUAL METHOD LOOKUP | |
1685 | ************************************************************************/ | |
1686 | ||
1687 | static ng_parse_t * | |
1688 | ng_get_parse_method(const struct ng_parse_type *t) | |
1689 | { | |
1690 | while (t != NULL && t->parse == NULL) | |
1691 | t = t->supertype; | |
1692 | return (t ? t->parse : NULL); | |
1693 | } | |
1694 | ||
1695 | static ng_unparse_t * | |
1696 | ng_get_unparse_method(const struct ng_parse_type *t) | |
1697 | { | |
1698 | while (t != NULL && t->unparse == NULL) | |
1699 | t = t->supertype; | |
1700 | return (t ? t->unparse : NULL); | |
1701 | } | |
1702 | ||
1703 | static ng_getDefault_t * | |
1704 | ng_get_getDefault_method(const struct ng_parse_type *t) | |
1705 | { | |
1706 | while (t != NULL && t->getDefault == NULL) | |
1707 | t = t->supertype; | |
1708 | return (t ? t->getDefault : NULL); | |
1709 | } | |
1710 | ||
1711 | static ng_getAlign_t * | |
1712 | ng_get_getAlign_method(const struct ng_parse_type *t) | |
1713 | { | |
1714 | while (t != NULL && t->getAlign == NULL) | |
1715 | t = t->supertype; | |
1716 | return (t ? t->getAlign : NULL); | |
1717 | } | |
1718 |