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