xref: /freebsd/contrib/bsnmp/snmpd/action.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  * Copyright (c) 2004-2006
6  *	Hartmut Brandt.
7  *	All rights reserved.
8  *
9  * Author: Harti Brandt <harti@freebsd.org>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Begemot: action.c 517 2006-10-31 08:52:04Z brandt_h $
33  *
34  * Variable access for SNMPd
35  */
36 #include <sys/types.h>
37 #include <sys/sysctl.h>
38 #include <sys/un.h>
39 #include <sys/utsname.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <syslog.h>
46 
47 #include "snmpmod.h"
48 #include "snmpd.h"
49 #include "tree.h"
50 #include "oid.h"
51 
52 static const struct asn_oid
53 	oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;
54 
55 #ifdef __FreeBSD__
56 static const struct asn_oid
57 	oid_freeBSDVersion = OIDX_freeBSDVersion;
58 #endif
59 
60 /*
61  * Get a string value from the KERN sysctl subtree.
62  */
63 static char *
64 act_getkernstring(int id)
65 {
66 	int mib[2];
67 	size_t len;
68 	char *string;
69 
70 	mib[0] = CTL_KERN;
71 	mib[1] = id;
72 	if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0)
73 		return (NULL);
74 	if ((string = malloc(len)) == NULL)
75 		return (NULL);
76 	if (sysctl(mib, 2, string, &len, NULL, 0) != 0) {
77 		free(string);
78 		return (NULL);
79 	}
80 	return (string);
81 }
82 
83 /*
84  * Get an integer value from the KERN sysctl subtree.
85  */
86 static char *
87 act_getkernint(int id)
88 {
89 	int mib[2];
90 	size_t len;
91 	u_long value;
92 	char *string;
93 
94 	mib[0] = CTL_KERN;
95 	mib[1] = id;
96 	len = sizeof(value);
97 	if (sysctl(mib, 2, &value, &len, NULL, 0) != 0)
98 		return (NULL);
99 
100 	if ((string = malloc(20)) == NULL)
101 		return (NULL);
102 	sprintf(string, "%lu", value);
103 	return (string);
104 }
105 
106 /*
107  * Initialize global variables of the system group.
108  */
109 int
110 init_actvals(void)
111 {
112 	struct utsname uts;
113 	char *hostid;
114 	size_t len;
115 #ifdef __FreeBSD__
116 	char *rel, *p, *end;
117 	u_long num;
118 #endif
119 
120 	if (uname(&uts) == -1)
121 		return (-1);
122 
123 	if ((systemg.name = strdup(uts.nodename)) == NULL)
124 		return (-1);
125 
126 	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
127 		return (-1);
128 
129 	len = strlen(uts.nodename) + 1;
130 	len += strlen(hostid) + 1;
131 	len += strlen(uts.sysname) + 1;
132 	len += strlen(uts.release) + 1;
133 
134 	if ((systemg.descr = malloc(len)) == NULL) {
135 		free(hostid);
136 		return (-1);
137 	}
138 	sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname,
139 	    uts.release);
140 
141 #ifdef __FreeBSD__
142 	/*
143 	 * Construct a FreeBSD oid
144 	 */
145 	systemg.object_id = oid_freeBSDVersion;
146 	rel = uts.release;
147 	while ((p = strsep(&rel, ".")) != NULL &&
148 	    systemg.object_id.len < ASN_MAXOIDLEN) {
149 		systemg.object_id.subs[systemg.object_id.len] = 0;
150 		if (*p != '\0') {
151 			num = strtoul(p, &end, 10);
152 			if (end == p)
153 				break;
154 			systemg.object_id.subs[systemg.object_id.len] = num;
155 		}
156 		systemg.object_id.len++;
157 	}
158 #endif
159 
160 	free(hostid);
161 
162 	return (0);
163 }
164 
165 
166 
167 /*************************************************************
168  *
169  * System group
170  */
171 int
172 op_system_group(struct snmp_context *ctx, struct snmp_value *value,
173     u_int sub, u_int iidx __unused, enum snmp_op op)
174 {
175 	asn_subid_t which = value->var.subs[sub - 1];
176 
177 	switch (op) {
178 
179 	  case SNMP_OP_GETNEXT:
180 		abort();
181 
182 	  case SNMP_OP_GET:
183 		break;
184 
185 	  case SNMP_OP_SET:
186 		switch (which) {
187 
188 		  case LEAF_sysDescr:
189 			if (community != COMM_INITIALIZE)
190 				return (SNMP_ERR_NOT_WRITEABLE);
191 			return (string_save(value, ctx, -1, &systemg.descr));
192 
193 		  case LEAF_sysObjectId:
194 			if (community != COMM_INITIALIZE)
195 				return (SNMP_ERR_NOT_WRITEABLE);
196 			return (oid_save(value, ctx, &systemg.object_id));
197 
198 		  case LEAF_sysContact:
199 			return (string_save(value, ctx, -1, &systemg.contact));
200 
201 		  case LEAF_sysName:
202 			return (string_save(value, ctx, -1, &systemg.name));
203 
204 		  case LEAF_sysLocation:
205 			return (string_save(value, ctx, -1, &systemg.location));
206 		}
207 		return (SNMP_ERR_NO_CREATION);
208 
209 	  case SNMP_OP_ROLLBACK:
210 		switch (which) {
211 
212 		  case LEAF_sysDescr:
213 			string_rollback(ctx, &systemg.descr);
214 			return (SNMP_ERR_NOERROR);
215 		  case LEAF_sysObjectId:
216 			oid_rollback(ctx, &systemg.object_id);
217 			return (SNMP_ERR_NOERROR);
218 		  case LEAF_sysContact:
219 			string_rollback(ctx, &systemg.contact);
220 			return (SNMP_ERR_NOERROR);
221 		  case LEAF_sysName:
222 			string_rollback(ctx, &systemg.name);
223 			return (SNMP_ERR_NOERROR);
224 		  case LEAF_sysLocation:
225 			string_rollback(ctx, &systemg.location);
226 			return (SNMP_ERR_NOERROR);
227 		}
228 		abort();
229 
230 	  case SNMP_OP_COMMIT:
231 		switch (which) {
232 
233 		  case LEAF_sysDescr:
234 			string_commit(ctx);
235 			return (SNMP_ERR_NOERROR);
236 		  case LEAF_sysObjectId:
237 			oid_commit(ctx);
238 			return (SNMP_ERR_NOERROR);
239 		  case LEAF_sysContact:
240 			string_commit(ctx);
241 			return (SNMP_ERR_NOERROR);
242 		  case LEAF_sysName:
243 			string_commit(ctx);
244 			return (SNMP_ERR_NOERROR);
245 		  case LEAF_sysLocation:
246 			string_commit(ctx);
247 			return (SNMP_ERR_NOERROR);
248 		}
249 		abort();
250 	}
251 
252 	/*
253 	 * Come here for GET.
254 	 */
255 	switch (which) {
256 
257 	  case LEAF_sysDescr:
258 		return (string_get(value, systemg.descr, -1));
259 	  case LEAF_sysObjectId:
260 		return (oid_get(value, &systemg.object_id));
261 	  case LEAF_sysUpTime:
262 		value->v.uint32 = get_ticks() - start_tick;
263 		break;
264 	  case LEAF_sysContact:
265 		return (string_get(value, systemg.contact, -1));
266 	  case LEAF_sysName:
267 		return (string_get(value, systemg.name, -1));
268 	  case LEAF_sysLocation:
269 		return (string_get(value, systemg.location, -1));
270 	  case LEAF_sysServices:
271 		value->v.integer = systemg.services;
272 		break;
273 	  case LEAF_sysORLastChange:
274 		value->v.uint32 = systemg.or_last_change;
275 		break;
276 	}
277 	return (SNMP_ERR_NOERROR);
278 }
279 
280 /*************************************************************
281  *
282  * Debug group
283  */
284 int
285 op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub,
286     u_int iidx __unused, enum snmp_op op)
287 {
288 	asn_subid_t which = value->var.subs[sub - 1];
289 
290 	switch (op) {
291 
292 	  case SNMP_OP_GETNEXT:
293 		abort();
294 
295 	  case SNMP_OP_GET:
296 		switch (which) {
297 
298 		  case LEAF_begemotSnmpdDebugDumpPdus:
299 			value->v.integer = TRUTH_MK(debug.dump_pdus);
300 			break;
301 
302 		  case LEAF_begemotSnmpdDebugSnmpTrace:
303 			value->v.uint32 = snmp_trace;
304 			break;
305 
306 		  case LEAF_begemotSnmpdDebugSyslogPri:
307 			value->v.integer = debug.logpri;
308 			break;
309 		}
310 		return (SNMP_ERR_NOERROR);
311 
312 	  case SNMP_OP_SET:
313 		switch (which) {
314 
315 		  case LEAF_begemotSnmpdDebugDumpPdus:
316 			if (!TRUTH_OK(value->v.integer))
317 				return (SNMP_ERR_WRONG_VALUE);
318 			ctx->scratch->int1 = debug.dump_pdus;
319 			debug.dump_pdus = TRUTH_GET(value->v.integer);
320 			return (SNMP_ERR_NOERROR);
321 
322 		  case LEAF_begemotSnmpdDebugSnmpTrace:
323 			ctx->scratch->int1 = snmp_trace;
324 			snmp_trace = value->v.uint32;
325 			return (SNMP_ERR_NOERROR);
326 
327 		  case LEAF_begemotSnmpdDebugSyslogPri:
328 			if (value->v.integer < 0 || value->v.integer > 8)
329 				return (SNMP_ERR_WRONG_VALUE);
330 			ctx->scratch->int1 = debug.logpri;
331 			debug.logpri = (u_int)value->v.integer;
332 			return (SNMP_ERR_NOERROR);
333 		}
334 		return (SNMP_ERR_NO_CREATION);
335 
336 	  case SNMP_OP_ROLLBACK:
337 		switch (which) {
338 
339 		  case LEAF_begemotSnmpdDebugDumpPdus:
340 			debug.dump_pdus = ctx->scratch->int1;
341 			return (SNMP_ERR_NOERROR);
342 
343 		  case LEAF_begemotSnmpdDebugSnmpTrace:
344 			snmp_trace = ctx->scratch->int1;
345 			return (SNMP_ERR_NOERROR);
346 
347 		  case LEAF_begemotSnmpdDebugSyslogPri:
348 			debug.logpri = ctx->scratch->int1;
349 			return (SNMP_ERR_NOERROR);
350 		}
351 		abort();
352 
353 	  case SNMP_OP_COMMIT:
354 		switch (which) {
355 
356 		  case LEAF_begemotSnmpdDebugDumpPdus:
357 		  case LEAF_begemotSnmpdDebugSnmpTrace:
358 			return (SNMP_ERR_NOERROR);
359 
360 		  case LEAF_begemotSnmpdDebugSyslogPri:
361 			if (debug.logpri == 0)
362 				setlogmask(0);
363 			else
364 				setlogmask(LOG_UPTO(debug.logpri - 1));
365 			return (SNMP_ERR_NOERROR);
366 		}
367 		abort();
368 	}
369 	abort();
370 }
371 
372 /*************************************************************
373  *
374  * OR Table
375  */
376 int
377 op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value,
378     u_int sub, u_int iidx __unused, enum snmp_op op)
379 {
380 	struct objres *objres;
381 
382 	switch (op) {
383 
384 	  case SNMP_OP_GETNEXT:
385 		if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub))
386 		    == NULL)
387 			return (SNMP_ERR_NOSUCHNAME);
388 		value->var.subs[sub] = objres->index;
389 		value->var.len = sub + 1;
390 		break;
391 
392 	  case SNMP_OP_GET:
393 		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
394 		    == NULL)
395 			return (SNMP_ERR_NOSUCHNAME);
396 		break;
397 
398 	  case SNMP_OP_SET:
399 		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
400 		    == NULL)
401 			return (SNMP_ERR_NO_CREATION);
402 		return (SNMP_ERR_NOT_WRITEABLE);
403 
404 	  case SNMP_OP_ROLLBACK:
405 	  case SNMP_OP_COMMIT:
406 	  default:
407 		abort();
408 	}
409 
410 	/*
411 	 * Come here for GET, GETNEXT.
412 	 */
413 	switch (value->var.subs[sub - 1]) {
414 
415 	  case LEAF_sysORID:
416 		value->v.oid = objres->oid;
417 		break;
418 
419 	  case LEAF_sysORDescr:
420 		return (string_get(value, objres->descr, -1));
421 
422 	  case LEAF_sysORUpTime:
423 		value->v.uint32 = objres->uptime;
424 		break;
425 	}
426 	return (SNMP_ERR_NOERROR);
427 }
428 
429 /*************************************************************
430  *
431  * mib-2 snmp
432  */
433 int
434 op_snmp(struct snmp_context *ctx, struct snmp_value *value,
435     u_int sub, u_int iidx __unused, enum snmp_op op)
436 {
437 	switch (op) {
438 
439 	  case SNMP_OP_GETNEXT:
440 		abort();
441 
442 	  case SNMP_OP_GET:
443 		switch (value->var.subs[sub - 1]) {
444 
445 		  case LEAF_snmpInPkts:
446 			value->v.uint32 = snmpd_stats.inPkts;
447 			break;
448 
449 		  case LEAF_snmpInBadVersions:
450 			value->v.uint32 = snmpd_stats.inBadVersions;
451 			break;
452 
453 		  case LEAF_snmpInBadCommunityNames:
454 			value->v.uint32 = snmpd_stats.inBadCommunityNames;
455 			break;
456 
457 		  case LEAF_snmpInBadCommunityUses:
458 			value->v.uint32 = snmpd_stats.inBadCommunityUses;
459 			break;
460 
461 		  case LEAF_snmpInASNParseErrs:
462 			value->v.uint32 = snmpd_stats.inASNParseErrs;
463 			break;
464 
465 		  case LEAF_snmpEnableAuthenTraps:
466 			value->v.integer = TRUTH_MK(snmpd.auth_traps);
467 			break;
468 
469 		  case LEAF_snmpSilentDrops:
470 			value->v.uint32 = snmpd_stats.silentDrops;
471 			break;
472 
473 		  case LEAF_snmpProxyDrops:
474 			value->v.uint32 = snmpd_stats.proxyDrops;
475 			break;
476 
477 		  default:
478 			return (SNMP_ERR_NOSUCHNAME);
479 
480 		}
481 		return (SNMP_ERR_NOERROR);
482 
483 	  case SNMP_OP_SET:
484 		switch (value->var.subs[sub - 1]) {
485 		  case LEAF_snmpEnableAuthenTraps:
486 			if (!TRUTH_OK(value->v.integer))
487 				return (SNMP_ERR_WRONG_VALUE);
488 			ctx->scratch->int1 = value->v.integer;
489 			snmpd.auth_traps = TRUTH_GET(value->v.integer);
490 			return (SNMP_ERR_NOERROR);
491 		}
492 		abort();
493 
494 	  case SNMP_OP_ROLLBACK:
495 		switch (value->var.subs[sub - 1]) {
496 		  case LEAF_snmpEnableAuthenTraps:
497 			snmpd.auth_traps = ctx->scratch->int1;
498 			return (SNMP_ERR_NOERROR);
499 		}
500 		abort();
501 
502 	  case SNMP_OP_COMMIT:
503 		switch (value->var.subs[sub - 1]) {
504 		  case LEAF_snmpEnableAuthenTraps:
505 			return (SNMP_ERR_NOERROR);
506 		}
507 		abort();
508 	}
509 	abort();
510 }
511 
512 /*************************************************************
513  *
514  * SNMPd statistics group
515  */
516 int
517 op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
518     u_int sub, u_int iidx __unused, enum snmp_op op)
519 {
520 	switch (op) {
521 
522 	  case SNMP_OP_GET:
523 		switch (value->var.subs[sub - 1]) {
524 
525 		  case LEAF_begemotSnmpdStatsNoRxBufs:
526 			value->v.uint32 = snmpd_stats.noRxbuf;
527 			break;
528 
529 		  case LEAF_begemotSnmpdStatsNoTxBufs:
530 			value->v.uint32 = snmpd_stats.noTxbuf;
531 			break;
532 
533 		  case LEAF_begemotSnmpdStatsInTooLongPkts:
534 			value->v.uint32 = snmpd_stats.inTooLong;
535 			break;
536 
537 		  case LEAF_begemotSnmpdStatsInBadPduTypes:
538 			value->v.uint32 = snmpd_stats.inBadPduTypes;
539 			break;
540 
541 		  default:
542 			return (SNMP_ERR_NOSUCHNAME);
543 		}
544 		return (SNMP_ERR_NOERROR);
545 
546 	  case SNMP_OP_SET:
547 	  case SNMP_OP_ROLLBACK:
548 	  case SNMP_OP_COMMIT:
549 	  case SNMP_OP_GETNEXT:
550 		abort();
551 	}
552 	abort();
553 }
554 
555 /*
556  * SNMPd configuration scalars
557  */
558 int
559 op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value,
560     u_int sub, u_int iidx __unused, enum snmp_op op)
561 {
562 	asn_subid_t which = value->var.subs[sub - 1];
563 
564 	switch (op) {
565 
566 	  case SNMP_OP_GETNEXT:
567 		abort();
568 
569 	  case SNMP_OP_GET:
570 		switch (which) {
571 
572 		  case LEAF_begemotSnmpdTransmitBuffer:
573 			value->v.integer = snmpd.txbuf;
574 			break;
575 		  case LEAF_begemotSnmpdReceiveBuffer:
576 			value->v.integer = snmpd.rxbuf;
577 			break;
578 		  case LEAF_begemotSnmpdCommunityDisable:
579 			value->v.integer = TRUTH_MK(snmpd.comm_dis);
580 			break;
581 		  case LEAF_begemotSnmpdTrap1Addr:
582 			return (ip_get(value, snmpd.trap1addr));
583 		  case LEAF_begemotSnmpdVersionEnable:
584 			value->v.uint32 = snmpd.version_enable;
585 			break;
586 		  default:
587 			return (SNMP_ERR_NOSUCHNAME);
588 		}
589 		return (SNMP_ERR_NOERROR);
590 
591 	  case SNMP_OP_SET:
592 		switch (which) {
593 
594 		  case LEAF_begemotSnmpdTransmitBuffer:
595 			ctx->scratch->int1 = snmpd.txbuf;
596 			if (value->v.integer < 484 ||
597 			    value->v.integer > 65535)
598 				return (SNMP_ERR_WRONG_VALUE);
599 			snmpd.txbuf = value->v.integer;
600 			return (SNMP_ERR_NOERROR);
601 
602 		  case LEAF_begemotSnmpdReceiveBuffer:
603 			ctx->scratch->int1 = snmpd.rxbuf;
604 			if (value->v.integer < 484 ||
605 			    value->v.integer > 65535)
606 				return (SNMP_ERR_WRONG_VALUE);
607 			snmpd.rxbuf = value->v.integer;
608 			return (SNMP_ERR_NOERROR);
609 
610 		  case LEAF_begemotSnmpdCommunityDisable:
611 			ctx->scratch->int1 = snmpd.comm_dis;
612 			if (!TRUTH_OK(value->v.integer))
613 				return (SNMP_ERR_WRONG_VALUE);
614 			if (TRUTH_GET(value->v.integer)) {
615 				snmpd.comm_dis = 1;
616 			} else {
617 				if (snmpd.comm_dis)
618 					return (SNMP_ERR_WRONG_VALUE);
619 			}
620 			return (SNMP_ERR_NOERROR);
621 
622 		  case LEAF_begemotSnmpdTrap1Addr:
623 			return (ip_save(value, ctx, snmpd.trap1addr));
624 
625 		  case LEAF_begemotSnmpdVersionEnable:
626 			if (community != COMM_INITIALIZE)
627 				return (SNMP_ERR_NOT_WRITEABLE);
628 			ctx->scratch->int1 = snmpd.version_enable;
629 			if (value->v.uint32 == 0 ||
630 			    (value->v.uint32 & ~VERS_ENABLE_ALL))
631 				return (SNMP_ERR_WRONG_VALUE);
632 			snmpd.version_enable = value->v.uint32;
633 			return (SNMP_ERR_NOERROR);
634 		}
635 		abort();
636 
637 	  case SNMP_OP_ROLLBACK:
638 		switch (which) {
639 
640 		  case LEAF_begemotSnmpdTransmitBuffer:
641 			snmpd.rxbuf = ctx->scratch->int1;
642 			return (SNMP_ERR_NOERROR);
643 		  case LEAF_begemotSnmpdReceiveBuffer:
644 			snmpd.txbuf = ctx->scratch->int1;
645 			return (SNMP_ERR_NOERROR);
646 		  case LEAF_begemotSnmpdCommunityDisable:
647 			snmpd.comm_dis = ctx->scratch->int1;
648 			return (SNMP_ERR_NOERROR);
649 		  case LEAF_begemotSnmpdTrap1Addr:
650 			ip_rollback(ctx, snmpd.trap1addr);
651 			return (SNMP_ERR_NOERROR);
652 		  case LEAF_begemotSnmpdVersionEnable:
653 			snmpd.version_enable = ctx->scratch->int1;
654 			return (SNMP_ERR_NOERROR);
655 		}
656 		abort();
657 
658 	  case SNMP_OP_COMMIT:
659 		switch (which) {
660 
661 		  case LEAF_begemotSnmpdTransmitBuffer:
662 		  case LEAF_begemotSnmpdReceiveBuffer:
663 		  case LEAF_begemotSnmpdCommunityDisable:
664 			return (SNMP_ERR_NOERROR);
665 		  case LEAF_begemotSnmpdTrap1Addr:
666 			ip_commit(ctx);
667 			return (SNMP_ERR_NOERROR);
668 		  case LEAF_begemotSnmpdVersionEnable:
669 			return (SNMP_ERR_NOERROR);
670 		}
671 		abort();
672 	}
673 	abort();
674 }
675 
676 /*
677  * The community table
678  */
679 int
680 op_community(struct snmp_context *ctx, struct snmp_value *value,
681     u_int sub, u_int iidx __unused, enum snmp_op op)
682 {
683 	asn_subid_t which = value->var.subs[sub - 1];
684 	struct community *c;
685 
686 	switch (op) {
687 
688 	  case SNMP_OP_GETNEXT:
689 		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
690 		    (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
691 			return (SNMP_ERR_NOSUCHNAME);
692 		index_append(&value->var, sub, &c->index);
693 		break;
694 
695 	  case SNMP_OP_GET:
696 		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
697 		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
698 			return (SNMP_ERR_NOSUCHNAME);
699 		break;
700 
701 	  case SNMP_OP_SET:
702 		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
703 		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
704 			return (SNMP_ERR_NO_CREATION);
705 		if (which != LEAF_begemotSnmpdCommunityString)
706 			return (SNMP_ERR_NOT_WRITEABLE);
707 		return (string_save(value, ctx, -1, &c->string));
708 
709 	  case SNMP_OP_ROLLBACK:
710 		if (which == LEAF_begemotSnmpdCommunityString) {
711 			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
712 			    sub)) == NULL)
713 				string_free(ctx);
714 			else
715 				string_rollback(ctx, &c->string);
716 			return (SNMP_ERR_NOERROR);
717 		}
718 		abort();
719 
720 	  case SNMP_OP_COMMIT:
721 		if (which == LEAF_begemotSnmpdCommunityString) {
722 			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
723 			    sub)) == NULL)
724 				string_free(ctx);
725 			else
726 				string_commit(ctx);
727 			return (SNMP_ERR_NOERROR);
728 		}
729 		abort();
730 
731 	  default:
732 		abort();
733 	}
734 
735 	switch (which) {
736 
737 	  case LEAF_begemotSnmpdCommunityString:
738 		return (string_get(value, c->string, -1));
739 
740 	  case LEAF_begemotSnmpdCommunityDescr:
741 		return (string_get(value, c->descr, -1));
742 	}
743 	abort();
744 }
745 
746 /*
747  * Module table.
748  */
749 struct module_dep {
750 	struct snmp_dependency dep;
751 	u_char	section[LM_SECTION_MAX + 1];
752 	u_char	*path;
753 	struct lmodule *m;
754 };
755 
756 static int
757 dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep,
758     enum snmp_depop op)
759 {
760 	struct module_dep *mdep = (struct module_dep *)(void *)dep;
761 
762 	switch (op) {
763 
764 	  case SNMP_DEPOP_COMMIT:
765 		if (mdep->path == NULL) {
766 			/* unload - find the module */
767 			TAILQ_FOREACH(mdep->m, &lmodules, link)
768 				if (strcmp(mdep->m->section,
769 				    mdep->section) == 0)
770 					break;
771 			if (mdep->m == NULL)
772 				/* no such module - that's ok */
773 				return (SNMP_ERR_NOERROR);
774 
775 			/* handle unloading in the finalizer */
776 			return (SNMP_ERR_NOERROR);
777 		}
778 		/* load */
779 		if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) {
780 			/* could not load */
781 			return (SNMP_ERR_RES_UNAVAIL);
782 		}
783 		/* start in finalizer */
784 		return (SNMP_ERR_NOERROR);
785 
786 	  case SNMP_DEPOP_ROLLBACK:
787 		if (mdep->path == NULL) {
788 			/* rollback unload - the finalizer takes care */
789 			return (SNMP_ERR_NOERROR);
790 		}
791 		/* rollback load */
792 		lm_unload(mdep->m);
793 		return (SNMP_ERR_NOERROR);
794 
795 	  case SNMP_DEPOP_FINISH:
796 		if (mdep->path == NULL) {
797 			if (mdep->m != NULL && ctx->code == SNMP_RET_OK)
798 				lm_unload(mdep->m);
799 		} else {
800 			if (mdep->m != NULL && ctx->code == SNMP_RET_OK &&
801 			    community != COMM_INITIALIZE)
802 				lm_start(mdep->m);
803 			free(mdep->path);
804 		}
805 		return (SNMP_ERR_NOERROR);
806 	}
807 	abort();
808 }
809 
810 int
811 op_modules(struct snmp_context *ctx, struct snmp_value *value,
812     u_int sub, u_int iidx, enum snmp_op op)
813 {
814 	asn_subid_t which = value->var.subs[sub - 1];
815 	struct lmodule *m;
816 	u_char *section, *ptr;
817 	size_t seclen;
818 	struct module_dep *mdep;
819 	struct asn_oid idx;
820 
821 	switch (op) {
822 
823 	  case SNMP_OP_GETNEXT:
824 		if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
825 			return (SNMP_ERR_NOSUCHNAME);
826 		index_append(&value->var, sub, &m->index);
827 		break;
828 
829 	  case SNMP_OP_GET:
830 		if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
831 			return (SNMP_ERR_NOSUCHNAME);
832 		break;
833 
834 	  case SNMP_OP_SET:
835 		m = FIND_OBJECT_OID(&lmodules, &value->var, sub);
836 		if (which != LEAF_begemotSnmpdModulePath) {
837 			if (m == NULL)
838 				return (SNMP_ERR_NO_CREATION);
839 			return (SNMP_ERR_NOT_WRITEABLE);
840 		}
841 
842 		/* the errors in the next few statements can only happen when
843 		 * m is NULL, hence the NO_CREATION error. */
844 		if (index_decode(&value->var, sub, iidx,
845 		    &section, &seclen))
846 			return (SNMP_ERR_NO_CREATION);
847 
848 		/* check the section name */
849 		if (seclen > LM_SECTION_MAX || seclen == 0) {
850 			free(section);
851 			return (SNMP_ERR_NO_CREATION);
852 		}
853 		for (ptr = section; ptr < section + seclen; ptr++)
854 			if (!isascii(*ptr) || !isalnum(*ptr)) {
855 				free(section);
856 				return (SNMP_ERR_NO_CREATION);
857 			}
858 		if (!isalpha(section[0])) {
859 			free(section);
860 			return (SNMP_ERR_NO_CREATION);
861 		}
862 
863 		/* check the path */
864 		for (ptr = value->v.octetstring.octets;
865 		     ptr < value->v.octetstring.octets + value->v.octetstring.len;
866 		     ptr++) {
867 			if (*ptr == '\0') {
868 				free(section);
869 				return (SNMP_ERR_WRONG_VALUE);
870 			}
871 		}
872 
873 		if (m == NULL) {
874 			if (value->v.octetstring.len == 0) {
875 				free(section);
876 				return (SNMP_ERR_INCONS_VALUE);
877 			}
878 		} else {
879 			if (value->v.octetstring.len != 0) {
880 				free(section);
881 				return (SNMP_ERR_INCONS_VALUE);
882 			}
883 		}
884 
885 		asn_slice_oid(&idx, &value->var, sub, value->var.len);
886 
887 		/* so far, so good */
888 		mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx,
889 		    &oid_begemotSnmpdModuleTable, &idx,
890 		    sizeof(*mdep), dep_modules);
891 		if (mdep == NULL) {
892 			free(section);
893 			return (SNMP_ERR_RES_UNAVAIL);
894 		}
895 
896 		if (mdep->section[0] != '\0') {
897 			/* two writes to the same entry - bad */
898 			free(section);
899 			return (SNMP_ERR_INCONS_VALUE);
900 		}
901 
902 		strncpy(mdep->section, section, seclen);
903 		mdep->section[seclen] = '\0';
904 		free(section);
905 
906 		if (value->v.octetstring.len == 0)
907 			mdep->path = NULL;
908 		else {
909 			if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL)
910 				return (SNMP_ERR_RES_UNAVAIL);
911 			strncpy(mdep->path, value->v.octetstring.octets,
912 			    value->v.octetstring.len);
913 			mdep->path[value->v.octetstring.len] = '\0';
914 		}
915 		ctx->scratch->ptr1 = mdep;
916 		return (SNMP_ERR_NOERROR);
917 
918 	  case SNMP_OP_ROLLBACK:
919 	  case SNMP_OP_COMMIT:
920 		return (SNMP_ERR_NOERROR);
921 
922 	  default:
923 		abort();
924 	}
925 
926 	switch (which) {
927 
928 	  case LEAF_begemotSnmpdModulePath:
929 		return (string_get(value, m->path, -1));
930 
931 	  case LEAF_begemotSnmpdModuleComment:
932 		return (string_get(value, m->config->comment, -1));
933 	}
934 	abort();
935 }
936 
937 int
938 op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
939     u_int sub, u_int iidx __unused, enum snmp_op op)
940 {
941 	switch (op) {
942 
943 	  case SNMP_OP_GETNEXT:
944 		abort();
945 
946 	  case SNMP_OP_GET:
947 		switch (value->var.subs[sub - 1]) {
948 
949 		  case LEAF_snmpSetSerialNo:
950 			value->v.integer = snmp_serial_no;
951 			break;
952 
953 		  default:
954 			abort();
955 		}
956 		return (SNMP_ERR_NOERROR);
957 
958 	  case SNMP_OP_SET:
959 		switch (value->var.subs[sub - 1]) {
960 
961 		  case LEAF_snmpSetSerialNo:
962 			if (value->v.integer != snmp_serial_no)
963 				return (SNMP_ERR_INCONS_VALUE);
964 			break;
965 
966 		  default:
967 			abort();
968 		}
969 		return (SNMP_ERR_NOERROR);
970 
971 	  case SNMP_OP_ROLLBACK:
972 		return (SNMP_ERR_NOERROR);
973 
974 	  case SNMP_OP_COMMIT:
975 		if (snmp_serial_no++ == 2147483647)
976 			snmp_serial_no = 0;
977 		return (SNMP_ERR_NOERROR);
978 	}
979 	abort();
980 }
981 
982 /*
983  * Transport table
984  */
985 int
986 op_transport_table(struct snmp_context *ctx __unused, struct snmp_value *value,
987     u_int sub, u_int iidx, enum snmp_op op)
988 {
989 	asn_subid_t which = value->var.subs[sub - 1];
990 	struct transport *t;
991 	u_char *tname, *ptr;
992 	size_t tnamelen;
993 
994 	switch (op) {
995 
996 	  case SNMP_OP_GETNEXT:
997 		if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub))
998 		    == NULL)
999 			return (SNMP_ERR_NOSUCHNAME);
1000 		index_append(&value->var, sub, &t->index);
1001 		break;
1002 
1003 	  case SNMP_OP_GET:
1004 		if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub))
1005 		    == NULL)
1006 			return (SNMP_ERR_NOSUCHNAME);
1007 		break;
1008 
1009 	  case SNMP_OP_SET:
1010 		t = FIND_OBJECT_OID(&transport_list, &value->var, sub);
1011 		if (which != LEAF_begemotSnmpdTransportStatus) {
1012 			if (t == NULL)
1013 				return (SNMP_ERR_NO_CREATION);
1014 			return (SNMP_ERR_NOT_WRITEABLE);
1015 		}
1016 
1017 		/* the errors in the next few statements can only happen when
1018 		 * t is NULL, hence the NO_CREATION error. */
1019 		if (index_decode(&value->var, sub, iidx,
1020 		    &tname, &tnamelen))
1021 			return (SNMP_ERR_NO_CREATION);
1022 
1023 		/* check the section name */
1024 		if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) {
1025 			free(tname);
1026 			return (SNMP_ERR_NO_CREATION);
1027 		}
1028 		for (ptr = tname; ptr < tname + tnamelen; ptr++) {
1029 			if (!isascii(*ptr) || !isalnum(*ptr)) {
1030 				free(tname);
1031 				return (SNMP_ERR_NO_CREATION);
1032 			}
1033 		}
1034 
1035 		/* for now */
1036 		return (SNMP_ERR_NOT_WRITEABLE);
1037 
1038 	  case SNMP_OP_ROLLBACK:
1039 	  case SNMP_OP_COMMIT:
1040 		return (SNMP_ERR_NOERROR);
1041 	  default:
1042 		abort();
1043 	}
1044 
1045 	switch (which) {
1046 
1047 	    case LEAF_begemotSnmpdTransportStatus:
1048 		value->v.integer = 1;
1049 		break;
1050 
1051 	    case LEAF_begemotSnmpdTransportOid:
1052 		memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id));
1053 		break;
1054 	}
1055 	return (SNMP_ERR_NOERROR);
1056 }
1057