1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/snmpd/config.c,v 1.22 2004/08/12 17:09:49 brandt Exp $ 30 * 31 * Parse configuration file. 32 */ 33 #include <sys/types.h> 34 #include <sys/socket.h> 35 #include <sys/un.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <stdarg.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <syslog.h> 43 #include <unistd.h> 44 #include <limits.h> 45 #include <netdb.h> 46 #include <setjmp.h> 47 #include <inttypes.h> 48 49 #include "snmpmod.h" 50 #include "snmpd.h" 51 #include "tree.h" 52 53 /* 54 #define DEBUGGING 55 */ 56 57 /* 58 * config_file: EMPTY | config_file line 59 * 60 * line: oid '=' value 61 * | '%' STRING 62 * | STRING := REST_OF_LINE 63 * | STRING ?= REST_OF_LINE 64 * | . INCLUDE STRING 65 * 66 * oid: STRING suboid 67 * 68 * suboid: EMPTY | suboid '.' subid 69 * 70 * subid: NUM | STRING | '[' STRING ']' 71 * 72 * value: EMPTY | STRING | NUM 73 */ 74 75 /* 76 * Input context for macros and includes 77 */ 78 enum input_type { 79 INPUT_FILE = 1, 80 INPUT_STRING 81 }; 82 struct input { 83 enum input_type type; 84 union { 85 struct { 86 FILE *fp; 87 char *filename; 88 u_int lno; 89 } file; 90 struct { 91 char *macro; 92 char *str; 93 char *ptr; 94 size_t left; 95 } str; 96 } u; 97 LIST_ENTRY(input) link; 98 }; 99 static LIST_HEAD(, input) inputs; 100 101 #define input_fp u.file.fp 102 #define input_filename u.file.filename 103 #define input_lno u.file.lno 104 #define input_macro u.str.macro 105 #define input_str u.str.str 106 #define input_ptr u.str.ptr 107 #define input_left u.str.left 108 109 static int input_push; 110 static int input_buf[2]; 111 112 /* 113 * Configuration data. The configuration file is handled as one single 114 * SNMP transaction. So we need to keep the assignment data for the 115 * commit or rollback pass. Note, that dependencies and finish functions 116 * are NOT allowed here. 117 */ 118 struct assign { 119 struct snmp_value value; 120 struct snmp_scratch scratch; 121 const char *node_name; 122 123 TAILQ_ENTRY(assign) link; 124 }; 125 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns); 126 127 128 static struct snmp_context *snmp_ctx; 129 130 struct macro { 131 char *name; 132 char *value; 133 size_t length; 134 LIST_ENTRY(macro) link; 135 int perm; 136 }; 137 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(¯os); 138 139 enum { 140 TOK_EOF = 0200, 141 TOK_EOL, 142 TOK_NUM, 143 TOK_STR, 144 TOK_HOST, 145 TOK_ASSIGN, 146 TOK_QASSIGN, 147 }; 148 149 /* lexer values and last token */ 150 static uint64_t numval; 151 static char strval[_POSIX2_LINE_MAX]; 152 static size_t strvallen; 153 static int token; 154 155 /* error return */ 156 static jmp_buf errjmp[4]; 157 static volatile int errstk; 158 159 # define ERRPUSH() (setjmp(errjmp[errstk++])) 160 # define ERRPOP() ((void)(errstk--)) 161 # define ERRNEXT() (longjmp(errjmp[--errstk], 1)) 162 # define ERR() (longjmp(errjmp[--errstk], 1)) 163 164 /* section context */ 165 static int ignore; 166 167 /* 168 * Report an error and jump to the error label 169 */ 170 static void report(const char *fmt, ...) __dead2 __printflike(1, 2); 171 172 static void 173 report(const char *fmt, ...) 174 { 175 va_list ap; 176 const struct input *input; 177 178 va_start(ap, fmt); 179 vsyslog(LOG_ERR, fmt, ap); 180 va_end(ap); 181 182 LIST_FOREACH(input, &inputs, link) { 183 switch (input->type) { 184 185 case INPUT_FILE: 186 syslog(LOG_ERR, " in file %s line %u", 187 input->input_filename, input->input_lno); 188 break; 189 190 case INPUT_STRING: 191 syslog(LOG_ERR, " in macro %s pos %td", 192 input->input_macro, 193 input->input_ptr - input->input_str); 194 break; 195 } 196 } 197 ERR(); 198 } 199 200 /* 201 * Open a file for input 202 */ 203 static int 204 input_open_file(const char *fname, int sysdir) 205 { 206 struct input *input; 207 FILE *fp; 208 char path[PATH_MAX + 1]; 209 char *col; 210 const char *ptr; 211 212 if (sysdir) { 213 ptr = syspath; 214 fp = NULL; 215 while (*ptr != '\0') { 216 if ((col = strchr(ptr, ':')) == NULL) 217 snprintf(path, sizeof(path), "%s/%s", 218 ptr, fname); 219 else if (col == ptr) 220 snprintf(path, sizeof(path), "./%s", fname); 221 else 222 snprintf(path, sizeof(path), "%.*s/%s", 223 (int)(col - ptr), ptr, fname); 224 if ((fp = fopen(path, "r")) != NULL) 225 break; 226 ptr = col + 1; 227 } 228 } else 229 fp = fopen(fname, "r"); 230 231 if (fp == NULL) 232 report("%s: %m", fname); 233 234 if ((input = malloc(sizeof(*input))) == NULL) { 235 fclose(fp); 236 return (-1); 237 } 238 if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) { 239 fclose(fp); 240 free(input); 241 return (-1); 242 } 243 strcpy(input->input_filename, fname); 244 input->input_fp = fp; 245 input->input_lno = 1; 246 input->type = INPUT_FILE; 247 LIST_INSERT_HEAD(&inputs, input, link); 248 return (0); 249 } 250 251 /* 252 * Make a macro the next input 253 */ 254 static void 255 input_open_macro(struct macro *m) 256 { 257 struct input *input; 258 259 if ((input = malloc(sizeof(*input))) == NULL) 260 report("%m"); 261 input->type = INPUT_STRING; 262 input->input_macro = m->name; 263 if ((input->input_str = malloc(m->length)) == NULL) { 264 free(input); 265 report("%m"); 266 } 267 memcpy(input->input_str, m->value, m->length); 268 input->input_ptr = input->input_str; 269 input->input_left = m->length; 270 LIST_INSERT_HEAD(&inputs, input, link); 271 } 272 273 /* 274 * Close top input source 275 */ 276 static void 277 input_close(void) 278 { 279 struct input *input; 280 281 if ((input = LIST_FIRST(&inputs)) == NULL) 282 abort(); 283 switch (input->type) { 284 285 case INPUT_FILE: 286 fclose(input->input_fp); 287 free(input->input_filename); 288 break; 289 290 case INPUT_STRING: 291 free(input->input_str); 292 break; 293 } 294 LIST_REMOVE(input, link); 295 free(input); 296 } 297 298 /* 299 * Close all inputs 300 */ 301 static void 302 input_close_all(void) 303 { 304 while (!LIST_EMPTY(&inputs)) 305 input_close(); 306 } 307 308 /* 309 * Push back one character 310 */ 311 static void 312 input_ungetc(int c) 313 { 314 if (c == EOF) 315 report("pushing EOF"); 316 if (input_push == 2) 317 report("pushing third char"); 318 input_buf[input_push++] = c; 319 } 320 321 322 /* 323 * Return next character from the input without preprocessing. 324 */ 325 static int 326 input_getc_raw(void) 327 { 328 int c; 329 struct input *input; 330 331 if (input_push != 0) { 332 c = input_buf[--input_push]; 333 goto ok; 334 } 335 while ((input = LIST_FIRST(&inputs)) != NULL) { 336 switch (input->type) { 337 338 case INPUT_FILE: 339 if ((c = getc(input->input_fp)) == EOF) { 340 if (ferror(input->input_fp)) 341 report("read error: %m"); 342 input_close(); 343 break; 344 } 345 if (c == '\n') 346 input->input_lno++; 347 goto ok; 348 349 case INPUT_STRING: 350 if (input->input_left-- == 0) { 351 input_close(); 352 break; 353 } 354 c = *input->input_ptr++; 355 goto ok; 356 } 357 } 358 # ifdef DEBUGGING 359 fprintf(stderr, "EOF"); 360 # endif 361 return (EOF); 362 363 ok: 364 # ifdef DEBUGGING 365 if (!isascii(c) || !isprint(c)) 366 fprintf(stderr, "'%#2x'", c); 367 else 368 fprintf(stderr, "'%c'", c); 369 # endif 370 return (c); 371 } 372 373 /* 374 * Get character with and \\n -> processing. 375 */ 376 static int 377 input_getc_plain(void) 378 { 379 int c; 380 381 again: 382 if ((c = input_getc_raw()) == '\\') { 383 if ((c = input_getc_raw()) == '\n') 384 goto again; 385 if (c != EOF) 386 input_ungetc(c); 387 return ('\\'); 388 } 389 return (c); 390 } 391 392 /* 393 * Get next character with substitution of macros 394 */ 395 static int 396 input_getc(void) 397 { 398 int c; 399 struct macro *m; 400 char name[_POSIX2_LINE_MAX]; 401 size_t namelen; 402 403 again: 404 if ((c = input_getc_plain()) != '$') 405 return (c); 406 407 if ((c = input_getc()) == EOF) 408 report("unexpected EOF"); 409 if (c != '(') 410 report("expecting '(' after '$'"); 411 412 namelen = 0; 413 while ((c = input_getc()) != EOF && c != ')') { 414 if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c))) 415 name[namelen++] = c; 416 else 417 goto badchar; 418 } 419 if (c == EOF) 420 report("unexpected EOF"); 421 name[namelen++] = '\0'; 422 423 LIST_FOREACH(m, ¯os, link) 424 if (strcmp(m->name, name) == 0) 425 break; 426 if (m == NULL) 427 report("undefined macro '%s'", name); 428 429 input_open_macro(m); 430 goto again; 431 432 badchar: 433 if (!isascii(c) || !isprint(c)) 434 report("unexpected character %#2x", (u_int)c); 435 else 436 report("bad character '%c'", c); 437 } 438 439 440 static void 441 input_getnum(u_int base, u_int flen) 442 { 443 int c; 444 u_int cnt; 445 446 cnt = 0; 447 numval = 0; 448 while (flen == 0 || cnt < flen) { 449 if ((c = input_getc()) == EOF) { 450 if (cnt == 0) 451 report("bad number"); 452 return; 453 } 454 if (isdigit(c)) { 455 if (base == 8 && (c == '8' || c == '9')) { 456 input_ungetc(c); 457 if (cnt == 0) 458 report("bad number"); 459 return; 460 } 461 numval = numval * base + (c - '0'); 462 } else if (base == 16 && isxdigit(c)) { 463 if (islower(c)) 464 numval = numval * base + (c - 'a' + 10); 465 else 466 numval = numval * base + (c - 'A' + 10); 467 } else { 468 input_ungetc(c); 469 if (cnt == 0) 470 report("bad number"); 471 return; 472 } 473 cnt++; 474 } 475 } 476 477 static int 478 # ifdef DEBUGGING 479 _gettoken(void) 480 # else 481 gettoken(void) 482 # endif 483 { 484 int c; 485 char *end; 486 static const char esc[] = "abfnrtv"; 487 static const char chr[] = "\a\b\f\n\r\t\v"; 488 489 /* 490 * Skip any whitespace before the next token 491 */ 492 while ((c = input_getc()) != EOF) { 493 if (!isspace(c) || c == '\n') 494 break; 495 } 496 if (c == EOF) 497 return (token = TOK_EOF); 498 if (!isascii(c)) 499 goto badchar; 500 501 /* 502 * Skip comments 503 */ 504 if (c == '#') { 505 while ((c = input_getc_plain()) != EOF) { 506 if (c == '\n') 507 return (token = TOK_EOL); 508 } 509 goto badeof; 510 } 511 512 /* 513 * Single character tokens 514 */ 515 if (c == '\n') 516 return (token = TOK_EOL); 517 if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>') 518 return (token = c); 519 if (c == ':') { 520 if ((c = input_getc()) == '=') 521 return (token = TOK_ASSIGN); 522 input_ungetc(c); 523 return (token = ':'); 524 } 525 if (c == '?') { 526 if ((c = input_getc()) == '=') 527 return (token = TOK_QASSIGN); 528 input_ungetc(c); 529 goto badchar; 530 } 531 532 /* 533 * Sort out numbers 534 */ 535 if (isdigit(c)) { 536 if (c == '0') { 537 if ((c = input_getc()) == 'x' || c == 'X') { 538 input_getnum(16, 0); 539 } else if (isdigit(c)) { 540 input_ungetc(c); 541 input_getnum(8, 0); 542 } else { 543 if (c != EOF) 544 input_ungetc(c); 545 numval = 0; 546 c = 1; 547 } 548 } else { 549 input_ungetc(c); 550 input_getnum(10, 0); 551 } 552 return (token = TOK_NUM); 553 } 554 555 /* 556 * Must be a string then 557 */ 558 strvallen = 0; 559 560 # define GETC(C) do { \ 561 if ((c = input_getc()) == EOF) \ 562 goto badeof; \ 563 if (!isascii(c) || (!isprint(c) && c != '\t')) \ 564 goto badchar; \ 565 } while(0) 566 567 if (c == '"') { 568 for(;;) { 569 GETC(c); 570 if (c == '"') { 571 strval[strvallen] = '\0'; 572 break; 573 } 574 if (c != '\\') { 575 strval[strvallen++] = c; 576 continue; 577 } 578 GETC(c); 579 if ((end = strchr(esc, c)) != NULL) { 580 strval[strvallen++] = chr[end - esc]; 581 continue; 582 } 583 if (c == 'x') { 584 input_getnum(16, 2); 585 c = numval; 586 } else if (c >= '0' && c <= '7') { 587 input_ungetc(c); 588 input_getnum(8, 3); 589 c = numval; 590 } 591 strval[strvallen++] = c; 592 } 593 # undef GETC 594 595 } else if (c == '[') { 596 /* 597 * Skip leading space 598 */ 599 while ((c = input_getc()) != EOF && isspace(c)) 600 ; 601 if (c == EOF) 602 goto badeof; 603 while (c != ']' && !isspace(c)) { 604 if (!isalnum(c) && c != '.' && c != '-') 605 goto badchar; 606 strval[strvallen++] = c; 607 if ((c = input_getc()) == EOF) 608 goto badeof; 609 } 610 while (c != ']' && isspace(c)) { 611 if ((c = input_getc()) == EOF) 612 goto badeof; 613 } 614 if (c != ']') 615 goto badchar; 616 strval[strvallen] = '\0'; 617 return (token = TOK_HOST); 618 619 } else if (!isalpha(c) && c != '_') { 620 goto badchar; 621 622 } else { 623 for (;;) { 624 strval[strvallen++] = c; 625 if ((c = input_getc()) == EOF) 626 goto badeof; 627 if (!isalnum(c) && c != '_' && c != '-') { 628 input_ungetc(c); 629 strval[strvallen] = '\0'; 630 break; 631 } 632 } 633 } 634 635 return (token = TOK_STR); 636 637 badeof: 638 report("unexpected EOF"); 639 640 badchar: 641 if (!isascii(c) || !isprint(c)) 642 report("unexpected character %#2x", (u_int)c); 643 else 644 report("bad character '%c'", c); 645 } 646 647 # ifdef DEBUGGING 648 static int 649 gettoken() 650 { 651 _gettoken(); 652 if (isascii(token) && isprint(token)) 653 printf("(%c)", token); 654 else { 655 switch (token) { 656 657 case TOK_EOF: 658 printf("(EOF)"); 659 break; 660 case TOK_EOL: 661 printf("(EOL)"); 662 break; 663 case TOK_NUM: 664 printf("(NUM %llu)", numval); 665 break; 666 case TOK_STR: 667 printf("(STR %.*s)", (int)strvallen, strval); 668 break; 669 case TOK_HOST: 670 printf("(HOST %s)", strval); 671 break; 672 default: 673 printf("(%#2x)", token); 674 break; 675 } 676 } 677 return (token); 678 } 679 #endif 680 681 682 /* 683 * Try to execute the assignment. 684 */ 685 static void 686 handle_assignment(const struct snmp_node *node, struct asn_oid *vindex, 687 const struct snmp_value *value) 688 { 689 u_int i; 690 int err; 691 struct assign *tp; 692 char nodename[100]; 693 694 if (node->type == SNMP_NODE_LEAF) { 695 /* index must be one single zero or no index at all */ 696 if (vindex->len > 1 || (vindex->len == 1 && 697 vindex->subs[0] != 0)) 698 report("bad index on leaf node"); 699 vindex->len = 1; 700 vindex->subs[0] = 0; 701 } else { 702 /* resulting oid must not be too long */ 703 if (node->oid.len + vindex->len > ASN_MAXOIDLEN) 704 report("resulting OID too long"); 705 } 706 707 /* 708 * Get the next assignment entry for the transaction. 709 */ 710 if ((tp = malloc(sizeof(*tp))) == NULL) 711 report("%m"); 712 713 tp->value = *value; 714 tp->node_name = node->name; 715 716 /* 717 * Build the OID 718 */ 719 tp->value.var = node->oid; 720 for (i = 0; i < vindex->len; i++) 721 tp->value.var.subs[tp->value.var.len++] = vindex->subs[i]; 722 723 /* 724 * Puzzle together the variables for the call and call the 725 * set routine. The set routine may make our node pointer 726 * invalid (if we happend to call the module loader) so 727 * get a copy of the node name beforehands. 728 */ 729 snprintf(nodename, sizeof(nodename), "%s", node->name); 730 snmp_ctx->scratch = &tp->scratch; 731 snmp_ctx->var_index = 0; 732 err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index, 733 SNMP_OP_SET); 734 if (err != 0) { 735 free(tp); 736 report("assignment to %s.%s returns %d", nodename, 737 asn_oid2str(vindex), err); 738 } 739 740 TAILQ_INSERT_TAIL(&assigns, tp, link); 741 } 742 743 744 /* 745 * Parse the section statement 746 */ 747 static void 748 parse_section(const struct lmodule *mod) 749 { 750 if (token != TOK_STR) 751 report("expecting section name"); 752 753 if (strcmp(strval, "snmpd") == 0) { 754 if (mod != NULL) 755 /* loading a module - ignore common stuff */ 756 ignore = 1; 757 else 758 /* global configuration - don't ignore */ 759 ignore = 0; 760 } else { 761 if (mod == NULL) { 762 /* global configuration - ignore module stuff */ 763 ignore = 1; 764 } else { 765 /* loading module - check if it's our section */ 766 ignore = (strcmp(strval, mod->section) != 0); 767 } 768 } 769 gettoken(); 770 } 771 772 /* 773 * Convert a hostname to four u_chars 774 */ 775 static void 776 gethost(const char *host, u_char *ip) 777 { 778 struct addrinfo hints, *res; 779 int error; 780 struct sockaddr_in *sain; 781 782 memset(&hints, 0, sizeof(hints)); 783 hints.ai_family = AF_INET; 784 hints.ai_socktype = SOCK_DGRAM; 785 hints.ai_protocol = IPPROTO_UDP; 786 hints.ai_flags = AI_PASSIVE; 787 error = getaddrinfo(host, NULL, &hints, &res); 788 if (error != 0) 789 report("%s: %s", host, gai_strerror(error)); 790 if (res == NULL) 791 report("%s: unknown hostname", host); 792 793 sain = (struct sockaddr_in *)(void *)res->ai_addr; 794 sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr); 795 ip[0] = sain->sin_addr.s_addr >> 24; 796 ip[1] = sain->sin_addr.s_addr >> 16; 797 ip[2] = sain->sin_addr.s_addr >> 8; 798 ip[3] = sain->sin_addr.s_addr >> 0; 799 800 freeaddrinfo(res); 801 } 802 803 /* 804 * Parse the left hand side of a config line. 805 */ 806 static const struct snmp_node * 807 parse_oid(const char *varname, struct asn_oid *oid) 808 { 809 struct snmp_node *node; 810 u_int i; 811 u_char ip[4]; 812 813 for (node = tree; node < &tree[tree_size]; node++) 814 if (strcmp(varname, node->name) == 0) 815 break; 816 if (node == &tree[tree_size]) 817 node = NULL; 818 819 oid->len = 0; 820 while (token == '.') { 821 if (gettoken() == TOK_NUM) { 822 if (numval > ASN_MAXID) 823 report("subid too large %#"PRIx64, numval); 824 if (oid->len == ASN_MAXOIDLEN) 825 report("index too long"); 826 oid->subs[oid->len++] = numval; 827 828 } else if (token == TOK_STR) { 829 if (strvallen + oid->len + 1 > ASN_MAXOIDLEN) 830 report("oid too long"); 831 oid->subs[oid->len++] = strvallen; 832 for (i = 0; i < strvallen; i++) 833 oid->subs[oid->len++] = strval[i]; 834 835 } else if (token == TOK_HOST) { 836 gethost(strval, ip); 837 if (oid->len + 4 > ASN_MAXOIDLEN) 838 report("index too long"); 839 for (i = 0; i < 4; i++) 840 oid->subs[oid->len++] = ip[i]; 841 842 } else 843 report("bad token in index"); 844 gettoken(); 845 } 846 847 return (node); 848 } 849 850 /* 851 * Parse the value for an assignment. 852 */ 853 static void 854 parse_syntax_null(struct snmp_value *value __unused) 855 { 856 if (token != TOK_EOL) 857 report("bad NULL syntax"); 858 } 859 860 static void 861 parse_syntax_integer(struct snmp_value *value) 862 { 863 if (token != TOK_NUM) 864 report("bad INTEGER syntax"); 865 if (numval > 0x7fffffff) 866 report("INTEGER too large %"PRIu64, numval); 867 868 value->v.integer = numval; 869 gettoken(); 870 } 871 872 static void 873 parse_syntax_counter64(struct snmp_value *value) 874 { 875 if (token != TOK_NUM) 876 report("bad COUNTER64 syntax"); 877 878 value->v.counter64 = numval; 879 gettoken(); 880 } 881 882 static void 883 parse_syntax_octetstring(struct snmp_value *value) 884 { 885 u_long alloc; 886 u_char *noct; 887 888 if (token == TOK_STR) { 889 value->v.octetstring.len = strvallen; 890 value->v.octetstring.octets = malloc(strvallen); 891 (void)memcpy(value->v.octetstring.octets, strval, strvallen); 892 gettoken(); 893 return; 894 } 895 896 /* XX:XX:XX syntax */ 897 value->v.octetstring.octets = NULL; 898 value->v.octetstring.len = 0; 899 900 if (token != TOK_NUM) 901 /* empty string is allowed */ 902 return; 903 904 if (ERRPUSH()) { 905 free(value->v.octetstring.octets); 906 ERRNEXT(); 907 } 908 909 alloc = 0; 910 for (;;) { 911 if (token != TOK_NUM) 912 report("bad OCTETSTRING syntax"); 913 if (numval > 0xff) 914 report("byte value too large"); 915 if (alloc == value->v.octetstring.len) { 916 alloc += 100; 917 noct = realloc(value->v.octetstring.octets, alloc); 918 if (noct == NULL) 919 report("%m"); 920 value->v.octetstring.octets = noct; 921 } 922 value->v.octetstring.octets[value->v.octetstring.len++] 923 = numval; 924 if (gettoken() != ':') 925 break; 926 gettoken(); 927 } 928 ERRPOP(); 929 } 930 931 static void 932 parse_syntax_oid(struct snmp_value *value) 933 { 934 value->v.oid.len = 0; 935 936 if (token != TOK_NUM) 937 return; 938 939 for (;;) { 940 if (token != TOK_NUM) 941 report("bad OID syntax"); 942 if (numval > ASN_MAXID) 943 report("subid too large"); 944 if (value->v.oid.len == ASN_MAXOIDLEN) 945 report("OID too long"); 946 value->v.oid.subs[value->v.oid.len++] = numval; 947 if (gettoken() != '.') 948 break; 949 gettoken(); 950 } 951 } 952 953 static void 954 parse_syntax_ipaddress(struct snmp_value *value) 955 { 956 int i; 957 u_char ip[4]; 958 959 if (token == TOK_NUM) { 960 /* numerical address */ 961 i = 0; 962 for (;;) { 963 if (numval >= 256) 964 report("ip address part too large"); 965 value->v.ipaddress[i++] = numval; 966 if (i == 4) 967 break; 968 if (gettoken() != '.') 969 report("expecting '.' in ip address"); 970 } 971 gettoken(); 972 973 } else if (token == TOK_HOST) { 974 /* host name */ 975 gethost(strval, ip); 976 for (i = 0; i < 4; i++) 977 value->v.ipaddress[i] = ip[i]; 978 gettoken(); 979 980 } else 981 report("bad ip address syntax"); 982 } 983 984 static void 985 parse_syntax_uint32(struct snmp_value *value) 986 { 987 988 if (token != TOK_NUM) 989 report("bad number syntax"); 990 if (numval > 0xffffffff) 991 report("number too large"); 992 value->v.uint32 = numval; 993 gettoken(); 994 } 995 996 /* 997 * Parse an assignement line 998 */ 999 static void 1000 parse_assign(const char *varname) 1001 { 1002 struct snmp_value value; 1003 struct asn_oid vindex; 1004 const struct snmp_node *node; 1005 1006 node = parse_oid(varname, &vindex); 1007 if (token != '=') 1008 report("'=' expected"); 1009 gettoken(); 1010 1011 if (ignore) { 1012 /* skip rest of line */ 1013 while (token != TOK_EOL && token != TOK_EOF) 1014 gettoken(); 1015 return; 1016 } 1017 if (node == NULL) 1018 report("unknown variable"); 1019 1020 switch (value.syntax = node->syntax) { 1021 1022 case SNMP_SYNTAX_NULL: 1023 parse_syntax_null(&value); 1024 break; 1025 1026 case SNMP_SYNTAX_INTEGER: 1027 parse_syntax_integer(&value); 1028 break; 1029 1030 case SNMP_SYNTAX_COUNTER64: 1031 parse_syntax_counter64(&value); 1032 break; 1033 1034 case SNMP_SYNTAX_OCTETSTRING: 1035 parse_syntax_octetstring(&value); 1036 break; 1037 1038 case SNMP_SYNTAX_OID: 1039 parse_syntax_oid(&value); 1040 break; 1041 1042 case SNMP_SYNTAX_IPADDRESS: 1043 parse_syntax_ipaddress(&value); 1044 break; 1045 1046 case SNMP_SYNTAX_COUNTER: 1047 case SNMP_SYNTAX_GAUGE: 1048 case SNMP_SYNTAX_TIMETICKS: 1049 parse_syntax_uint32(&value); 1050 break; 1051 1052 case SNMP_SYNTAX_NOSUCHOBJECT: 1053 case SNMP_SYNTAX_NOSUCHINSTANCE: 1054 case SNMP_SYNTAX_ENDOFMIBVIEW: 1055 abort(); 1056 } 1057 1058 if (ERRPUSH()) { 1059 snmp_value_free(&value); 1060 ERRNEXT(); 1061 } 1062 1063 handle_assignment(node, &vindex, &value); 1064 1065 ERRPOP(); 1066 } 1067 1068 /* 1069 * Handle macro definition line 1070 * We have already seen the := and the input now stands at the character 1071 * after the =. Skip whitespace and then call the input routine directly to 1072 * eat up characters. 1073 */ 1074 static void 1075 parse_define(const char *varname) 1076 { 1077 char *volatile string; 1078 char *new; 1079 volatile size_t alloc, length; 1080 int c; 1081 struct macro *m; 1082 int t = token; 1083 1084 alloc = 100; 1085 length = 0; 1086 if ((string = malloc(alloc)) == NULL) 1087 report("%m"); 1088 1089 if (ERRPUSH()) { 1090 free(string); 1091 ERRNEXT(); 1092 } 1093 1094 while ((c = input_getc_plain()) != EOF) { 1095 if (c == '\n' || !isspace(c)) 1096 break; 1097 } 1098 1099 while (c != EOF && c != '#' && c != '\n') { 1100 if (alloc == length) { 1101 alloc *= 2; 1102 if ((new = realloc(string, alloc)) == NULL) 1103 report("%m"); 1104 string = new; 1105 } 1106 string[length++] = c; 1107 c = input_getc_plain(); 1108 } 1109 if (c == '#') { 1110 while ((c = input_getc_plain()) != EOF && c != '\n') 1111 ; 1112 } 1113 if (c == EOF) 1114 report("EOF in macro definition"); 1115 1116 LIST_FOREACH(m, ¯os, link) 1117 if (strcmp(m->name, varname) == 0) 1118 break; 1119 1120 if (m == NULL) { 1121 if ((m = malloc(sizeof(*m))) == NULL) 1122 report("%m"); 1123 if ((m->name = malloc(strlen(varname) + 1)) == NULL) { 1124 free(m); 1125 report("%m"); 1126 } 1127 strcpy(m->name, varname); 1128 m->perm = 0; 1129 LIST_INSERT_HEAD(¯os, m, link); 1130 1131 m->value = string; 1132 m->length = length; 1133 } else { 1134 if (t != TOK_ASSIGN) { 1135 free(m->value); 1136 m->value = string; 1137 m->length = length; 1138 } 1139 } 1140 1141 token = TOK_EOL; 1142 1143 ERRPOP(); 1144 } 1145 1146 /* 1147 * Free all macros 1148 */ 1149 static void 1150 macro_free_all(void) 1151 { 1152 static struct macro *m, *m1; 1153 1154 m = LIST_FIRST(¯os); 1155 while (m != NULL) { 1156 m1 = LIST_NEXT(m, link); 1157 if (!m->perm) { 1158 free(m->name); 1159 free(m->value); 1160 LIST_REMOVE(m, link); 1161 free(m); 1162 } 1163 m = m1; 1164 } 1165 } 1166 1167 /* 1168 * Parse an include directive and switch to the new file 1169 */ 1170 static void 1171 parse_include(void) 1172 { 1173 int sysdir = 0; 1174 char fname[_POSIX2_LINE_MAX]; 1175 1176 if (gettoken() == '<') { 1177 sysdir = 1; 1178 if (gettoken() != TOK_STR) 1179 report("expecting filename after in .include"); 1180 } else if (token != TOK_STR) 1181 report("expecting filename after in .include"); 1182 1183 strcpy(fname, strval); 1184 if (sysdir && gettoken() != '>') 1185 report("expecting '>'"); 1186 gettoken(); 1187 if (input_open_file(fname, sysdir) == -1) 1188 report("%s: %m", fname); 1189 } 1190 1191 /* 1192 * Parse the configuration file 1193 */ 1194 static void 1195 parse_file(const struct lmodule *mod) 1196 { 1197 char varname[_POSIX2_LINE_MAX]; 1198 1199 while (gettoken() != TOK_EOF) { 1200 if (token == TOK_EOL) 1201 /* empty line */ 1202 continue; 1203 if (token == '%') { 1204 gettoken(); 1205 parse_section(mod); 1206 } else if (token == '.') { 1207 if (gettoken() != TOK_STR) 1208 report("keyword expected after '.'"); 1209 if (strcmp(strval, "include") == 0) 1210 parse_include(); 1211 else 1212 report("unknown keyword '%s'", strval); 1213 } else if (token == TOK_STR) { 1214 strcpy(varname, strval); 1215 if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN) 1216 parse_define(varname); 1217 else 1218 parse_assign(varname); 1219 } 1220 if (token != TOK_EOL) 1221 report("eol expected"); 1222 } 1223 } 1224 1225 /* 1226 * Do rollback on errors 1227 */ 1228 static void 1229 do_rollback(void) 1230 { 1231 struct assign *tp; 1232 struct snmp_node *node; 1233 1234 while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) { 1235 TAILQ_REMOVE(&assigns, tp, link); 1236 for (node = tree; node < &tree[tree_size]; node++) 1237 if (node->name == tp->node_name) { 1238 snmp_ctx->scratch = &tp->scratch; 1239 (void)(*node->op)(snmp_ctx, &tp->value, 1240 node->oid.len, node->index, 1241 SNMP_OP_ROLLBACK); 1242 break; 1243 } 1244 if (node == &tree[tree_size]) 1245 syslog(LOG_ERR, "failed to find node for " 1246 "rollback"); 1247 snmp_value_free(&tp->value); 1248 free(tp); 1249 } 1250 } 1251 1252 /* 1253 * Do commit 1254 */ 1255 static void 1256 do_commit(void) 1257 { 1258 struct assign *tp; 1259 struct snmp_node *node; 1260 1261 while ((tp = TAILQ_FIRST(&assigns)) != NULL) { 1262 TAILQ_REMOVE(&assigns, tp, link); 1263 for (node = tree; node < &tree[tree_size]; node++) 1264 if (node->name == tp->node_name) { 1265 snmp_ctx->scratch = &tp->scratch; 1266 (void)(*node->op)(snmp_ctx, &tp->value, 1267 node->oid.len, node->index, SNMP_OP_COMMIT); 1268 break; 1269 } 1270 if (node == &tree[tree_size]) 1271 syslog(LOG_ERR, "failed to find node for commit"); 1272 snmp_value_free(&tp->value); 1273 free(tp); 1274 } 1275 } 1276 1277 /* 1278 * Read the configuration file. Handle the entire file as one transaction. 1279 * 1280 * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are 1281 * executed. If it is not NULL, only the sections for that module are handled. 1282 */ 1283 int 1284 read_config(const char *fname, struct lmodule *lodmod) 1285 { 1286 int err; 1287 char objbuf[ASN_OIDSTRLEN]; 1288 char idxbuf[ASN_OIDSTRLEN]; 1289 1290 ignore = 0; 1291 1292 input_push = 0; 1293 1294 if (ERRPUSH()) 1295 return (-1); 1296 if (input_open_file(fname, 0) == -1) { 1297 syslog(LOG_ERR, "%s: %m", fname); 1298 return (-1); 1299 } 1300 ERRPOP(); 1301 community = COMM_INITIALIZE; 1302 1303 if ((snmp_ctx = snmp_init_context()) == NULL) { 1304 input_close_all(); 1305 syslog(LOG_ERR, "%m"); 1306 return (-1); 1307 } 1308 1309 if (ERRPUSH()) { 1310 do_rollback(); 1311 input_close_all(); 1312 macro_free_all(); 1313 free(snmp_ctx); 1314 return (-1); 1315 } 1316 parse_file(lodmod); 1317 ERRPOP(); 1318 1319 if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) { 1320 syslog(LOG_ERR, "init dep failed: %u %s %s", err, 1321 asn_oid2str_r(&snmp_ctx->dep->obj, objbuf), 1322 asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf)); 1323 snmp_dep_rollback(snmp_ctx); 1324 do_rollback(); 1325 input_close_all(); 1326 macro_free_all(); 1327 free(snmp_ctx); 1328 return (-1); 1329 } 1330 1331 do_commit(); 1332 snmp_dep_finish(snmp_ctx); 1333 1334 macro_free_all(); 1335 1336 free(snmp_ctx); 1337 1338 return (0); 1339 } 1340 1341 /* 1342 * Define a permanent macro 1343 */ 1344 int 1345 define_macro(const char *name, const char *value) 1346 { 1347 struct macro *m; 1348 1349 if ((m = malloc(sizeof(*m))) == NULL) 1350 return (-1); 1351 if ((m->name = malloc(strlen(name) + 1)) == NULL) { 1352 free(m); 1353 return (-1); 1354 } 1355 strcpy(m->name, name); 1356 if ((m->value = malloc(strlen(value) + 1)) == NULL) { 1357 free(m->name); 1358 free(m); 1359 return (-1); 1360 } 1361 strcpy(m->value, value); 1362 m->length = strlen(value); 1363 return (0); 1364 } 1365