PageRenderTime 80ms CodeModel.GetById 17ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bsnmp/snmpd/config.c

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