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 41static struct element *reduce_equal_expression(struct element *left, 42 struct element *right); 43static 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 56struct element * 57reduce_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 379struct element * 380reduce_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 950struct element * 951reduce_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 964static struct element * 965reduce_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 1012static void 1013debug(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