1 /* $NetBSD: reduce.c,v 1.3 2022/04/03 01:10:59 christos Exp $ */
2
3 /*
4 * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Internet Systems Consortium, Inc.
19 * PO Box 360
20 * Newmarket, NH 03857 USA
21 * <info@isc.org>
22 * https://www.isc.org/
23 *
24 */
25
26 #include <sys/cdefs.h>
27 __RCSID("$NetBSD: reduce.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
28
29 #include "keama.h"
30
31 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <arpa/inet.h>
34 #include <ctype.h>
35 #include <netdb.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 static struct element *reduce_equal_expression(struct element *left,
42 struct element *right);
43 static void debug(const char* fmt, ...);
44
45 /*
46 * boolean_expression :== CHECK STRING |
47 * NOT boolean-expression |
48 * data-expression EQUAL data-expression |
49 * data-expression BANG EQUAL data-expression |
50 * data-expression REGEX_MATCH data-expression |
51 * boolean-expression AND boolean-expression |
52 * boolean-expression OR boolean-expression
53 * EXISTS OPTION-NAME
54 */
55
56 struct element *
reduce_boolean_expression(struct element * expr)57 reduce_boolean_expression(struct element *expr)
58 {
59 /* trivial case: already done */
60 if (expr->type == ELEMENT_BOOLEAN)
61 return expr;
62
63 /*
64 * From is_boolean_expression
65 */
66
67 if (expr->type != ELEMENT_MAP)
68 return NULL;
69
70 /* check */
71 if (mapContains(expr, "check"))
72 /*
73 * syntax := { "check": <collection_name> }
74 * semantic: check_collection
75 * on server try to match classes of the collection
76 */
77 return NULL;
78
79
80 /* exists */
81 if (mapContains(expr, "exists")) {
82 /*
83 * syntax := { "exists":
84 * { "universe": <option_space_old>,
85 * "name": <option_name> }
86 * }
87 * semantic: check universe/code from incoming packet
88 */
89 struct element *arg;
90 struct element *universe;
91 struct element *name;
92 struct option *option;
93 char result[80];
94
95 arg = mapGet(expr, "exists");
96 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
97 debug("can't get exists argument");
98 return NULL;
99 }
100 universe = mapGet(arg, "universe");
101 if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
102 debug("can't get exists option universe");
103 return NULL;
104 }
105 name = mapGet(arg, "name");
106 if ((name == NULL) || (name->type != ELEMENT_STRING)) {
107 debug("can't get exists option name");
108 return NULL;
109 }
110 option = option_lookup_name(stringValue(universe)->content,
111 stringValue(name)->content);
112 if ((option == NULL) || (option->code == 0))
113 return NULL;
114 if (((local_family == AF_INET) &&
115 (strcmp(option->space->name, "dhcp4") != 0)) ||
116 ((local_family == AF_INET6) &&
117 (strcmp(option->space->name, "dhcp6") != 0)))
118 return NULL;
119 snprintf(result, sizeof(result),
120 "option[%u].exists", option->code);
121 return createString(makeString(-1, result));
122 }
123
124 /* variable-exists */
125 if (mapContains(expr, "variable-exists"))
126 /*
127 * syntax := { "variable-exists": <variable_name> }
128 * semantics: find_binding(scope, name)
129 */
130 return NULL;
131
132 /* equal */
133 if (mapContains(expr, "equal")) {
134 /*
135 * syntax := { "equal":
136 * { "left": <expression>,
137 * "right": <expression> }
138 * }
139 * semantics: evaluate branches and return true
140 * if same type and same value
141 */
142 struct element *arg;
143 struct element *left;
144 struct element *right;
145
146 arg = mapGet(expr, "equal");
147 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
148 debug("can't get equal argument");
149 return NULL;
150 }
151 left = mapGet(arg, "left");
152 if (left == NULL) {
153 debug("can't get equal left branch");
154 return NULL;
155 }
156 right = mapGet(arg, "right");
157 if (right == NULL) {
158 debug("can't get equal right branch");
159 return NULL;
160 }
161 return reduce_equal_expression(left, right);
162 }
163
164 /* not-equal */
165 if (mapContains(expr, "not-equal")) {
166 /*
167 * syntax := { "not-equal":
168 * { "left": <expression>,
169 * "right": <expression> }
170 * }
171 * semantics: evaluate branches and return true
172 * if different type or different value
173 */
174 struct element *arg;
175 struct element *left;
176 struct element *right;
177 struct element *equal;
178 struct string *result;
179
180 arg = mapGet(expr, "not-equal");
181 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
182 debug("can't get not-equal argument");
183 return NULL;
184 }
185 left = mapGet(arg, "left");
186 if (left == NULL) {
187 debug("can't get not-equal left branch");
188 return NULL;
189 }
190 right = mapGet(arg, "right");
191 if (right == NULL) {
192 debug("can't get not-equal right branch");
193 return NULL;
194 }
195 equal = reduce_equal_expression(left, right);
196 if ((equal == NULL) || (equal->type != ELEMENT_STRING))
197 return NULL;
198 result = makeString(-1, "not (");
199 concatString(result, stringValue(equal));
200 appendString(result, ")");
201 return createString(result);
202 }
203
204 /* regex-match */
205 if (mapContains(expr, "regex-match"))
206 /*
207 * syntax := { "regex-match":
208 * { "left": <data_expression>,
209 * "right": <data_expression> }
210 * }
211 * semantics: evaluate branches, compile right as a
212 * regex and apply it to left
213 */
214 return NULL;
215
216 /* iregex-match */
217 if (mapContains(expr, "iregex-match"))
218 /*
219 * syntax := { "regex-match":
220 * { "left": <data_expression>,
221 * "right": <data_expression> }
222 * }
223 * semantics: evaluate branches, compile right as a
224 * case insensistive regex and apply it to left
225 */
226 return NULL;
227
228 /* and */
229 if (mapContains(expr, "and")) {
230 /*
231 * syntax := { "and":
232 * { "left": <boolean_expression>,
233 * "right": <boolean_expression> }
234 * }
235 * semantics: evaluate branches, return true
236 * if both are true
237 */
238 struct element *arg;
239 struct element *left;
240 struct element *right;
241 struct string *result;
242
243 arg = mapGet(expr, "and");
244 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
245 debug("can't get and argument");
246 return NULL;
247 }
248 left = mapGet(arg, "left");
249 if (left == NULL) {
250 debug("can't get and left branch");
251 return NULL;
252 }
253 right = mapGet(arg, "right");
254 if (right == NULL) {
255 debug("can't get and right branch");
256 return NULL;
257 }
258 left = reduce_boolean_expression(left);
259 if ((left == NULL) || (left->type != ELEMENT_STRING))
260 return NULL;
261 right = reduce_boolean_expression(right);
262 if ((right == NULL) || (right->type != ELEMENT_STRING))
263 return NULL;
264 result = makeString(-1, "(");
265 concatString(result, stringValue(left));
266 appendString(result, ") and (");
267 concatString(result, stringValue(right));
268 appendString(result, ")");
269 return createString(result);
270 }
271
272 /* or */
273 if (mapContains(expr, "or")) {
274 /*
275 * syntax := { "or":
276 * { "left": <boolean_expression>,
277 * "right": <boolean_expression> }
278 * }
279 * semantics: evaluate branches, return true
280 * if any is true
281 */
282 struct element *arg;
283 struct element *left;
284 struct element *right;
285 struct string *result;
286
287 arg = mapGet(expr, "or");
288 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
289 debug("can't get or argument");
290 return NULL;
291 }
292 left = mapGet(arg, "left");
293 if (left == NULL) {
294 debug("can't get or left branch");
295 return NULL;
296 }
297 right = mapGet(arg, "right");
298 if (right == NULL) {
299 debug("can't get or right branch");
300 return NULL;
301 }
302 left = reduce_boolean_expression(left);
303 if ((left == NULL) || (left->type != ELEMENT_STRING))
304 return NULL;
305 right = reduce_boolean_expression(right);
306 if ((right == NULL) || (right->type != ELEMENT_STRING))
307 return NULL;
308 result = makeString(-1, "(");
309 concatString(result, stringValue(left));
310 appendString(result, ") or (");
311 concatString(result, stringValue(right));
312 appendString(result, ")");
313 return createString(result);
314 }
315
316 /* not */
317 if (mapContains(expr, "not")) {
318 /*
319 * syntax := { "not": <boolean_expression> }
320 * semantic: evaluate its branch and return its negation
321 */
322 struct element *arg;
323 struct string *result;
324
325 arg = mapGet(expr, "not");
326 if (arg == NULL) {
327 debug("can't get not argument");
328 return NULL;
329 }
330 arg = reduce_boolean_expression(arg);
331 if ((arg == NULL) || (arg->type != ELEMENT_STRING))
332 return NULL;
333 result = makeString(-1, "not (");
334 concatString(result, stringValue(arg));
335 appendString(result, ")");
336 return createString(result);
337 }
338
339 /* known */
340 if (mapContains(expr, "known"))
341 /*
342 * syntax := { "known": null }
343 * semantics: client is known, i.e., has a matching
344 * host declaration (aka reservation in Kea)
345 */
346 return NULL;
347
348 /* static */
349 if (mapContains(expr, "static"))
350 /*
351 * syntax := { "static": null }
352 * semantics: lease is static (doesn't exist in Kea)
353 */
354 return NULL;
355
356 return NULL;
357 }
358
359 /*
360 * data_expression :== SUBSTRING LPAREN data-expression COMMA
361 * numeric-expression COMMA
362 * numeric-expression RPAREN |
363 * CONCAT LPAREN data-expression COMMA
364 * data-expression RPAREN
365 * SUFFIX LPAREN data_expression COMMA
366 * numeric-expression RPAREN |
367 * LCASE LPAREN data_expression RPAREN |
368 * UCASE LPAREN data_expression RPAREN |
369 * OPTION option_name |
370 * HARDWARE |
371 * PACKET LPAREN numeric-expression COMMA
372 * numeric-expression RPAREN |
373 * V6RELAY LPAREN numeric-expression COMMA
374 * data-expression RPAREN |
375 * STRING |
376 * colon_separated_hex_list
377 */
378
379 struct element *
reduce_data_expression(struct element * expr)380 reduce_data_expression(struct element *expr)
381 {
382 /* trivial case: already done */
383 if (expr->type == ELEMENT_STRING)
384 return expr;
385
386 /*
387 * From is_data_expression
388 */
389
390 if (expr->type != ELEMENT_MAP)
391 return NULL;
392
393 /* substring */
394 if (mapContains(expr, "substring")) {
395 /*
396 * syntax := { "substring":
397 * { "expression": <data_expression>,
398 * "offset": <numeric_expression>,
399 * "length": <numeric_expression> }
400 * }
401 * semantic: evaluate arguments, if the string is
402 * shorter than offset return "" else return substring
403 */
404 struct element *arg;
405 struct element *string;
406 struct element *offset;
407 struct element *length;
408 struct string *result;
409 int64_t off;
410 int64_t len;
411 char buf[80];
412
413 arg = mapGet(expr, "substring");
414 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
415 debug("can't get substring argument");
416 return NULL;
417 }
418 string = mapGet(arg, "expression");
419 if (string == NULL) {
420 debug("can't get substring expression");
421 return NULL;
422 }
423 offset = mapGet(arg, "offset");
424 if (offset == NULL) {
425 debug("can't get substring offset");
426 return NULL;
427 }
428 length = mapGet(arg, "length");
429 if (length == NULL) {
430 debug("can't get substring length");
431 return NULL;
432 }
433 /* can't be a literal as it was evaluated before */
434 string = reduce_data_expression(string);
435 if ((string == NULL) || (string->type != ELEMENT_STRING))
436 return NULL;
437 offset = reduce_numeric_expression(offset);
438 if ((offset == NULL) || (offset->type != ELEMENT_INTEGER))
439 return NULL;
440 off = intValue(offset);
441 if (off < 0) {
442 debug("substring with a negative offset (%lld)",
443 (long long)off);
444 return NULL;
445 }
446 length = reduce_numeric_expression(length);
447 if ((length == NULL) || (length->type != ELEMENT_INTEGER))
448 return NULL;
449 len = intValue(length);
450 if (len < 0) {
451 debug("substring with a negative length (%lld)",
452 (long long)len);
453 return NULL;
454 }
455 result = makeString(-1, "substring(");
456 concatString(result, stringValue(string));
457 snprintf(buf, sizeof(buf),
458 ",%u,%u)", (unsigned)off, (unsigned)len);
459 appendString(result, buf);
460 return createString(result);
461 }
462
463 /* suffix */
464 if (mapContains(expr, "suffix")) {
465 /*
466 * syntax := { "suffix":
467 * { "expression": <data_expression>,
468 * "length": <numeric_expression> }
469 * }
470 * semantic: evaluate arguments, if the string is
471 * shorter than length return it else return suffix
472 */
473 struct element *arg;
474 struct element *string;
475 struct element *length;
476 struct string *result;
477 int64_t len;
478 char buf[80];
479
480 arg = mapGet(expr, "suffix");
481 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
482 debug("can't get suffix argument");
483 return NULL;
484 }
485 string = mapGet(arg, "expression");
486 if (string == NULL) {
487 debug("can't get suffix expression");
488 return NULL;
489 }
490 length = mapGet(arg, "length");
491 if (length == NULL) {
492 debug("can't get suffix length");
493 return NULL;
494 }
495 /* can't be a literal as it was evaluated before */
496 string = reduce_data_expression(string);
497 if ((string == NULL) || (string->type != ELEMENT_STRING))
498 return NULL;
499 length = reduce_numeric_expression(length);
500 if ((length == NULL) || (length->type != ELEMENT_INTEGER))
501 return NULL;
502 len = intValue(length);
503 if (len < 0) {
504 debug("suffix with a negative length (%lld)",
505 (long long)len);
506 return NULL;
507 }
508 result = makeString(-1, "substring(");
509 concatString(result, stringValue(string));
510 snprintf(buf, sizeof(buf), ",-%u,all)", (unsigned)len);
511 appendString(result, buf);
512 return createString(result);
513 }
514
515 /* lowercase */
516 if (mapContains(expr, "lowercase"))
517 /*
518 * syntax := { "lowercase": <data_expression> }
519 * semantic: evaluate its argument and apply tolower to
520 * its content
521 */
522 return NULL;
523
524 /* uppercase */
525 if (mapContains(expr, "uppercase"))
526 /*
527 * syntax := { "uppercase": <data_expression> }
528 * semantic: evaluate its argument and apply toupper to
529 * its content
530 */
531 return NULL;
532
533 /* option */
534 if (mapContains(expr, "option")) {
535 /*
536 * syntax := { "option":
537 * { "universe": <option_space_old>,
538 * "name": <option_name> }
539 * }
540 * semantic: get universe/code option from incoming packet
541 */
542 struct element *arg;
543 struct element *universe;
544 struct element *name;
545 struct option *option;
546 char result[80];
547
548 arg = mapGet(expr, "option");
549 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
550 debug("can't get option argument");
551 return NULL;
552 }
553 universe = mapGet(arg, "universe");
554 if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
555 debug("can't get option universe");
556 return NULL;
557 }
558 name = mapGet(arg, "name");
559 if ((name == NULL) || (name->type != ELEMENT_STRING)) {
560 debug("can't get option name");
561 return NULL;
562 }
563 option = option_lookup_name(stringValue(universe)->content,
564 stringValue(name)->content);
565 if ((option == NULL) || (option->code == 0))
566 return NULL;
567 if (((local_family == AF_INET) &&
568 (strcmp(option->space->name, "dhcp4") != 0)) ||
569 ((local_family == AF_INET6) &&
570 (strcmp(option->space->name, "dhcp6") != 0)))
571 return NULL;
572 snprintf(result, sizeof(result),
573 "option[%u].hex", option->code);
574 return createString(makeString(-1, result));
575 }
576
577 /* hardware */
578 if (mapContains(expr, "hardware")) {
579 /*
580 * syntax := { "hardware": null }
581 * semantic: get mac type and address from incoming packet
582 */
583 struct string *result;
584
585 if (local_family != AF_INET) {
586 debug("get hardware for DHCPv6");
587 return NULL;
588 }
589 result = makeString(-1,
590 "concat(substring(pkt4.htype,-1,all),pkt4.mac)");
591 return createString(result);
592 }
593
594 /* hw-type */
595 if (mapContains(expr, "hw-type")) {
596 /*
597 * ADDED
598 * syntax := { "hw-type": null }
599 * semantic: get mac type from incoming packet
600 */
601 struct string *result;
602
603 if (local_family != AF_INET) {
604 debug("get hw-type for DHCPv6");
605 return NULL;
606 }
607 result = makeString(-1, "substring(pkt4.htype,-1,all)");
608 return createString(result);
609 }
610
611 /* hw-address */
612 if (mapContains(expr, "hw-address")) {
613 /*
614 * ADDED
615 * syntax := { "hw-address": null }
616 * semantic: get mac address from incoming packet
617 */
618 struct string *result;
619
620 if (local_family != AF_INET) {
621 debug("get hw-address for DHCPv6");
622 return NULL;
623 }
624 result = makeString(-1, "pkt4.mac");
625 return createString(result);
626 }
627
628 /* const-data */
629 if (mapContains(expr, "const-data")) {
630 /*
631 * syntax := { "const-data": <string> }
632 * semantic: embedded string value
633 */
634 struct element *arg;
635
636 arg = mapGet(expr, "const-data");
637 if ((arg == NULL) || (arg->type != ELEMENT_STRING)) {
638 debug("can't get const-data argument");
639 return NULL;
640 }
641 return createString(stringValue(arg));
642 }
643
644 /* packet */
645 if (mapContains(expr, "packet"))
646 /*
647 * syntax := { "packet":
648 * { "offset": <numeric_expression>,
649 * "length": <numeric_expression> }
650 * }
651 * semantic: return the selected substring of the incoming
652 * packet content
653 */
654 return NULL;
655
656 /* concat */
657 if (mapContains(expr, "concat")) {
658 /*
659 * syntax := { "concat":
660 * { "left": <data_expression>,
661 * "right": <data_expression> }
662 * }
663 * semantic: evaluate arguments and return the concatenation
664 */
665 struct element *arg;
666 struct element *left;
667 struct element *right;
668 struct string *result;
669
670 arg = mapGet(expr, "concat");
671 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
672 debug("can't get concat argument");
673 return NULL;
674 }
675 left = mapGet(arg, "left");
676 if (left == NULL) {
677 debug("can't get concat left branch");
678 return NULL;
679 }
680 right = mapGet(arg, "right");
681 if (right == NULL) {
682 debug("can't get concat right branch");
683 return NULL;
684 }
685 /* left is a literal case */
686 if (left->type == ELEMENT_STRING) {
687 /* can't be a literal as it was evaluated before */
688 right = reduce_data_expression(right);
689 if ((right == NULL) || (right->type != ELEMENT_STRING))
690 return NULL;
691 result = makeString(-1, "concat(");
692 concatString(result, quote(stringValue(left)));
693 appendString(result, ", ");
694 concatString(result, stringValue(right));
695 appendString(result, ")");
696 return createString(result);
697 }
698 left = reduce_data_expression(left);
699 if ((left == NULL) || (left->type != ELEMENT_STRING))
700 return NULL;
701 /* right is a literal case */
702 if (right->type == ELEMENT_STRING) {
703 /* literal left was handled before */
704 result = makeString(-1, "concat(");
705 concatString(result, stringValue(left));
706 appendString(result, ", ");
707 concatString(result, quote(stringValue(right)));
708 appendString(result, ")");
709 return createString(result);
710 }
711 right = reduce_data_expression(right);
712 if ((right == NULL) || (right->type != ELEMENT_STRING))
713 return NULL;
714 result = makeString(-1, "concat(");
715 concatString(result, stringValue(left));
716 appendString(result, ", ");
717 concatString(result, stringValue(right));
718 appendString(result, ")");
719 return createString(result);
720 }
721
722 /* encapsulate */
723 if (mapContains(expr, "encapsulate"))
724 /*
725 * syntax := { "encapsulate": <encapsulated_space> }
726 * semantic: encapsulate options of the given space
727 */
728 return NULL;
729
730 /* encode-int8 */
731 if (mapContains(expr, "encode-int8"))
732 /*
733 * syntax := { "encode-int8": <numeric_expression> }
734 * semantic: return a string buffer with the evaluated
735 * number as content
736 */
737 return NULL;
738
739 /* encode-int16 */
740 if (mapContains(expr, "encode-int16"))
741 /*
742 * syntax := { "encode-int16": <numeric_expression> }
743 * semantic: return a string buffer with the evaluated
744 * number as content
745 */
746 return NULL;
747
748 /* encode-int32 */
749 if (mapContains(expr, "encode-int32"))
750 /*
751 * syntax := { "encode-int32": <numeric_expression> }
752 * semantic: return a string buffer with the evaluated
753 * number as content
754 */
755 return NULL;
756
757 /* gethostbyname */
758 if (mapContains(expr, "gethostbyname"))
759 /*
760 * syntax := { "gethostbyname": <string> }
761 * semantic: call gethostbyname and return
762 * a binary buffer with addresses
763 */
764 return NULL;
765
766 /* binary-to-ascii */
767 if (mapContains(expr, "binary-to-ascii"))
768 /*
769 * syntax := { "binary-to-ascii":
770 * { "base": <numeric_expression 2..16>,
771 * "width": <numeric_expression 8, 16 or 32>,
772 * "separator": <data_expression>,
773 * "buffer": <data_expression> }
774 * }
775 * semantic: split the input buffer into int8/16/32 numbers,
776 * output them separated by the given string
777 */
778 return NULL;
779
780 /* filename */
781 if (mapContains(expr, "filename"))
782 /*
783 * syntax := { "filename": null }
784 * semantic: get filename field from incoming DHCPv4 packet
785 */
786 return NULL;
787
788 /* server-name */
789 if (mapContains(expr, "server-name"))
790 /*
791 * syntax := { "server-name": null }
792 * semantic: get server-name field from incoming DHCPv4 packet
793 */
794 return NULL;
795
796 /* reverse */
797 if (mapContains(expr, "reverse"))
798 /*
799 * syntax := { "reverse":
800 * { "width": <numeric_expression>,
801 * "buffer": <data_expression> }
802 * }
803 * semantic: reverse the input buffer by width chunks of bytes
804 */
805 return NULL;
806
807 /* pick-first-value */
808 if (mapContains(expr, "pick-first-value"))
809 /*
810 * syntax := { "pick-first-value":
811 * [ <data_expression>, ... ]
812 * }
813 * semantic: evaluates expressions and return the first
814 * not null, return null if all are null
815 */
816 return NULL;
817
818 /* host-decl-name */
819 if (mapContains(expr, "host-decl-name"))
820 /*
821 * syntax := { "host-decl-name": null }
822 * semantic: return the name of the matching host
823 * declaration (aka revervation in kea) or null
824 */
825 return NULL;
826
827 /* leased-address */
828 if (mapContains(expr, "leased-address"))
829 /*
830 * syntax := { "leased-address": null }
831 * semantic: return the address of the assigned lease or
832 * log a message
833 */
834 return NULL;
835
836 /* config-option */
837 if (mapContains(expr, "config-option"))
838 /*
839 * syntax := { "config-option":
840 * { "universe": <option_space_old>,
841 * "name": <option_name> }
842 * }
843 * semantic: get universe/code option to send
844 */
845 return NULL;
846
847 /* null */
848 if (mapContains(expr, "null")) {
849 /*
850 * syntax := { "null": null }
851 * semantic: return null
852 */
853 debug("unexpected null: this expression was not evaluated");
854 return NULL;
855 }
856
857 /* gethostname */
858 if (mapContains(expr, "gethostname")) {
859 /*
860 * syntax := { "gethostname": null }
861 * semantic: return gethostname
862 */
863 debug("unexpected gethostname: this expression was not "
864 "evaluated");
865 return NULL;
866 }
867
868 /* v6relay */
869 if (mapContains(expr, "v6relay")) {
870 /*
871 * syntax := { "v6relay":
872 * { "relay": <numeric_expression>,
873 * "relay-option" <data_expression> }
874 * }
875 * semantic: relay is a counter from client, 0 is no-op,
876 * 1 is the relay closest to the client, etc, option
877 * is a dhcp6 option ans is return when found
878 */
879 struct element *arg;
880 struct element *relay;
881 struct element *universe;
882 struct element *name;
883 struct option *option;
884 int64_t r;
885 char result[100];
886
887 if (local_family != AF_INET6) {
888 debug("get v6relay for DHCPv4");
889 return NULL;
890 }
891 arg = mapGet(expr, "v6relay");
892 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
893 debug("can't get v6relay argument");
894 return NULL;
895 }
896 relay = mapGet(arg, "relay");
897 if (relay == NULL) {
898 debug("can't get v6relay relay");
899 return NULL;
900 }
901 relay = reduce_numeric_expression(relay);
902 if ((relay == NULL) || (relay->type != ELEMENT_INTEGER))
903 return NULL;
904 r = intValue(relay);
905 if (r < 0) {
906 debug("v6relay called with illegal relay (%lld)",
907 (long long)r);
908 return NULL;
909 }
910 arg = mapGet(arg, "relay-option");
911 if ((arg == NULL) || (arg->type != ELEMENT_MAP)) {
912 debug("can't get v6relay relay-option");
913 return NULL;
914 }
915 universe = mapGet(arg, "universe");
916 if ((universe == NULL) || (universe->type != ELEMENT_STRING)) {
917 debug("can't get v6relay option universe");
918 NULL;
919 }
920 name = mapGet(arg, "name");
921 if ((name == NULL) || (name->type != ELEMENT_STRING)) {
922 debug("can't get v6relay option name");
923 return NULL;
924 }
925 option = option_lookup_name(stringValue(universe)->content,
926 stringValue(name)->content);
927 if ((option == NULL) || (option->code == 0) ||
928 (strcmp(option->space->name, "dhcp6") != 0))
929 return NULL;
930 if (r == 0)
931 snprintf(result, sizeof(result),
932 "option[%u].hex", option->code);
933 else {
934 /* r > MAX_V6RELAY_HOPS means the relay closest
935 to server */
936 if (r > MAX_V6RELAY_HOPS)
937 r = 0;
938 /* Kea counts from the server, use negative nesting
939 levels to count from the client */
940 snprintf(result, sizeof(result),
941 "relay6[%d].option[%u].hex",
942 (int)-r, option->code);
943 }
944 return createString(makeString(-1, result));
945 }
946
947 return NULL;
948 }
949
950 struct element *
reduce_numeric_expression(struct element * expr)951 reduce_numeric_expression(struct element *expr)
952 {
953 /* trivial case: already done */
954 if (expr->type == ELEMENT_INTEGER)
955 return expr;
956
957 if (expr->type != ELEMENT_MAP)
958 return NULL;
959
960 /* Kea has no numeric operators... */
961 return NULL;
962 }
963
964 static struct element *
reduce_equal_expression(struct element * left,struct element * right)965 reduce_equal_expression(struct element *left, struct element *right)
966 {
967 struct string *result;
968
969 /*
970 * numeric case was handled by evaluation
971 */
972
973 if (!is_data_expression(left) || !is_data_expression(right))
974 return NULL;
975
976 /* left is a literal case */
977 if (left->type == ELEMENT_STRING) {
978 /* can't be a literal as it was evaluated before */
979 right = reduce_data_expression(right);
980 if ((right == NULL) || (right->type != ELEMENT_STRING))
981 return NULL;
982 result = allocString();
983 concatString(result, quote(stringValue(left)));
984 appendString(result, " == ");
985 concatString(result, stringValue(right));
986 return createString(result);
987 }
988 left = reduce_data_expression(left);
989 if ((left == NULL) || (left->type != ELEMENT_STRING))
990 return NULL;
991
992 /* right is a literal case */
993 if (right->type == ELEMENT_STRING) {
994 /* literal left was handled before */
995 result = allocString();
996 concatString(result, stringValue(left));
997 appendString(result, " == ");
998 concatString(result, quote(stringValue(right)));
999 return createString(result);
1000 }
1001 right = reduce_data_expression(right);
1002 if ((right == NULL) || (right->type != ELEMENT_STRING))
1003 return NULL;
1004
1005 result = allocString();
1006 concatString(result, stringValue(left));
1007 appendString(result, " == ");
1008 concatString(result, stringValue(right));
1009 return createString(result);
1010 }
1011
1012 static void
debug(const char * fmt,...)1013 debug(const char* fmt, ...)
1014 {
1015 va_list list;
1016
1017 va_start(list, fmt);
1018 vfprintf(stderr, fmt, list);
1019 fprintf(stderr, "\n");
1020 va_end(list);
1021 }
1022