xref: /openbsd/usr.sbin/snmpd/application.c (revision 9ea232b5)
1 /*	$OpenBSD: application.c,v 1.41 2023/12/21 12:43:30 martijn Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/time.h>
21 #include <sys/tree.h>
22 #include <sys/types.h>
23 
24 #include <assert.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <inttypes.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 #include "application.h"
33 #include "log.h"
34 #include "smi.h"
35 #include "snmp.h"
36 #include "snmpe.h"
37 #include "mib.h"
38 
39 #define OID(...)		(struct ber_oid){ { __VA_ARGS__ },	\
40     (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) }
41 
42 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts);
43 
44 struct appl_agentcap {
45 	struct appl_backend *aa_backend;
46 	struct appl_context *aa_context;
47 	uint32_t aa_index;
48 	struct ber_oid aa_oid;
49 	char aa_descr[256];
50 	int aa_uptime;
51 
52 	TAILQ_ENTRY(appl_agentcap) aa_entry;
53 };
54 
55 struct appl_context {
56 	char ac_name[APPL_CONTEXTNAME_MAX + 1];
57 
58 	RB_HEAD(appl_regions, appl_region) ac_regions;
59 	TAILQ_HEAD(, appl_agentcap) ac_agentcaps;
60 	int ac_agentcap_lastid;
61 	int ac_agentcap_lastchange;
62 
63 	TAILQ_ENTRY(appl_context) ac_entries;
64 };
65 
66 struct appl_region {
67 	struct ber_oid ar_oid;
68 	uint8_t ar_priority;
69 	int32_t ar_timeout;
70 	int ar_instance;
71 	int ar_subtree; /* Claim entire subtree */
72 	struct appl_backend *ar_backend;
73 	struct appl_region *ar_next; /* Sorted by priority */
74 
75 	RB_ENTRY(appl_region) ar_entry;
76 };
77 
78 struct appl_request_upstream {
79 	struct appl_context *aru_ctx;
80 	struct snmp_message *aru_statereference;
81 	enum snmp_pdutype aru_requesttype;
82 	enum snmp_pdutype aru_responsetype;
83 	int32_t aru_requestid; /* upstream requestid */
84 	int32_t aru_transactionid; /* RFC 2741 section 6.1 */
85 	uint16_t aru_nonrepeaters;
86 	uint16_t aru_maxrepetitions;
87 	struct appl_varbind_internal *aru_vblist;
88 	size_t aru_varbindlen;
89 	enum appl_error aru_error;
90 	int16_t aru_index;
91 	int aru_locked; /* Prevent recursion through appl_request_send */
92 
93 	enum snmp_version aru_pduversion;
94 };
95 
96 struct appl_request_downstream {
97 	struct appl_request_upstream *ard_request;
98 	struct appl_backend *ard_backend;
99 	enum snmp_pdutype ard_requesttype;
100 	uint16_t ard_nonrepeaters;
101 	uint16_t ard_maxrepetitions;
102 	int32_t ard_requestid;
103 	uint8_t ard_retries;
104 
105 	struct appl_varbind_internal *ard_vblist;
106 	struct event ard_timer;
107 
108 	RB_ENTRY(appl_request_downstream) ard_entry;
109 };
110 
111 enum appl_varbind_state {
112 	APPL_VBSTATE_MUSTFILL,
113 	APPL_VBSTATE_NEW,
114 	APPL_VBSTATE_PENDING,
115 	APPL_VBSTATE_DONE
116 };
117 
118 struct appl_varbind_internal {
119 	enum appl_varbind_state avi_state;
120 	struct appl_varbind avi_varbind;
121 	struct appl_region *avi_region;
122 	struct ber_oid avi_origid;
123 	int16_t avi_index;
124 	struct appl_request_upstream *avi_request_upstream;
125 	struct appl_request_downstream *avi_request_downstream;
126 	struct appl_varbind_internal *avi_next;
127 	struct appl_varbind_internal *avi_sub;
128 };
129 
130 /* SNMP-TARGET-MIB (RFC 3413) */
131 struct snmp_target_mib {
132 	uint32_t		snmp_unavailablecontexts;
133 	uint32_t		snmp_unknowncontexts;
134 } snmp_target_mib;
135 
136 void appl_agentcap_free(struct appl_agentcap *);
137 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t,
138     struct ber_oid *, int, int, struct appl_backend *);
139 void appl_region_free(struct appl_context *, struct appl_region *);
140 enum appl_error appl_region_unregister_match(struct appl_context *, uint8_t,
141     struct ber_oid *, char *, struct appl_backend *, int);
142 struct appl_region *appl_region_find(struct appl_context *,
143     const struct ber_oid *);
144 struct appl_region *appl_region_next(struct appl_context *,
145     struct ber_oid *, struct appl_region *);
146 void appl_request_upstream_free(struct appl_request_upstream *);
147 void appl_request_downstream_free(struct appl_request_downstream *);
148 void appl_request_upstream_resolve(struct appl_request_upstream *);
149 void appl_request_downstream_send(struct appl_request_downstream *);
150 void appl_request_downstream_timeout(int, short, void *);
151 void appl_request_upstream_reply(struct appl_request_upstream *);
152 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *,
153     int, int, int, const char **);
154 int appl_error_valid(enum appl_error, enum snmp_pdutype);
155 int appl_varbind_backend(struct appl_varbind_internal *);
156 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
157 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t,
158     uint16_t, struct appl_varbind *);
159 void ober_oid_nextsibling(struct ber_oid *);
160 
161 int appl_region_cmp(struct appl_region *, struct appl_region *);
162 int appl_request_cmp(struct appl_request_downstream *,
163     struct appl_request_downstream *);
164 
165 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
166 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry,
167     appl_request_cmp);
168 
169 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name)
170 
171 void
172 appl(void)
173 {
174 	appl_agentx();
175 }
176 
177 void
178 appl_init(void)
179 {
180 	appl_blocklist_init();
181 	appl_internal_init();
182 	appl_agentx_init();
183 }
184 
185 void
186 appl_shutdown(void)
187 {
188 	struct appl_context *ctx, *tctx;
189 
190 	appl_blocklist_shutdown();
191 	appl_internal_shutdown();
192 	appl_agentx_shutdown();
193 
194 	TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
195 		assert(RB_EMPTY(&(ctx->ac_regions)));
196 		assert(TAILQ_EMPTY(&(ctx->ac_agentcaps)));
197 		TAILQ_REMOVE(&contexts, ctx, ac_entries);
198 		free(ctx);
199 	}
200 }
201 
202 struct appl_context *
203 appl_context(const char *name, int create)
204 {
205 	struct appl_context *ctx;
206 
207 	if (name == NULL)
208 		name = "";
209 
210 	if (strlen(name) > APPL_CONTEXTNAME_MAX) {
211 		errno = EINVAL;
212 		return NULL;
213 	}
214 
215 	TAILQ_FOREACH(ctx, &contexts, ac_entries) {
216 		if (strcmp(name, ctx->ac_name) == 0)
217 			return ctx;
218 	}
219 
220 	/* Always allow the default namespace */
221 	if (!create && name[0] != '\0') {
222 		errno = ENOENT;
223 		return NULL;
224 	}
225 
226 	if ((ctx = malloc(sizeof(*ctx))) == NULL)
227 		return NULL;
228 
229 	strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name));
230 	RB_INIT(&(ctx->ac_regions));
231 	TAILQ_INIT(&(ctx->ac_agentcaps));
232 	ctx->ac_agentcap_lastid = 0;
233 	ctx->ac_agentcap_lastchange = 0;
234 
235 	TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries);
236 	return ctx;
237 }
238 
239 /* Name from RFC 2741 section 6.2.14 */
240 enum appl_error
241 appl_addagentcaps(const char *ctxname, struct ber_oid *oid, const char *descr,
242     struct appl_backend *backend)
243 {
244 	struct appl_context *ctx;
245 	struct appl_agentcap *cap;
246 	char oidbuf[1024];
247 
248 	if (ctxname == NULL)
249 		ctxname = "";
250 
251 	(void)smi_oid2string(oid, oidbuf, sizeof(oidbuf), 0);
252 	log_info("%s: Adding agent capabilities %s context(%s)",
253 		backend->ab_name, oidbuf, ctxname);
254 
255 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
256 		log_info("%s: Can't add agent capabilities %s: "
257 		    "Unsupported context \"%s\"", backend->ab_name, oidbuf,
258 		    ctxname);
259 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
260 	}
261 
262 	if ((cap = malloc(sizeof(*cap))) == NULL) {
263 		log_warn("%s: Can't add agent capabilities %s",
264 		    backend->ab_name, oidbuf);
265 		return APPL_ERROR_PROCESSINGERROR;
266 	}
267 
268 	cap->aa_backend = backend;
269 	cap->aa_context = ctx;
270 	cap->aa_index = ++ctx->ac_agentcap_lastid;
271 	cap->aa_oid = *oid;
272 	cap->aa_uptime = smi_getticks();
273 	if (strlcpy(cap->aa_descr, descr,
274 	    sizeof(cap->aa_descr)) >= sizeof(cap->aa_descr)) {
275 		log_info("%s: Can't add agent capabilities %s: "
276 		    "Invalid description", backend->ab_name, oidbuf);
277 		free(cap);
278 		return APPL_ERROR_PARSEERROR;
279 	}
280 
281 	TAILQ_INSERT_TAIL(&(ctx->ac_agentcaps), cap, aa_entry);
282 	ctx->ac_agentcap_lastchange = cap->aa_uptime;
283 
284 	return APPL_ERROR_NOERROR;
285 }
286 
287 /* Name from RFC2741 section 6.2.15 */
288 enum appl_error
289 appl_removeagentcaps(const char *ctxname, struct ber_oid *oid,
290     struct appl_backend *backend)
291 {
292 	struct appl_context *ctx;
293 	struct appl_agentcap *cap, *tmp;
294 	char oidbuf[1024];
295 	int found = 0;
296 
297 	if (ctxname == NULL)
298 		ctxname = "";
299 
300 	(void)smi_oid2string(oid, oidbuf, sizeof(oidbuf), 0);
301 	log_info("%s: Removing agent capabilities %s context(%s)",
302 	    backend->ab_name, oidbuf, ctxname);
303 
304 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
305 		log_info("%s: Can't remove agent capabilities %s: "
306 		    "Unsupported context \"%s\"", backend->ab_name, oidbuf,
307 		    ctxname);
308 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
309 	}
310 
311 	TAILQ_FOREACH_SAFE(cap, &(ctx->ac_agentcaps), aa_entry, tmp) {
312 		/* No duplicate oid check, just continue */
313 		if (cap->aa_backend != backend ||
314 		    ober_oid_cmp(oid, &(cap->aa_oid)) != 0)
315 			continue;
316 		found = 1;
317 		appl_agentcap_free(cap);
318 	}
319 
320 	if (found)
321 		return APPL_ERROR_NOERROR;
322 
323 	log_info("%s: Can't remove agent capabilities %s: not found",
324 	    backend->ab_name, oidbuf);
325 	return APPL_ERROR_UNKNOWNAGENTCAPS;
326 }
327 
328 void
329 appl_agentcap_free(struct appl_agentcap *cap)
330 {
331 	TAILQ_REMOVE(&(cap->aa_context->ac_agentcaps), cap, aa_entry);
332 	cap->aa_context->ac_agentcap_lastchange = smi_getticks();
333 	free(cap);
334 }
335 
336 struct ber_element *
337 appl_sysorlastchange(struct ber_oid *oid)
338 {
339 	struct appl_context *ctx;
340 	struct ber_element *value;
341 
342 	ctx = appl_context(NULL, 0);
343 	value = ober_add_integer(NULL, ctx->ac_agentcap_lastchange);
344 	if (value != NULL)
345 		ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
346 	else
347 		log_warn("ober_add_integer");
348 
349 	return value;
350 }
351 
352 #define SYSORIDX_POS 10
353 struct ber_element *
354 appl_sysortable(struct ber_oid *oid)
355 {
356 	struct appl_context *ctx;
357 	struct appl_agentcap *cap;
358 	struct ber_element *value = NULL;
359 
360 	if (oid->bo_n != SYSORIDX_POS + 1)
361 		goto notfound;
362 
363 	ctx = appl_context(NULL, 0);
364 	TAILQ_FOREACH(cap, &(ctx->ac_agentcaps), aa_entry) {
365 		if (cap->aa_index == oid->bo_id[SYSORIDX_POS])
366 			break;
367 	}
368 	if (cap == NULL)
369 		goto notfound;
370 
371 	if (ober_oid_cmp(&OID(MIB_sysORID), oid) == -2)
372 		value = ober_add_oid(NULL, &(cap->aa_oid));
373 	else if (ober_oid_cmp(&OID(MIB_sysORDescr), oid) == -2)
374 		value = ober_add_string(NULL, cap->aa_descr);
375 	else if (ober_oid_cmp(&OID(MIB_sysORUpTime), oid) == -2) {
376 		if ((value = ober_add_integer(NULL, cap->aa_uptime)) != NULL)
377 			ober_set_header(value,
378 			    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
379 	}
380 	if (value == NULL)
381 		log_warn("ober_add_*");
382 	return value;
383 
384  notfound:
385 	if ((value = appl_exception(APPL_EXC_NOSUCHINSTANCE)) == NULL)
386 		log_warn("appl_exception");
387 	return value;
388 }
389 
390 struct ber_element *
391 appl_sysortable_getnext(int8_t include, struct ber_oid *oid)
392 {
393 	struct appl_context *ctx;
394 	struct appl_agentcap *cap;
395 	struct ber_element *value = NULL;
396 
397 	if (oid->bo_n < SYSORIDX_POS + 1) {
398 		include = 1;
399 		oid->bo_id[SYSORIDX_POS] = 0;
400 	} else if (oid->bo_n < SYSORIDX_POS + 1)
401 		include = 0;
402 
403 	ctx = appl_context(NULL, 0);
404 	TAILQ_FOREACH(cap, &(ctx->ac_agentcaps), aa_entry) {
405 		if (cap->aa_index > oid->bo_id[SYSORIDX_POS])
406 			break;
407 		if (cap->aa_index == oid->bo_id[SYSORIDX_POS] && include)
408 			break;
409 	}
410 	if (cap == NULL) {
411 		value = appl_exception(APPL_EXC_NOSUCHINSTANCE);
412 		goto done;
413 	}
414 
415 	oid->bo_id[SYSORIDX_POS] = cap->aa_index;
416 	oid->bo_n = SYSORIDX_POS + 1;
417 
418 	if (ober_oid_cmp(&OID(MIB_sysORID), oid) == -2)
419 		value = ober_add_oid(NULL, &(cap->aa_oid));
420 	else if (ober_oid_cmp(&OID(MIB_sysORDescr), oid) == -2)
421 		value = ober_add_string(NULL, cap->aa_descr);
422 	else if (ober_oid_cmp(&OID(MIB_sysORUpTime), oid) == -2) {
423 		if ((value = ober_add_integer(NULL, cap->aa_uptime)) != NULL)
424 			ober_set_header(value,
425 			    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
426 	}
427  done:
428 	if (value == NULL)
429 		log_warn("ober_add_*");
430 	return value;
431 }
432 
433 struct ber_element *
434 appl_targetmib(struct ber_oid *oid)
435 {
436 	struct ber_element *value = NULL;
437 
438 	if (ober_oid_cmp(oid, &OID(MIB_snmpUnavailableContexts, 0)) == 0)
439 		value = ober_add_integer(NULL,
440 		    snmp_target_mib.snmp_unavailablecontexts);
441 	else if (ober_oid_cmp(oid, &OID(MIB_snmpUnknownContexts, 0)) == 0)
442 		value = ober_add_integer(NULL,
443 		    snmp_target_mib.snmp_unknowncontexts);
444 
445 	if (value != NULL)
446 		ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
447 	return value;
448 }
449 
450 enum appl_error
451 appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority,
452     struct ber_oid *oid, int instance, int subtree,
453     struct appl_backend *backend)
454 {
455 	struct appl_region *region = NULL, *nregion;
456 	char oidbuf[1024], regionbuf[1024], subidbuf[11];
457 	size_t i;
458 
459 	/* Don't use smi_oid2string, because appl_register can't use it */
460 	oidbuf[0] = '\0';
461 	for (i = 0; i < oid->bo_n; i++) {
462 		if (i != 0)
463 			strlcat(oidbuf, ".", sizeof(oidbuf));
464 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
465 		    oid->bo_id[i]);
466 		strlcat(oidbuf, subidbuf, sizeof(oidbuf));
467 	}
468 
469 	/*
470 	 * Don't allow overlap when subtree flag is set.
471 	 * This allows us to keep control of certain regions like system.
472 	 */
473 	region = appl_region_find(ctx, oid);
474 	if (region != NULL && region->ar_subtree &&
475 	    region->ar_backend != backend)
476 		goto overlap;
477 
478 	if ((nregion = malloc(sizeof(*nregion))) == NULL) {
479 		log_warn("%s: Can't register %s: Processing error",
480 		    backend->ab_name, oidbuf);
481 		return APPL_ERROR_PROCESSINGERROR;
482 	}
483 	nregion->ar_oid = *oid;
484 	nregion->ar_priority = priority;
485 	nregion->ar_timeout = timeout;
486 	nregion->ar_instance = instance;
487 	nregion->ar_subtree = subtree;
488 	nregion->ar_backend = backend;
489 	nregion->ar_next = NULL;
490 
491 	region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
492 	if (region == NULL)
493 		return APPL_ERROR_NOERROR;
494 
495 	if (region->ar_priority == priority)
496 		goto duplicate;
497 	if (region->ar_priority > priority) {
498 		RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
499 		RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
500 		nregion->ar_next = region;
501 		return APPL_ERROR_NOERROR;
502 	}
503 
504 	while (region->ar_next != NULL &&
505 	    region->ar_next->ar_priority < priority)
506 		region = region->ar_next;
507 	if (region->ar_next != NULL && region->ar_next->ar_priority == priority)
508 		goto duplicate;
509 	nregion->ar_next = region->ar_next;
510 	region->ar_next = nregion;
511 
512 	return APPL_ERROR_NOERROR;
513  duplicate:
514 	free(nregion);
515 	log_info("%s: %s priority %"PRId8": Duplicate registration",
516 	    backend->ab_name, oidbuf, priority);
517 	return APPL_ERROR_DUPLICATEREGISTRATION;
518  overlap:
519 	regionbuf[0] = '\0';
520 	for (i = 0; i < region->ar_oid.bo_n; i++) {
521 		if (i != 0)
522 			strlcat(regionbuf, ".", sizeof(regionbuf));
523 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
524 		    region->ar_oid.bo_id[i]);
525 		strlcat(regionbuf, subidbuf, sizeof(regionbuf));
526 	}
527 	log_info("%s: %s overlaps with %s: Request denied",
528 	    backend->ab_name, oidbuf, regionbuf);
529 	return APPL_ERROR_REQUESTDENIED;
530 }
531 
532 /* Name from RFC 2741 section 6.2.3 */
533 enum appl_error
534 appl_register(const char *ctxname, uint32_t timeout, uint8_t priority,
535     struct ber_oid *oid, int instance, int subtree, uint8_t range_subid,
536     uint32_t upper_bound, struct appl_backend *backend)
537 {
538 	struct appl_context *ctx;
539 	struct appl_region *region, search;
540 	char oidbuf[1024], subidbuf[11];
541 	enum appl_error error;
542 	size_t i;
543 	uint32_t lower_bound;
544 
545 	oidbuf[0] = '\0';
546 	/* smi_oid2string can't do ranges */
547 	for (i = 0; i < oid->bo_n; i++) {
548 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
549 		if (i != 0)
550 			strlcat(oidbuf, ".", sizeof(oidbuf));
551 		if (range_subid == i + 1) {
552 			strlcat(oidbuf, "[", sizeof(oidbuf));
553 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
554 			strlcat(oidbuf, "-", sizeof(oidbuf));
555 			snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
556 			    upper_bound);
557 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
558 			strlcat(oidbuf, "]", sizeof(oidbuf));
559 		} else
560 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
561 	}
562 
563 	if (ctxname == NULL)
564 		ctxname = "";
565 	log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") "
566 	    "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf,
567 	     instance ? "(instance)" : "", ctxname, priority,
568 	     timeout/100, timeout % 100);
569 
570 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
571 		if (errno == ENOMEM) {
572 			log_warn("%s: Can't register %s: Processing error",
573 			    backend->ab_name, oidbuf);
574 			return APPL_ERROR_PROCESSINGERROR;
575 		}
576 		log_info("%s: Can't register %s: Unsupported context \"%s\"",
577 		    backend->ab_name, oidbuf, ctxname);
578 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
579 	}
580 	/* Default timeouts should be handled by backend */
581 	if (timeout == 0)
582 		fatalx("%s: Timeout can't be 0", __func__);
583 	if (priority == 0) {
584 		log_warnx("%s: Can't register %s: priority can't be 0",
585 		    backend->ab_name, oidbuf);
586 		return APPL_ERROR_PARSEERROR;
587 	}
588 
589 	if (range_subid == 0)
590 		return appl_region(ctx, timeout, priority, oid, instance,
591 		    subtree, backend);
592 
593 	range_subid--;
594 	if (range_subid >= oid->bo_n) {
595 		log_warnx("%s: Can't register %s: range_subid too large",
596 		    backend->ab_name, oidbuf);
597 		return APPL_ERROR_PARSEERROR;
598 	}
599 	if (oid->bo_id[range_subid] > upper_bound) {
600 		log_warnx("%s: Can't register %s: upper bound smaller than "
601 		    "range_subid", backend->ab_name, oidbuf);
602 		return APPL_ERROR_PARSEERROR;
603 	}
604 
605 	lower_bound = oid->bo_id[range_subid];
606 	do {
607 		if ((error = appl_region(ctx, timeout, priority, oid, instance,
608 		    subtree, backend)) != APPL_ERROR_NOERROR)
609 			goto fail;
610 	} while (oid->bo_id[range_subid]++ != upper_bound);
611 	if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree,
612 	    backend)) != APPL_ERROR_NOERROR)
613 		goto fail;
614 
615 	return APPL_ERROR_NOERROR;
616  fail:
617 	search.ar_oid = *oid;
618 	if (search.ar_oid.bo_id[range_subid] == lower_bound)
619 		return error;
620 
621 	for (search.ar_oid.bo_id[range_subid]--;
622 	    search.ar_oid.bo_id[range_subid] != lower_bound;
623 	    search.ar_oid.bo_id[range_subid]--) {
624 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
625 		while (region->ar_priority != priority)
626 			region = region->ar_next;
627 		appl_region_free(ctx, region);
628 	}
629 	region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
630 	while (region->ar_priority != priority)
631 		region = region->ar_next;
632 	appl_region_free(ctx, region);
633 	return error;
634 }
635 
636 /* Name from RFC 2741 section 6.2.4 */
637 enum appl_error
638 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid,
639     uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend)
640 {
641 	struct appl_context *ctx;
642 	char oidbuf[1024], subidbuf[11];
643 	enum appl_error error;
644 	uint32_t lower_bound;
645 	size_t i;
646 
647 	oidbuf[0] = '\0';
648 	for (i = 0; i < oid->bo_n; i++) {
649 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
650 		if (i != 0)
651 			strlcat(oidbuf, ".", sizeof(oidbuf));
652 		if (range_subid == i + 1) {
653 			strlcat(oidbuf, "[", sizeof(oidbuf));
654 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
655 			strlcat(oidbuf, "-", sizeof(oidbuf));
656 			snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
657 			    upper_bound);
658 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
659 			strlcat(oidbuf, "]", sizeof(oidbuf));
660 		} else
661 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
662 	}
663 
664 	if (ctxname == NULL)
665 		ctxname = "";
666 	log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")",
667 	     backend->ab_name, oidbuf,ctxname, priority);
668 
669 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
670 		if (errno == ENOMEM) {
671 			log_warn("%s: Can't unregister %s: Processing error",
672 			    backend->ab_name, oidbuf);
673 			return APPL_ERROR_PROCESSINGERROR;
674 		}
675 		log_info("%s: Can't unregister %s: Unsupported context \"%s\"",
676 		    backend->ab_name, oidbuf, ctxname);
677 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
678 	}
679 
680 	if (priority == 0) {
681 		log_warnx("%s: Can't unregister %s: priority can't be 0",
682 		    backend->ab_name, oidbuf);
683 		return APPL_ERROR_PARSEERROR;
684 	}
685 
686 	if (range_subid == 0)
687 		return appl_region_unregister_match(ctx, priority, oid, oidbuf,
688 		    backend, 1);
689 
690 	range_subid--;
691 	if (range_subid >= oid->bo_n) {
692 		log_warnx("%s: Can't unregiser %s: range_subid too large",
693 		    backend->ab_name, oidbuf);
694 		return APPL_ERROR_PARSEERROR;
695 	}
696 	if (oid->bo_id[range_subid] > upper_bound) {
697 		log_warnx("%s: Can't unregister %s: upper bound smaller than "
698 		    "range_subid", backend->ab_name, oidbuf);
699 		return APPL_ERROR_PARSEERROR;
700 	}
701 
702 	lower_bound = oid->bo_id[range_subid];
703 	do {
704 		if ((error = appl_region_unregister_match(ctx, priority, oid,
705 		    oidbuf, backend, 0)) != APPL_ERROR_NOERROR)
706 			return error;
707 	} while (oid->bo_id[range_subid]++ != upper_bound);
708 
709 	oid->bo_id[range_subid] = lower_bound;
710 	do {
711 		(void)appl_region_unregister_match(ctx, priority, oid, oidbuf,
712 		    backend, 1);
713 	} while (oid->bo_id[range_subid]++ != upper_bound);
714 
715 	return APPL_ERROR_NOERROR;
716 }
717 
718 enum appl_error
719 appl_region_unregister_match(struct appl_context *ctx, uint8_t priority,
720     struct ber_oid *oid, char *oidbuf, struct appl_backend *backend, int dofree)
721 {
722 	struct appl_region *region, search;
723 
724 	search.ar_oid = *oid;
725 	region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
726 	while (region != NULL && region->ar_priority < priority)
727 		region = region->ar_next;
728 	if (region == NULL || region->ar_priority != priority) {
729 		log_warnx("%s: Can't unregister %s: region not found",
730 		    backend->ab_name, oidbuf);
731 		return APPL_ERROR_UNKNOWNREGISTRATION;
732 	}
733 	if (region->ar_backend != backend) {
734 		log_warnx("%s: Can't unregister %s: region not owned "
735 		    "by backend", backend->ab_name, oidbuf);
736 		return APPL_ERROR_UNKNOWNREGISTRATION;
737 	}
738 	if (dofree)
739 		appl_region_free(ctx, region);
740 	return APPL_ERROR_NOERROR;
741 }
742 
743 void
744 appl_region_free(struct appl_context *ctx, struct appl_region *region)
745 {
746 	struct appl_region *pregion;
747 
748 	pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region);
749 
750 	if (pregion == region) {
751 		RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
752 		if (region->ar_next != NULL)
753 			RB_INSERT(appl_regions, &(ctx->ac_regions),
754 			    region->ar_next);
755 	} else {
756 		while (pregion->ar_next != region)
757 			pregion = pregion->ar_next;
758 		pregion->ar_next = region->ar_next;
759 	}
760 
761 	free(region);
762 }
763 
764 /* backend is owned by the sub-application, just release application.c stuff */
765 void
766 appl_close(struct appl_backend *backend)
767 {
768 	struct appl_context *ctx;
769 	struct appl_agentcap *cap, *tcap;
770 	struct appl_region *region, *tregion, *nregion;
771 	struct appl_request_downstream *request, *trequest;
772 
773 	TAILQ_FOREACH(ctx, &contexts, ac_entries) {
774 		TAILQ_FOREACH_SAFE(cap, &(ctx->ac_agentcaps), aa_entry, tcap) {
775 			if (cap->aa_backend == backend)
776 				appl_agentcap_free(cap);
777 		}
778 		RB_FOREACH_SAFE(region, appl_regions,
779 		    &(ctx->ac_regions), tregion) {
780 			while (region != NULL) {
781 				nregion = region->ar_next;
782 				if (region->ar_backend == backend)
783 					appl_region_free(ctx, region);
784 				region = nregion;
785 			}
786 		}
787 	}
788 
789 	RB_FOREACH_SAFE(request, appl_requests,
790 	    &(backend->ab_requests), trequest)
791 		appl_request_downstream_free(request);
792 }
793 
794 struct appl_region *
795 appl_region_find(struct appl_context *ctx,
796     const struct ber_oid *oid)
797 {
798 	struct appl_region *region, search;
799 
800 	search.ar_oid = *oid;
801 	while (search.ar_oid.bo_n > 0) {
802 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
803 		if (region != NULL)
804 			return region;
805 		search.ar_oid.bo_n--;
806 	}
807 	return NULL;
808 }
809 
810 struct appl_region *
811 appl_region_next(struct appl_context *ctx, struct ber_oid *oid,
812     struct appl_region *cregion)
813 {
814 	struct appl_region search, *nregion, *pregion;
815 	int cmp;
816 
817 	search.ar_oid = *oid;
818 	nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
819 
820 	if (cregion == nregion)
821 		nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion);
822 	/* Past last element in tree, we might still have a parent */
823 	if (nregion == NULL) {
824 		search.ar_oid = cregion->ar_oid;
825 		search.ar_oid.bo_n--;
826 		return appl_region_find(ctx, &(search.ar_oid));
827 	}
828 	cmp = appl_region_cmp(cregion, nregion);
829 	if (cmp >= 0)
830 		fatalx("%s: wrong OID order", __func__);
831 	/* Direct descendant */
832 	if (cmp == -2)
833 		return nregion;
834 
835 	/* cmp == -1 */
836 	search.ar_oid = cregion->ar_oid;
837 	/* Find direct next sibling */
838 	ober_oid_nextsibling(&(search.ar_oid));
839 	if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0)
840 		return nregion;
841 	/* Sibling gaps go to parent, or end end at border */
842 	search.ar_oid = cregion->ar_oid;
843 	search.ar_oid.bo_n--;
844 	pregion = appl_region_find(ctx, &(search.ar_oid));
845 
846 	return pregion != NULL ? pregion : nregion;
847 }
848 
849 /* Name from RFC 3413 section 3.2 */
850 void
851 appl_processpdu(struct snmp_message *statereference, const char *ctxname,
852     enum snmp_version pduversion, struct ber_element *pdu)
853 {
854 	struct appl_context *ctx;
855 	struct appl_request_upstream *ureq;
856 	struct ber_element *varbind, *varbindlist;
857 	long long nonrepeaters, maxrepetitions;
858 	static uint32_t transactionid;
859 	int32_t requestid;
860 	size_t i, varbindlen = 0, repeaterlen;
861 
862 	/* pdu must be ASN.1 validated in snmpe.c */
863 	(void) ober_scanf_elements(pdu, "{diie", &requestid, &nonrepeaters,
864 	    &maxrepetitions, &varbindlist);
865 
866 	/* RFC 3413, section 3.2, processPDU, item 5, final bullet */
867 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
868 		snmp_target_mib.snmp_unknowncontexts++;
869 		appl_report(statereference, requestid,
870 		    &OID(MIB_snmpUnknownContexts, 0));
871 		return;
872 	}
873 
874 	if ((ureq = malloc(sizeof(*ureq))) == NULL)
875 		fatal("malloc");
876 
877 	ureq->aru_ctx = ctx;
878 	ureq->aru_statereference = statereference;
879 	ureq->aru_transactionid = transactionid++;
880 	ureq->aru_requesttype = pdu->be_type;
881 	ureq->aru_responsetype = SNMP_C_RESPONSE;
882 	ureq->aru_requestid = requestid;
883 	ureq->aru_error = APPL_ERROR_NOERROR;
884 	ureq->aru_index = 0;
885 	ureq->aru_nonrepeaters = nonrepeaters;
886 	ureq->aru_maxrepetitions = maxrepetitions;
887 	ureq->aru_varbindlen = 0;
888 	ureq->aru_locked = 0;
889 	ureq->aru_pduversion = pduversion;
890 
891 	varbind = varbindlist->be_sub;
892 	for (; varbind != NULL; varbind = varbind->be_next)
893 		varbindlen++;
894 
895 	repeaterlen = varbindlen - nonrepeaters;
896 	if (pdu->be_type == SNMP_C_GETBULKREQ)
897 		ureq->aru_varbindlen = nonrepeaters +
898 		    (repeaterlen * maxrepetitions);
899 	else
900 		ureq->aru_varbindlen = varbindlen;
901 	if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen,
902 	    sizeof(*ureq->aru_vblist))) == NULL)
903 		fatal("malloc");
904 
905 	varbind = varbindlist->be_sub;
906 	/* Use aru_varbindlen in case maxrepetitions == 0 */
907 	for (i = 0; i < ureq->aru_varbindlen; i++) {
908 		ureq->aru_vblist[i].avi_request_upstream = ureq;
909 		ureq->aru_vblist[i].avi_index = i + 1;
910 		ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW;
911 		/* This can only happen with bulkreq */
912 		if (varbind == NULL) {
913 			ureq->aru_vblist[i - repeaterlen].avi_sub =
914 			    &(ureq->aru_vblist[i]);
915 			ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL;
916 			ureq->aru_vblist[i].avi_index =
917 			    ureq->aru_vblist[i - repeaterlen].avi_index;
918 			continue;
919 		}
920 		ober_get_oid(varbind->be_sub,
921 		    &(ureq->aru_vblist[i].avi_varbind.av_oid));
922 		ureq->aru_vblist[i].avi_origid =
923 		    ureq->aru_vblist[i].avi_varbind.av_oid;
924 		if (i + 1 < varbindlen)
925 			ureq->aru_vblist[i].avi_varbind.av_next =
926 			    &(ureq->aru_vblist[i + 1].avi_varbind);
927 		else
928 			ureq->aru_vblist[i].avi_varbind.av_next = NULL;
929 		varbind = varbind->be_next;
930 	}
931 
932 	appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters,
933 	    maxrepetitions, &(ureq->aru_vblist[0].avi_varbind));
934 
935 	appl_request_upstream_resolve(ureq);
936 }
937 
938 void
939 appl_request_upstream_free(struct appl_request_upstream *ureq)
940 {
941 	size_t i;
942 	struct appl_varbind_internal *vb;
943 
944 	if (ureq == NULL)
945 		return;
946 
947 	ureq->aru_locked = 1;
948 	for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) {
949 		vb = &(ureq->aru_vblist[i]);
950 		ober_free_elements(vb->avi_varbind.av_value);
951 		appl_request_downstream_free(vb->avi_request_downstream);
952 	}
953 	free(ureq->aru_vblist);
954 
955 	assert(ureq->aru_statereference == NULL);
956 
957 	free(ureq);
958 }
959 
960 void
961 appl_request_downstream_free(struct appl_request_downstream *dreq)
962 {
963 	struct appl_varbind_internal *vb;
964 
965 	if (dreq == NULL)
966 		return;
967 
968 	RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq);
969 	evtimer_del(&(dreq->ard_timer));
970 
971 	for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) {
972 		vb->avi_request_downstream = NULL;
973 		if (vb->avi_state == APPL_VBSTATE_PENDING)
974 			vb->avi_state = APPL_VBSTATE_NEW;
975 	}
976 
977 	appl_request_upstream_resolve(dreq->ard_request);
978 	free(dreq);
979 }
980 
981 void
982 appl_request_upstream_resolve(struct appl_request_upstream *ureq)
983 {
984 	struct appl_varbind_internal *vb, *lvb, *tvb;
985 	struct appl_request_downstream *dreq;
986 	struct appl_region *region, *lregion;
987 	struct timeval tv;
988 	int done;
989 	size_t i;
990 	int32_t maxrepetitions;
991 	int32_t timeout;
992 
993 	if (ureq->aru_locked)
994 		return;
995 	ureq->aru_locked = 1;
996 
997 	if (ureq->aru_requesttype == SNMP_C_SETREQ) {
998 		ureq->aru_error = APPL_ERROR_NOTWRITABLE;
999 		ureq->aru_index = 1;
1000 		appl_request_upstream_reply(ureq);
1001 		return;
1002 	}
1003 
1004  next:
1005 	dreq = NULL;
1006 	lvb = NULL;
1007 	done = 1;
1008 	timeout = 0;
1009 
1010 	if (ureq->aru_error != APPL_ERROR_NOERROR) {
1011 		appl_request_upstream_reply(ureq);
1012 		return;
1013 	}
1014 	for (i = 0; i < ureq->aru_varbindlen; i++) {
1015 		vb = &(ureq->aru_vblist[i]);
1016 
1017 		switch (vb->avi_state) {
1018 		case APPL_VBSTATE_MUSTFILL:
1019 		case APPL_VBSTATE_PENDING:
1020 			done = 0;
1021 			continue;
1022 		case APPL_VBSTATE_DONE:
1023 			continue;
1024 		case APPL_VBSTATE_NEW:
1025 			break;
1026 		}
1027 		if (appl_varbind_backend(vb) == -1)
1028 			fatal("appl_varbind_backend");
1029 		if (vb->avi_state != APPL_VBSTATE_DONE)
1030 			done = 0;
1031 	}
1032 
1033 	for (i = 0; i < ureq->aru_varbindlen; i++) {
1034 		vb = &(ureq->aru_vblist[i]);
1035 
1036 		if (vb->avi_state != APPL_VBSTATE_NEW)
1037 			continue;
1038 
1039 		vb = &(ureq->aru_vblist[i]);
1040 		region = vb->avi_region;
1041 		lregion = lvb != NULL ? lvb->avi_region : NULL;
1042 		if (lvb != NULL && region->ar_backend != lregion->ar_backend)
1043 			continue;
1044 
1045 		vb->avi_varbind.av_next = NULL;
1046 		vb->avi_next = NULL;
1047 		tvb = vb;
1048 		for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub)
1049 			maxrepetitions++;
1050 		if (dreq == NULL) {
1051 			if ((dreq = malloc(sizeof(*dreq))) == NULL)
1052 				fatal("malloc");
1053 
1054 			dreq->ard_request = ureq;
1055 			dreq->ard_vblist = vb;
1056 			dreq->ard_backend = vb->avi_region->ar_backend;
1057 			dreq->ard_retries = dreq->ard_backend->ab_retries;
1058 			dreq->ard_requesttype = ureq->aru_requesttype;
1059 			/*
1060 			 * We don't yet fully handle bulkrequest responses.
1061 			 * It's completely valid to map onto getrequest.
1062 			 * maxrepetitions calculated in preparation of support.
1063 			 */
1064 			if (dreq->ard_requesttype == SNMP_C_GETBULKREQ &&
1065 			    dreq->ard_backend->ab_fn->ab_getbulk == NULL)
1066 				dreq->ard_requesttype = SNMP_C_GETNEXTREQ;
1067 			/*
1068 			 * If first varbind is nonrepeater, set maxrepetitions
1069 			 * to 0, so that the next varbind with
1070 			 * maxrepetitions > 1 determines length.
1071 			 */
1072 			if (maxrepetitions == 1) {
1073 				dreq->ard_maxrepetitions = 0;
1074 				dreq->ard_nonrepeaters = 1;
1075 			} else {
1076 				dreq->ard_maxrepetitions = maxrepetitions;
1077 				dreq->ard_nonrepeaters = 0;
1078 			}
1079 			do {
1080 				dreq->ard_requestid = arc4random();
1081 			} while (RB_INSERT(appl_requests,
1082 			    &(dreq->ard_backend->ab_requests), dreq) != NULL);
1083 			lvb = vb;
1084 		/* avi_sub isn't set on !bulkrequest, so we always enter here */
1085 		} else if (maxrepetitions == 1) {
1086 			dreq->ard_nonrepeaters++;
1087 			vb->avi_varbind.av_next =
1088 			    &(dreq->ard_vblist->avi_varbind);
1089 			vb->avi_next = dreq->ard_vblist;
1090 			dreq->ard_vblist = vb;
1091 		} else {
1092 			lvb->avi_varbind.av_next = &(vb->avi_varbind);
1093 			lvb->avi_next = vb;
1094 			/* RFC 2741 section 7.2.1.3:
1095 			 * The value of g.max_repetitions in the GetBulk-PDU may
1096 			 * be less than (but not greater than) the value in the
1097 			 * original request PDU.
1098 			 */
1099 			if (dreq->ard_maxrepetitions > maxrepetitions ||
1100 			    dreq->ard_maxrepetitions == 0)
1101 				dreq->ard_maxrepetitions = maxrepetitions;
1102 			lvb = vb;
1103 		}
1104 		vb->avi_request_downstream = dreq;
1105 		vb->avi_state = APPL_VBSTATE_PENDING;
1106 		if (region->ar_timeout > timeout)
1107 			timeout = region->ar_timeout;
1108 	}
1109 
1110 	if (dreq == NULL) {
1111 		ureq->aru_locked = 0;
1112 		if (done)
1113 			appl_request_upstream_reply(ureq);
1114 		return;
1115 	}
1116 
1117 	tv.tv_sec = timeout / 100;
1118 	tv.tv_usec = (timeout % 100) * 10000;
1119 	evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq);
1120 	evtimer_add(&(dreq->ard_timer), &tv);
1121 
1122 	appl_request_downstream_send(dreq);
1123 	goto next;
1124 }
1125 
1126 void
1127 appl_request_downstream_send(struct appl_request_downstream *dreq)
1128 {
1129 
1130 	appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype,
1131 	    dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind));
1132 
1133 	if (dreq->ard_requesttype == SNMP_C_GETREQ) {
1134 		dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend,
1135 		    dreq->ard_request->aru_transactionid,
1136 		    dreq->ard_requestid,
1137 		    APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
1138 		    &(dreq->ard_vblist->avi_varbind));
1139 	} else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) {
1140 		dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend,
1141 		    dreq->ard_request->aru_transactionid,
1142 		    dreq->ard_requestid,
1143 		    APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
1144 		    &(dreq->ard_vblist->avi_varbind));
1145 	}
1146 }
1147 
1148 void
1149 appl_request_downstream_timeout(__unused int fd, __unused short event,
1150     void *cookie)
1151 {
1152 	struct appl_request_downstream *dreq = cookie;
1153 
1154 	log_info("%s: %"PRIu32" timed out%s",
1155 	    dreq->ard_backend->ab_name, dreq->ard_requestid,
1156 	    dreq->ard_retries > 0 ? ": retrying" : "");
1157 	if (dreq->ard_retries > 0) {
1158 		dreq->ard_retries--;
1159 		appl_request_downstream_send(dreq);
1160 	} else
1161 		appl_response(dreq->ard_backend, dreq->ard_requestid,
1162 		    APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind));
1163 }
1164 
1165 void
1166 appl_request_upstream_reply(struct appl_request_upstream *ureq)
1167 {
1168 	struct ber_element *varbindlist = NULL, *varbind = NULL, *value;
1169 	struct appl_varbind_internal *vb;
1170 	size_t i, repvarbinds, varbindlen;
1171 	ssize_t match = -1;
1172 
1173 	varbindlen = ureq->aru_varbindlen;
1174 
1175 	if (ureq->aru_pduversion == SNMP_V1) {
1176 		/* RFC 3584 section 4.2.2.2 Map exceptions */
1177 		for (i = 0; i < varbindlen; i++) {
1178 			vb = &(ureq->aru_vblist[i]);
1179 			value = vb->avi_varbind.av_value;
1180 			if (value != NULL &&
1181 			    value->be_class == BER_CLASS_CONTEXT)
1182 				appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME);
1183 		}
1184 		/* RFC 3584 section 4.4 Map errors */
1185 		switch (ureq->aru_error) {
1186 		case APPL_ERROR_WRONGVALUE:
1187 		case APPL_ERROR_WRONGENCODING:
1188 		case APPL_ERROR_WRONGTYPE:
1189 		case APPL_ERROR_WRONGLENGTH:
1190 		case APPL_ERROR_INCONSISTENTVALUE:
1191 			ureq->aru_error = APPL_ERROR_BADVALUE;
1192 			break;
1193 		case APPL_ERROR_NOACCESS:
1194 		case APPL_ERROR_NOTWRITABLE:
1195 		case APPL_ERROR_NOCREATION:
1196 		case APPL_ERROR_INCONSISTENTNAME:
1197 		case APPL_ERROR_AUTHORIZATIONERROR:
1198 			ureq->aru_error = APPL_ERROR_NOSUCHNAME;
1199 			break;
1200 		case APPL_ERROR_RESOURCEUNAVAILABLE:
1201 		case APPL_ERROR_COMMITFAILED:
1202 		case APPL_ERROR_UNDOFAILED:
1203 			ureq->aru_error = APPL_ERROR_GENERR;
1204 			break;
1205 		default:
1206 			break;
1207 		}
1208 	}
1209 	/* RFC 3416 section 4.2.{1,2,3} reset original varbinds */
1210 	if (ureq->aru_error != APPL_ERROR_NOERROR) {
1211 		if (ureq->aru_requesttype == SNMP_C_GETBULKREQ)
1212 			varbindlen =
1213 			    (ureq->aru_varbindlen - ureq->aru_nonrepeaters) /
1214 			    ureq->aru_maxrepetitions;
1215 		for (i = 0; i < varbindlen; i++) {
1216 			vb = &(ureq->aru_vblist[i]);
1217 			vb->avi_varbind.av_oid = vb->avi_origid;
1218 			ober_free_elements(vb->avi_varbind.av_value);
1219 			vb->avi_varbind.av_value = ober_add_null(NULL);
1220 		}
1221 	/* RFC 3416 section 4.2.3: Strip excessive EOMV */
1222 	} else if (ureq->aru_requesttype == SNMP_C_GETBULKREQ) {
1223 		repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) /
1224 		    ureq->aru_maxrepetitions;
1225 		for (i = ureq->aru_nonrepeaters;
1226 		    i < ureq->aru_varbindlen - repvarbinds; i++) {
1227 			value = ureq->aru_vblist[i].avi_varbind.av_value;
1228 			if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 &&
1229 			    value->be_class == BER_CLASS_CONTEXT &&
1230 			    value->be_type == APPL_EXC_ENDOFMIBVIEW) {
1231 				if (match != -1)
1232 					break;
1233 				match = i;
1234 			}
1235 			if (value->be_class != BER_CLASS_CONTEXT ||
1236 			    value->be_type != APPL_EXC_ENDOFMIBVIEW)
1237 				match = -1;
1238 		}
1239 		if (match != -1)
1240 			varbindlen = match + repvarbinds;
1241 	}
1242 
1243 	for (i = 0; i < varbindlen; i++) {
1244 		vb = &(ureq->aru_vblist[i]);
1245 		vb->avi_varbind.av_next =
1246 		    &(ureq->aru_vblist[i + 1].avi_varbind);
1247 		value = vb->avi_varbind.av_value;
1248 		if (value->be_class == BER_CLASS_CONTEXT &&
1249 		    value->be_type == APPL_EXC_ENDOFMIBVIEW)
1250 			vb->avi_varbind.av_oid = vb->avi_origid;
1251 	}
1252 
1253 	ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL;
1254 	appl_pdu_log(NULL, ureq->aru_responsetype, ureq->aru_requestid,
1255 	    ureq->aru_error, ureq->aru_index,
1256 	    &(ureq->aru_vblist[0].avi_varbind));
1257 
1258 	for (i = 0; i < varbindlen; i++) {
1259 		varbind = ober_printf_elements(varbind, "{Oe}",
1260 		    &(ureq->aru_vblist[i].avi_varbind.av_oid),
1261 		    ureq->aru_vblist[i].avi_varbind.av_value);
1262 		ureq->aru_vblist[i].avi_varbind.av_value = NULL;
1263 		if (varbind == NULL)
1264 			fatal("ober_printf_elements");
1265 		if (varbindlist == NULL)
1266 			varbindlist = varbind;
1267 	}
1268 
1269 	snmpe_send(ureq->aru_statereference, ureq->aru_responsetype,
1270 	    ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist);
1271 	ureq->aru_statereference = NULL;
1272 	appl_request_upstream_free(ureq);
1273 }
1274 
1275 /* Name from RFC 2741 section 6.2.16 */
1276 void
1277 appl_response(struct appl_backend *backend, int32_t requestid,
1278     enum appl_error error, int16_t index, struct appl_varbind *vblist)
1279 {
1280 	struct appl_request_downstream *dreq, search;
1281 	struct appl_request_upstream *ureq = NULL;
1282 	const char *errstr;
1283 	char oidbuf[1024];
1284 	struct appl_varbind *vb;
1285 	struct appl_varbind_internal *origvb = NULL;
1286 	int invalid = 0;
1287 	int next = 0, eomv;
1288 	int32_t i;
1289 
1290 	appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist);
1291 
1292 	search.ard_requestid = requestid;
1293 	dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search);
1294 	if (dreq == NULL) {
1295 		log_debug("%s: %"PRIu32" not outstanding",
1296 		    backend->ab_name, requestid);
1297 		/* Continue to verify validity */
1298 	} else {
1299 		ureq = dreq->ard_request;
1300 		next = ureq->aru_requesttype == SNMP_C_GETNEXTREQ ||
1301 		    ureq->aru_requesttype == SNMP_C_GETBULKREQ;
1302 		origvb = dreq->ard_vblist;
1303 		if (!appl_error_valid(error, dreq->ard_requesttype)) {
1304 			log_warnx("%s: %"PRIu32" Invalid error",
1305 			    backend->ab_name, requestid);
1306 			invalid = 1;
1307 		}
1308 	}
1309 
1310 	vb = vblist;
1311 	for (i = 1; vb != NULL; vb = vb->av_next, i++) {
1312 		if (!appl_varbind_valid(vb, origvb, next,
1313 		    error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) {
1314 			smi_oid2string(&(vb->av_oid), oidbuf,
1315 			    sizeof(oidbuf), 0);
1316 			log_warnx("%s: %"PRIu32" %s: %s",
1317 			    backend->ab_name, requestid, oidbuf, errstr);
1318 			invalid = 1;
1319 		}
1320 		/* Transfer av_value */
1321 		if (origvb != NULL) {
1322 			if (error != APPL_ERROR_NOERROR && i == index)
1323 				appl_varbind_error(origvb, error);
1324 			origvb->avi_state = APPL_VBSTATE_DONE;
1325 			origvb->avi_varbind.av_oid = vb->av_oid;
1326 
1327 			eomv = vb->av_value != NULL &&
1328 			    vb->av_value->be_class == BER_CLASS_CONTEXT &&
1329 			    vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW;
1330 			/*
1331 			 * Treat results past av_oid_end for backends that
1332 			 * don't support searchranges as EOMV
1333 			 */
1334 			eomv |= !backend->ab_range && next &&
1335 			    ober_oid_cmp(&(vb->av_oid),
1336 			    &(origvb->avi_varbind.av_oid_end)) >= 0;
1337 			/* RFC 3584 section 4.2.2.1 */
1338 			if (ureq->aru_pduversion == SNMP_V1 &&
1339 			    vb->av_value != NULL &&
1340 			    vb->av_value->be_class == BER_CLASS_APPLICATION &&
1341 			    vb->av_value->be_type == SNMP_COUNTER64) {
1342 				if (next)
1343 					eomv = 1;
1344 				else
1345 					appl_varbind_error(origvb,
1346 					    APPL_ERROR_NOSUCHNAME);
1347 			}
1348 
1349 			if (eomv) {
1350 				ober_free_elements(vb->av_value);
1351 				origvb->avi_varbind.av_oid =
1352 				    origvb->avi_varbind.av_oid_end;
1353 				origvb->avi_varbind.av_include = 1;
1354 				vb->av_value = NULL;
1355 				origvb->avi_state = APPL_VBSTATE_NEW;
1356 			}
1357 			origvb->avi_varbind.av_value = vb->av_value;
1358 			if (origvb->avi_varbind.av_next == NULL &&
1359 			    vb->av_next != NULL) {
1360 				log_warnx("%s: Request %"PRIu32" returned more "
1361 				    "varbinds then requested",
1362 				    backend->ab_name, requestid);
1363 				invalid = 1;
1364 			}
1365 			if (origvb->avi_sub != NULL &&
1366 			    origvb->avi_state == APPL_VBSTATE_DONE) {
1367 				origvb->avi_sub->avi_varbind.av_oid =
1368 				    origvb->avi_varbind.av_oid;
1369 				origvb->avi_sub->avi_origid =
1370 				    origvb->avi_varbind.av_oid;
1371 				origvb->avi_sub->avi_state = APPL_VBSTATE_NEW;
1372 			}
1373 			origvb = origvb->avi_next;
1374 		} else {
1375 			ober_free_elements(vb->av_value);
1376 			vb->av_value = NULL;
1377 		}
1378 	}
1379 	if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) {
1380 		log_warnx("Invalid error index");
1381 		invalid = 1;
1382 	}
1383 /* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */
1384 #if PEDANTIC
1385 	if (error == APPL_ERROR_NOERROR && index != 0) {
1386 		log_warnx("error index with no error");
1387 		invalid = 1;
1388 	}
1389 #endif
1390 	if (vb == NULL && origvb != NULL) {
1391 		log_warnx("%s: Request %"PRIu32" returned less varbinds then "
1392 		    "requested", backend->ab_name, requestid);
1393 		invalid = 1;
1394 	}
1395 
1396 	if (dreq != NULL) {
1397 		if (invalid)
1398 			appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR);
1399 		appl_request_downstream_free(dreq);
1400 	}
1401 
1402 	if (invalid && backend->ab_fn->ab_close != NULL) {
1403 		log_warnx("%s: Closing: Too many parse errors",
1404 		    backend->ab_name);
1405 		backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR);
1406 	}
1407 }
1408 
1409 int
1410 appl_varbind_valid(struct appl_varbind *varbind,
1411     struct appl_varbind_internal *request, int next, int null, int range,
1412     const char **errstr)
1413 {
1414 	int cmp;
1415 	int eomv = 0;
1416 
1417 	if (null)
1418 		next = 0;
1419 
1420 	if (varbind->av_value == NULL) {
1421 		if (!null) {
1422 			*errstr = "missing value";
1423 			return 0;
1424 		}
1425 		return 1;
1426 	}
1427 	if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) {
1428 		switch (varbind->av_value->be_type) {
1429 		case BER_TYPE_NULL:
1430 			if (null)
1431 				break;
1432 			*errstr = "not expecting null value";
1433 			return 0;
1434 		case BER_TYPE_INTEGER:
1435 		case BER_TYPE_OCTETSTRING:
1436 		case BER_TYPE_OBJECT:
1437 			if (!null)
1438 				break;
1439 			/* FALLTHROUGH */
1440 		default:
1441 			*errstr = "invalid value";
1442 			return 0;
1443 		}
1444 	} else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) {
1445 		switch (varbind->av_value->be_type) {
1446 		case SNMP_T_IPADDR:
1447 		case SNMP_T_COUNTER32:
1448 		case SNMP_T_GAUGE32:
1449 		case SNMP_T_TIMETICKS:
1450 		case SNMP_T_OPAQUE:
1451 		case SNMP_T_COUNTER64:
1452 			if (!null)
1453 				break;
1454 			/* FALLTHROUGH */
1455 		default:
1456 			*errstr = "expecting null value";
1457 			return 0;
1458 		}
1459 	} else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) {
1460 		switch (varbind->av_value->be_type) {
1461 		case APPL_EXC_NOSUCHOBJECT:
1462 			if (next && request != NULL) {
1463 				*errstr = "Unexpected noSuchObject";
1464 				return 0;
1465 			}
1466 			/* FALLTHROUGH */
1467 		case APPL_EXC_NOSUCHINSTANCE:
1468 			if (null) {
1469 				*errstr = "expecting null value";
1470 				return 0;
1471 			}
1472 			if (next && request != NULL) {
1473 				*errstr = "Unexpected noSuchInstance";
1474 				return 0;
1475 			}
1476 			break;
1477 		case APPL_EXC_ENDOFMIBVIEW:
1478 			if (null) {
1479 				*errstr = "expecting null value";
1480 				return 0;
1481 			}
1482 			if (!next && request != NULL) {
1483 				*errstr = "Unexpected endOfMibView";
1484 				return 0;
1485 			}
1486 			eomv = 1;
1487 			break;
1488 		default:
1489 			*errstr = "invalid exception";
1490 			return 0;
1491 		}
1492 	} else {
1493 		*errstr = "invalid value";
1494 		return 0;
1495 	}
1496 
1497 	if (request == NULL)
1498 		return 1;
1499 
1500 	cmp = ober_oid_cmp(&(request->avi_varbind.av_oid), &(varbind->av_oid));
1501 	if (next) {
1502 		if (request->avi_region->ar_instance &&
1503 		    ober_oid_cmp(&(request->avi_region->ar_oid),
1504 		    &(varbind->av_oid)) != 0) {
1505 			*errstr = "oid below instance";
1506 			return 0;
1507 		}
1508 		if (!eomv) {
1509 			if (request->avi_varbind.av_include) {
1510 				if (cmp > 0) {
1511 					*errstr = "oid not incrementing";
1512 					return 0;
1513 				}
1514 			} else {
1515 				if (cmp >= 0) {
1516 					*errstr = "oid not incrementing";
1517 					return 0;
1518 				}
1519 			}
1520 			if (range && ober_oid_cmp(&(varbind->av_oid),
1521 			    &(request->avi_varbind.av_oid_end)) >= 0) {
1522 				*errstr = "end oid not honoured";
1523 				return 0;
1524 			}
1525 		}
1526 	} else {
1527 		if (cmp != 0) {
1528 			*errstr = "oids not equal";
1529 			return 0;
1530 		}
1531 	}
1532 	return 1;
1533 }
1534 
1535 int
1536 appl_error_valid(enum appl_error error, enum snmp_pdutype type)
1537 {
1538 	switch (error) {
1539 	case APPL_ERROR_NOERROR:
1540 	case APPL_ERROR_TOOBIG:
1541 	case APPL_ERROR_NOSUCHNAME:
1542 	case APPL_ERROR_GENERR:
1543 		return 1;
1544 	case APPL_ERROR_BADVALUE:
1545 	case APPL_ERROR_READONLY:
1546 	case APPL_ERROR_NOACCESS:
1547 	case APPL_ERROR_WRONGTYPE:
1548 	case APPL_ERROR_WRONGLENGTH:
1549 	case APPL_ERROR_WRONGENCODING:
1550 	case APPL_ERROR_WRONGVALUE:
1551 	case APPL_ERROR_NOCREATION:
1552 	case APPL_ERROR_INCONSISTENTVALUE:
1553 	case APPL_ERROR_RESOURCEUNAVAILABLE:
1554 	case APPL_ERROR_COMMITFAILED:
1555 	case APPL_ERROR_UNDOFAILED:
1556 	case APPL_ERROR_NOTWRITABLE:
1557 	case APPL_ERROR_INCONSISTENTNAME:
1558 		return type == SNMP_C_SETREQ;
1559 	case APPL_ERROR_AUTHORIZATIONERROR:
1560 		return type == SNMP_C_GETREQ || type == SNMP_C_SETREQ;
1561 	default:
1562 		return 0;
1563 	}
1564 }
1565 
1566 int
1567 appl_varbind_backend(struct appl_varbind_internal *ivb)
1568 {
1569 	struct appl_request_upstream *ureq = ivb->avi_request_upstream;
1570 	struct appl_region search, *region, *pregion;
1571 	struct appl_varbind *vb = &(ivb->avi_varbind);
1572 	struct ber_oid oid, nextsibling;
1573 	int next, cmp;
1574 
1575 	next = ureq->aru_requesttype == SNMP_C_GETNEXTREQ ||
1576 	    ureq->aru_requesttype == SNMP_C_GETBULKREQ;
1577 
1578 	region = appl_region_find(ureq->aru_ctx, &(vb->av_oid));
1579 	if (region == NULL) {
1580 		if (!next) {
1581 			vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT);
1582 			ivb->avi_state = APPL_VBSTATE_DONE;
1583 			if (vb->av_value == NULL)
1584 				return -1;
1585 			return 0;
1586 		}
1587 		search.ar_oid = vb->av_oid;
1588 		region = RB_NFIND(appl_regions,
1589 		    &(ureq->aru_ctx->ac_regions), &search);
1590 		if (region == NULL)
1591 			goto eomv;
1592 		vb->av_oid = region->ar_oid;
1593 		vb->av_include = 1;
1594 	}
1595 	cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid));
1596 	if (cmp == -2) {
1597 		if (region->ar_instance) {
1598 			if (!next) {
1599 				vb->av_value =
1600 				    appl_exception(APPL_EXC_NOSUCHINSTANCE);
1601 				ivb->avi_state = APPL_VBSTATE_DONE;
1602 				if (vb->av_value == NULL)
1603 					return -1;
1604 				return 0;
1605 			}
1606 			vb->av_oid = region->ar_oid;
1607 			ober_oid_nextsibling(&(vb->av_oid));
1608 			vb->av_include = 1;
1609 			return appl_varbind_backend(ivb);
1610 		}
1611 	} else if (cmp == 0) {
1612 		if (region->ar_instance && next && !vb->av_include) {
1613 			vb->av_oid = region->ar_oid;
1614 			ober_oid_nextsibling(&(vb->av_oid));
1615 			vb->av_include = 1;
1616 			return appl_varbind_backend(ivb);
1617 		}
1618 	}
1619 	ivb->avi_region = region;
1620 	if (next) {
1621 		oid = vb->av_oid;
1622 		/*
1623 		 * For the searchrange end we only want contiguous regions.
1624 		 * This means directly connecting, or overlapping with the same
1625 		 * backend.
1626 		 */
1627 		do {
1628 			pregion = region;
1629 			region = appl_region_next(ureq->aru_ctx, &oid, pregion);
1630 			if (region == NULL) {
1631 				oid = pregion->ar_oid;
1632 				ober_oid_nextsibling(&oid);
1633 				break;
1634 			}
1635 			cmp = ober_oid_cmp(&(region->ar_oid), &oid);
1636 			if (cmp == 2)
1637 				oid = region->ar_oid;
1638 			else if (cmp == 1) {
1639 				/* Break out if we find a gap */
1640 				nextsibling = pregion->ar_oid;
1641 				ober_oid_nextsibling(&nextsibling);
1642 				if (ober_oid_cmp(&(region->ar_oid),
1643 				    &nextsibling) != 0) {
1644 					oid = pregion->ar_oid;
1645 					ober_oid_nextsibling(&oid);
1646 					break;
1647 				}
1648 				oid = region->ar_oid;
1649 			} else if (cmp == -2) {
1650 				oid = pregion->ar_oid;
1651 				ober_oid_nextsibling(&oid);
1652 			} else
1653 				fatalx("We can't stop/move back on getnext");
1654 		} while (region->ar_backend == pregion->ar_backend);
1655 		vb->av_oid_end = oid;
1656 	}
1657 	return 0;
1658 
1659  eomv:
1660 	do {
1661 		ivb->avi_varbind.av_value =
1662 		    appl_exception(APPL_EXC_ENDOFMIBVIEW);
1663 		ivb->avi_state = APPL_VBSTATE_DONE;
1664 		if (ivb->avi_varbind.av_value == NULL)
1665 			return -1;
1666 		if (ivb->avi_sub != NULL) {
1667 			ivb->avi_sub->avi_varbind.av_oid =
1668 			    ivb->avi_varbind.av_oid;
1669 			ivb->avi_sub->avi_origid = ivb->avi_origid;
1670 		}
1671 		ivb = ivb->avi_sub;
1672 	} while (ivb != NULL);
1673 
1674 	return 0;
1675 }
1676 
1677 void
1678 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error)
1679 {
1680 	struct appl_request_upstream *ureq = avi->avi_request_upstream;
1681 
1682 	if (ureq->aru_error == APPL_ERROR_GENERR)
1683 		return;
1684 	if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR)
1685 		return;
1686 	ureq->aru_error = error;
1687 	ureq->aru_index = avi->avi_index;
1688 }
1689 
1690 void
1691 appl_report(struct snmp_message *statereference, int32_t requestid,
1692     struct ber_oid *oid)
1693 {
1694 	struct appl_request_upstream *ureq;
1695 
1696 	if ((ureq = calloc(1, sizeof(*ureq))) == NULL)
1697 		fatal("malloc");
1698 	ureq->aru_ctx = appl_context(NULL, 0);
1699 	ureq->aru_statereference = statereference;
1700 	ureq->aru_requesttype = SNMP_C_GETREQ;
1701 	ureq->aru_responsetype = SNMP_C_REPORT;
1702 	ureq->aru_requestid = requestid;
1703 	ureq->aru_transactionid = 0;
1704 	ureq->aru_nonrepeaters = 0;
1705 	ureq->aru_maxrepetitions = 0;
1706 	if ((ureq->aru_vblist = calloc(1, sizeof(*ureq->aru_vblist))) == NULL)
1707 		fatal("malloc");
1708 	ureq->aru_varbindlen = 1;
1709 	ureq->aru_error = APPL_ERROR_NOERROR;
1710 	ureq->aru_index = 0;
1711 	ureq->aru_locked = 0;
1712 	ureq->aru_pduversion = SNMP_V3;
1713 
1714 	ureq->aru_vblist[0].avi_state = APPL_VBSTATE_NEW;
1715 	ureq->aru_vblist[0].avi_varbind.av_oid = *oid;
1716 	ureq->aru_vblist[0].avi_varbind.av_value = NULL;
1717 	ureq->aru_vblist[0].avi_varbind.av_next = NULL;
1718 	ureq->aru_vblist[0].avi_origid = *oid;
1719 	ureq->aru_vblist[0].avi_index = 1;
1720 	ureq->aru_vblist[0].avi_request_upstream = ureq;
1721 	ureq->aru_vblist[0].avi_request_downstream = NULL;
1722 	ureq->aru_vblist[0].avi_next = NULL;
1723 	ureq->aru_vblist[0].avi_sub = NULL;
1724 
1725 	appl_request_upstream_resolve(ureq);
1726 }
1727 
1728 struct ber_element *
1729 appl_exception(enum appl_exception type)
1730 {
1731 	struct ber_element *value;
1732 
1733 	if ((value = ober_add_null(NULL)) == NULL) {
1734 		log_warn("malloc");
1735 		return NULL;
1736 	}
1737 	ober_set_header(value, BER_CLASS_CONTEXT, type);
1738 
1739 	return value;
1740 }
1741 
1742 void
1743 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype,
1744     int32_t requestid, uint16_t error, uint16_t index,
1745     struct appl_varbind *vblist)
1746 {
1747 	struct appl_varbind *vb;
1748 	char buf[1024], oidbuf[1024], *str;
1749 	int next;
1750 
1751 	if (log_getverbose() < 2)
1752 		return;
1753 
1754 	next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ);
1755 
1756 	buf[0] = '\0';
1757 	for (vb = vblist; vb != NULL; vb = vb->av_next) {
1758 		strlcat(buf, "{", sizeof(buf));
1759 		strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf,
1760 		    sizeof(oidbuf), 0), sizeof(buf));
1761 		if (next) {
1762 			if (vb->av_include)
1763 				strlcat(buf, "(incl)", sizeof(buf));
1764 			if (vb->av_oid_end.bo_n > 0) {
1765 				strlcat(buf, "-", sizeof(buf));
1766 				strlcat(buf, smi_oid2string(&(vb->av_oid_end),
1767 				    oidbuf, sizeof(oidbuf), 0), sizeof(buf));
1768 			}
1769 		}
1770 		strlcat(buf, ":", sizeof(buf));
1771 		if (vb->av_value != NULL) {
1772 			str = smi_print_element(vb->av_value);
1773 			strlcat(buf, str == NULL ? "???" : str, sizeof(buf));
1774 			free(str);
1775 		} else
1776 			strlcat(buf, "null", sizeof(buf));
1777 		strlcat(buf, "}", sizeof(buf));
1778 	}
1779 	log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}",
1780 	    backend != NULL ? backend->ab_name : "",
1781 	    backend != NULL ? ": " : "",
1782 	    snmpe_pdutype2string(pdutype), requestid, error, index, buf);
1783 }
1784 
1785 void
1786 ober_oid_nextsibling(struct ber_oid *oid)
1787 {
1788 	while (oid->bo_n > 0) {
1789 		oid->bo_id[oid->bo_n - 1]++;
1790 		/* Overflow check */
1791 		if (oid->bo_id[oid->bo_n - 1] != 0)
1792 			return;
1793 		oid->bo_n--;
1794 	}
1795 }
1796 
1797 int
1798 appl_region_cmp(struct appl_region *r1, struct appl_region *r2)
1799 {
1800 	return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid));
1801 }
1802 
1803 int
1804 appl_request_cmp(struct appl_request_downstream *r1,
1805     struct appl_request_downstream *r2)
1806 {
1807 	return r1->ard_requestid < r2->ard_requestid ? -1 :
1808 	    r1->ard_requestid > r2->ard_requestid;
1809 }
1810 
1811 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
1812 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry,
1813     appl_request_cmp);
1814