xref: /openbsd-src/usr.sbin/snmpd/application.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /*	$OpenBSD: application.c,v 1.6 2022/06/30 11:28:36 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/tree.h>
21 
22 #include <assert.h>
23 #include <errno.h>
24 #include <event.h>
25 #include <inttypes.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include "application.h"
31 #include "log.h"
32 #include "smi.h"
33 #include "snmp.h"
34 #include "snmpe.h"
35 #include "mib.h"
36 
37 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts);
38 
39 struct appl_context {
40 	char ac_name[APPL_CONTEXTNAME_MAX + 1];
41 
42 	RB_HEAD(appl_regions, appl_region) ac_regions;
43 
44 	TAILQ_ENTRY(appl_context) ac_entries;
45 };
46 
47 struct appl_region {
48 	struct ber_oid ar_oid;
49 	uint8_t ar_priority;
50 	int32_t ar_timeout;
51 	int ar_instance;
52 	int ar_subtree; /* Claim entire subtree */
53 	struct appl_backend *ar_backend;
54 	struct appl_region *ar_next; /* Sorted by priority */
55 
56 	RB_ENTRY(appl_region) ar_entry;
57 };
58 
59 struct appl_request_upstream {
60 	struct appl_context *aru_ctx;
61 	struct snmp_message *aru_statereference;
62 	int32_t aru_requestid; /* upstream requestid */
63 	int32_t aru_transactionid; /* RFC 2741 section 6.1 */
64 	int16_t aru_nonrepeaters;
65 	int16_t aru_maxrepetitions;
66 	struct appl_varbind_internal *aru_vblist;
67 	size_t aru_varbindlen;
68 	enum appl_error aru_error;
69 	int16_t aru_index;
70 	int aru_locked; /* Prevent recursion through appl_request_send */
71 
72 	enum snmp_version aru_pduversion;
73 	struct ber_element *aru_pdu; /* Original requested pdu */
74 };
75 
76 struct appl_request_downstream {
77 	struct appl_request_upstream *ard_request;
78 	struct appl_backend *ard_backend;
79 	enum snmp_pdutype ard_requesttype;
80 	int16_t ard_nonrepeaters;
81 	int16_t ard_maxrepetitions;
82 	int32_t ard_requestid;
83 	uint8_t ard_retries;
84 
85 	struct appl_varbind_internal *ard_vblist;
86 	struct event ard_timer;
87 
88 	RB_ENTRY(appl_request_downstream) ard_entry;
89 };
90 
91 enum appl_varbind_state {
92 	APPL_VBSTATE_MUSTFILL,
93 	APPL_VBSTATE_NEW,
94 	APPL_VBSTATE_PENDING,
95 	APPL_VBSTATE_DONE
96 };
97 
98 struct appl_varbind_internal {
99 	enum appl_varbind_state avi_state;
100 	struct appl_varbind avi_varbind;
101 	struct appl_region *avi_region;
102 	int16_t avi_index;
103 	struct appl_request_upstream *avi_request_upstream;
104 	struct appl_request_downstream *avi_request_downstream;
105 	struct appl_varbind_internal *avi_next;
106 	struct appl_varbind_internal *avi_sub;
107 };
108 
109 /* SNMP-TARGET-MIB (RFC 3413) */
110 struct snmp_target_mib {
111 	uint32_t		snmp_unavailablecontexts;
112 	uint32_t		snmp_unknowncontexts;
113 } snmp_target_mib;
114 
115 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t,
116     struct ber_oid *, int, int, struct appl_backend *);
117 void appl_region_free(struct appl_context *, struct appl_region *);
118 struct appl_region *appl_region_find(struct appl_context *,
119     const struct ber_oid *);
120 struct appl_region *appl_region_next(struct appl_context *,
121     struct ber_oid *, struct appl_region *);
122 void appl_request_upstream_free(struct appl_request_upstream *);
123 void appl_request_downstream_free(struct appl_request_downstream *);
124 void appl_request_upstream_resolve(struct appl_request_upstream *);
125 void appl_request_downstream_send(struct appl_request_downstream *);
126 void appl_request_downstream_timeout(int, short, void *);
127 void appl_request_upstream_reply(struct appl_request_upstream *);
128 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int,
129     const char **);
130 int appl_varbind_backend(struct appl_varbind_internal *);
131 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
132 void appl_report(struct snmp_message *, int32_t, struct ber_oid *,
133     struct ber_element *);
134 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t,
135     uint16_t, struct appl_varbind *);
136 void ober_oid_nextsibling(struct ber_oid *);
137 
138 int appl_region_cmp(struct appl_region *, struct appl_region *);
139 int appl_request_cmp(struct appl_request_downstream *,
140     struct appl_request_downstream *);
141 
142 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
143 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry,
144     appl_request_cmp);
145 
146 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name)
147 
148 void
149 appl_init(void)
150 {
151 	appl_blocklist_init();
152 	appl_legacy_init();
153 }
154 
155 void
156 appl_shutdown(void)
157 {
158 	struct appl_context *ctx, *tctx;
159 
160 	appl_blocklist_shutdown();
161 	appl_legacy_shutdown();
162 
163 	TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
164 		assert(RB_EMPTY(&(ctx->ac_regions)));
165 		TAILQ_REMOVE(&contexts, ctx, ac_entries);
166 		free(ctx);
167 	}
168 }
169 
170 static struct appl_context *
171 appl_context(const char *name, int create)
172 {
173 	struct appl_context *ctx;
174 
175 	if (name == NULL)
176 		name = "";
177 
178 	if (strlen(name) > APPL_CONTEXTNAME_MAX) {
179 		errno = EINVAL;
180 		return NULL;
181 	}
182 
183 	TAILQ_FOREACH(ctx, &contexts, ac_entries) {
184 		if (strcmp(name, ctx->ac_name) == 0)
185 			return ctx;
186 	}
187 
188 	/* Always allow the default namespace */
189 	if (!create && name[0] != '\0') {
190 		errno = ENOENT;
191 		return NULL;
192 	}
193 
194 	if ((ctx = malloc(sizeof(*ctx))) == NULL)
195 		return NULL;
196 
197 	strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name));
198 	RB_INIT(&(ctx->ac_regions));
199 
200 	TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries);
201 	return ctx;
202 }
203 
204 enum appl_error
205 appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority,
206     struct ber_oid *oid, int instance, int subtree,
207     struct appl_backend *backend)
208 {
209 	struct appl_region *region = NULL, *nregion, search;
210 	char oidbuf[1024], regionbuf[1024], subidbuf[11];
211 	size_t i;
212 
213 	/*
214 	 * Don't allow overlap when subtree flag is set.
215 	 * This allows us to keep control of certain regions like system.
216 	 */
217 	region = appl_region_find(ctx, oid);
218 	if (region != NULL && region->ar_subtree)
219 		goto overlap;
220 
221 	search.ar_oid = *oid;
222 	region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
223 	if (region != NULL && region->ar_subtree && (
224 	    appl_region_cmp(&search, region) == 0 ||
225 	    appl_region_cmp(&search, region) == -2))
226 		goto overlap;
227 
228 	/* Don't use smi_oid2string, because appl_register can't use it */
229 	oidbuf[0] = '\0';
230 	for (i = 0; i < oid->bo_n; i++) {
231 		if (i != 0)
232 			strlcat(oidbuf, ".", sizeof(oidbuf));
233 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
234 		    oid->bo_id[i]);
235 		strlcat(oidbuf, subidbuf, sizeof(oidbuf));
236 	}
237 	if ((nregion = malloc(sizeof(*nregion))) == NULL) {
238 		log_warn("%s: Can't register %s: Processing error",
239 		    backend->ab_name, oidbuf);
240 		return APPL_ERROR_PROCESSINGERROR;
241 	}
242 	nregion->ar_oid = *oid;
243 	nregion->ar_priority = priority;
244 	nregion->ar_timeout = timeout;
245 	nregion->ar_instance = instance;
246 	nregion->ar_subtree = subtree;
247 	nregion->ar_backend = backend;
248 	nregion->ar_next = NULL;
249 
250 	region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
251 	if (region == NULL)
252 		return APPL_ERROR_NOERROR;
253 
254 	if (region->ar_priority == priority)
255 		goto duplicate;
256 	if (region->ar_priority > priority) {
257 		RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
258 		RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
259 		nregion->ar_next = region;
260 		return APPL_ERROR_NOERROR;
261 	}
262 
263 	while (region->ar_next != NULL &&
264 	    region->ar_next->ar_priority < priority)
265 		region = region->ar_next;
266 	if (region->ar_next != NULL && region->ar_next->ar_priority == priority)
267 		goto duplicate;
268 	nregion->ar_next = region->ar_next;
269 	region->ar_next = nregion;
270 
271 	return APPL_ERROR_NOERROR;
272  duplicate:
273 	free(nregion);
274 	log_info("%s: %s priority %"PRId8": Duplicate registration",
275 	    backend->ab_name, oidbuf, priority);
276 	return APPL_ERROR_DUPLICATEREGISTRATION;
277  overlap:
278 	regionbuf[0] = '\0';
279 	for (i = 0; i < region->ar_oid.bo_n; i++) {
280 		if (i != 0)
281 			strlcat(regionbuf, ".", sizeof(regionbuf));
282 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
283 		    region->ar_oid.bo_id[i]);
284 		strlcat(regionbuf, subidbuf, sizeof(regionbuf));
285 	}
286 	log_info("%s: %s overlaps with %s: Request denied",
287 	    backend->ab_name, oidbuf, regionbuf);
288 	return APPL_ERROR_REQUESTDENIED;
289 }
290 
291 /* Name from RFC 2741 section 6.2.3 */
292 enum appl_error
293 appl_register(const char *ctxname, uint32_t timeout, uint8_t priority,
294     struct ber_oid *oid, int instance, int subtree, uint8_t range_subid,
295     uint32_t upper_bound, struct appl_backend *backend)
296 {
297 	struct appl_context *ctx;
298 	struct appl_region *region, search;
299 	char oidbuf[1024], subidbuf[11];
300 	enum appl_error error;
301 	size_t i;
302 	uint32_t lower_bound;
303 
304 	oidbuf[0] = '\0';
305 	/* smi_oid2string can't do ranges */
306 	for (i = 0; i < oid->bo_n; i++) {
307 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
308 		if (i != 0)
309 			strlcat(oidbuf, ".", sizeof(oidbuf));
310 		if (range_subid == i + 1) {
311 			strlcat(oidbuf, "[", sizeof(oidbuf));
312 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
313 			strlcat(oidbuf, "-", sizeof(oidbuf));
314 			snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
315 			    upper_bound);
316 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
317 			strlcat(oidbuf, "]", sizeof(oidbuf));
318 		} else
319 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
320 	}
321 
322 	if (ctxname == NULL)
323 		ctxname = "";
324 	log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") "
325 	    "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf,
326 	     instance ? "(instance)" : "", ctxname, priority,
327 	     timeout/100, timeout % 100);
328 
329 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
330 		if (errno == ENOMEM) {
331 			log_warn("%s: Can't register %s: Processing error",
332 			    backend->ab_name, oidbuf);
333 			return APPL_ERROR_PROCESSINGERROR;
334 		}
335 		log_info("%s: Can't register %s: Unsupported context \"%s\"",
336 		    backend->ab_name, oidbuf, ctxname);
337 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
338 	}
339 	/* Default timeouts should be handled by backend */
340 	if (timeout == 0)
341 		fatalx("%s: Timeout can't be 0", __func__);
342 	if (priority == 0) {
343 		log_warnx("%s: Can't register %s: priority can't be 0",
344 		    backend->ab_name, oidbuf);
345 		return APPL_ERROR_PARSEERROR;
346 	}
347 	if (range_subid > oid->bo_n) {
348 		log_warnx("%s: Can't register %s: range_subid too large",
349 		    backend->ab_name, oidbuf);
350 		return APPL_ERROR_PARSEERROR;
351 	}
352 	if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
353 		log_warnx("%s: Can't register %s: upper bound smaller or equal "
354 		    "to range_subid", backend->ab_name, oidbuf);
355 		return APPL_ERROR_PARSEERROR;
356 	}
357 	if (range_subid != 0)
358 		lower_bound = oid->bo_id[range_subid];
359 
360 	if (range_subid == 0)
361 		return appl_region(ctx, timeout, priority, oid, instance,
362 		    subtree, backend);
363 
364 	do {
365 		if ((error = appl_region(ctx, timeout, priority, oid, instance,
366 		    subtree, backend)) != APPL_ERROR_NOERROR)
367 			goto fail;
368 	} while (oid->bo_id[range_subid] != upper_bound);
369 	if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree,
370 	    backend)) != APPL_ERROR_NOERROR)
371 		goto fail;
372 
373 	return APPL_ERROR_NOERROR;
374  fail:
375 	search.ar_oid = *oid;
376 	if (search.ar_oid.bo_id[range_subid] == lower_bound)
377 		return error;
378 
379 	for (search.ar_oid.bo_id[range_subid]--;
380 	    search.ar_oid.bo_id[range_subid] != lower_bound;
381 	    search.ar_oid.bo_id[range_subid]--) {
382 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
383 		while (region->ar_priority != priority)
384 			region = region->ar_next;
385 		appl_region_free(ctx, region);
386 	}
387 	region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
388 	while (region->ar_priority != priority)
389 		region = region->ar_next;
390 	appl_region_free(ctx, region);
391 	return error;
392 }
393 
394 /* Name from RFC 2741 section 6.2.4 */
395 enum appl_error
396 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid,
397     uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend)
398 {
399 	struct appl_region *region, search;
400 	struct appl_context *ctx;
401 	char oidbuf[1024], subidbuf[11];
402 	size_t i;
403 
404 	oidbuf[0] = '\0';
405 	for (i = 0; i < oid->bo_n; i++) {
406 		snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
407 		if (i != 0)
408 			strlcat(oidbuf, ".", sizeof(oidbuf));
409 		if (range_subid == i + 1) {
410 			strlcat(oidbuf, "[", sizeof(oidbuf));
411 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
412 			strlcat(oidbuf, "-", sizeof(oidbuf));
413 			snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
414 			    upper_bound);
415 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
416 			strlcat(oidbuf, "]", sizeof(oidbuf));
417 		} else
418 			strlcat(oidbuf, subidbuf, sizeof(oidbuf));
419 	}
420 
421 	if (ctxname == NULL)
422 		ctxname = "";
423 	log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")",
424 	     backend->ab_name, oidbuf,ctxname, priority);
425 
426 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
427 		if (errno == ENOMEM) {
428 			log_warn("%s: Can't unregister %s: Processing error",
429 			    backend->ab_name, oidbuf);
430 			return APPL_ERROR_PROCESSINGERROR;
431 		}
432 		log_info("%s: Can't unregister %s: Unsupported context \"%s\"",
433 		    backend->ab_name, oidbuf, ctxname);
434 		return APPL_ERROR_UNSUPPORTEDCONTEXT;
435 	}
436 
437 	if (priority == 0) {
438 		log_warnx("%s: Can't unregister %s: priority can't be 0",
439 		    backend->ab_name, oidbuf);
440 		return APPL_ERROR_PARSEERROR;
441 	}
442 
443 	if (range_subid > oid->bo_n) {
444 		log_warnx("%s: Can't unregiser %s: range_subid too large",
445 		    backend->ab_name, oidbuf);
446 		return APPL_ERROR_PARSEERROR;
447 	}
448 	if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
449 		log_warnx("%s: Can't unregister %s: upper bound smaller or "
450 		    "equal to range_subid", backend->ab_name, oidbuf);
451 		return APPL_ERROR_PARSEERROR;
452 	}
453 
454 	search.ar_oid = *oid;
455 	while (range_subid != 0 &&
456 	    search.ar_oid.bo_id[range_subid] != upper_bound) {
457 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
458 		while (region != NULL && region->ar_priority < priority)
459 			region = region->ar_next;
460 		if (region == NULL || region->ar_priority != priority) {
461 			log_warnx("%s: Can't unregister %s: region not found",
462 			    backend->ab_name, oidbuf);
463 			return APPL_ERROR_UNKNOWNREGISTRATION;
464 		}
465 		if (region->ar_backend != backend) {
466 			log_warnx("%s: Can't unregister %s: region not owned "
467 			    "by backend", backend->ab_name, oidbuf);
468 			return APPL_ERROR_UNKNOWNREGISTRATION;
469 		}
470 	}
471 	region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
472 	while (region != NULL && region->ar_priority < priority)
473 		region = region->ar_next;
474 	if (region == NULL || region->ar_priority != priority) {
475 		log_warnx("%s: Can't unregister %s: region not found",
476 		    backend->ab_name, oidbuf);
477 		return APPL_ERROR_UNKNOWNREGISTRATION;
478 	}
479 	if (region->ar_backend != backend) {
480 		log_warnx("%s: Can't unregister %s: region not owned "
481 		    "by backend", backend->ab_name, oidbuf);
482 		return APPL_ERROR_UNKNOWNREGISTRATION;
483 	}
484 
485 	search.ar_oid = *oid;
486 	while (range_subid != 0 &&
487 	    search.ar_oid.bo_id[range_subid] != upper_bound) {
488 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
489 		while (region != NULL && region->ar_priority != priority)
490 			region = region->ar_next;
491 		appl_region_free(ctx, region);
492 	}
493 	region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
494 	while (region != NULL && region->ar_priority != priority)
495 		region = region->ar_next;
496 	appl_region_free(ctx, region);
497 
498 	return APPL_ERROR_NOERROR;
499 }
500 
501 void
502 appl_region_free(struct appl_context *ctx, struct appl_region *region)
503 {
504 	struct appl_region *pregion;
505 
506 	pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region);
507 
508 	if (pregion == region) {
509 		RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
510 		if (region->ar_next != NULL)
511 			RB_INSERT(appl_regions, &(ctx->ac_regions),
512 			    region->ar_next);
513 	} else {
514 		while (pregion->ar_next != region)
515 			pregion = pregion->ar_next;
516 		pregion->ar_next = region->ar_next;
517 	}
518 
519 	free(region);
520 }
521 
522 /* backend is owned by the sub-application, just release application.c stuff */
523 void
524 appl_close(struct appl_backend *backend)
525 {
526 	struct appl_context *ctx;
527 	struct appl_region *region, *tregion, *nregion;
528 	struct appl_request_downstream *request, *trequest;
529 
530 	TAILQ_FOREACH(ctx, &contexts, ac_entries) {
531 		RB_FOREACH_SAFE(region, appl_regions,
532 		    &(ctx->ac_regions), tregion) {
533 			while (region != NULL) {
534 				nregion = region->ar_next;
535 				if (region->ar_backend == backend)
536 					appl_region_free(ctx, region);
537 				region = nregion;
538 			}
539 		}
540 	}
541 
542 	RB_FOREACH_SAFE(request, appl_requests,
543 	    &(backend->ab_requests), trequest)
544 	appl_request_downstream_free(request);
545 }
546 
547 struct appl_region *
548 appl_region_find(struct appl_context *ctx,
549     const struct ber_oid *oid)
550 {
551 	struct appl_region *region, search;
552 
553 	search.ar_oid = *oid;
554 	while (search.ar_oid.bo_n > 0) {
555 		region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
556 		if (region != NULL)
557 			return region;
558 		search.ar_oid.bo_n--;
559 	}
560 	return NULL;
561 }
562 
563 struct appl_region *
564 appl_region_next(struct appl_context *ctx, struct ber_oid *oid,
565     struct appl_region *cregion)
566 {
567 	struct appl_region search, *nregion, *pregion;
568 	int cmp;
569 
570 	search.ar_oid = *oid;
571 	nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
572 
573 	if (cregion == nregion)
574 		nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion);
575 	/* Past last element in tree, we might still have a parent */
576 	if (nregion == NULL) {
577 		search.ar_oid = cregion->ar_oid;
578 		search.ar_oid.bo_n--;
579 		return appl_region_find(ctx, &(search.ar_oid));
580 	}
581 	cmp = appl_region_cmp(cregion, nregion);
582 	if (cmp >= 0)
583 		fatalx("%s: wrong OID order", __func__);
584 	/* Direct descendant */
585 	if (cmp == -2)
586 		return nregion;
587 
588 	/* cmp == -1 */
589 	search.ar_oid = cregion->ar_oid;
590 	/* Find direct next sibling */
591 	ober_oid_nextsibling(&(search.ar_oid));
592 	if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0)
593 		return nregion;
594 	/* Sibling gaps go to parent, or end end at border */
595 	search.ar_oid = cregion->ar_oid;
596 	search.ar_oid.bo_n--;
597 	pregion = appl_region_find(ctx, &(search.ar_oid));
598 
599 	return pregion != NULL ? pregion : nregion;
600 }
601 
602 /* Name from RFC 3413 section 3.2 */
603 void
604 appl_processpdu(struct snmp_message *statereference, const char *ctxname,
605     enum snmp_version pduversion, struct ber_element *pdu)
606 {
607 	struct appl_context *ctx;
608 	struct appl_request_upstream *ureq;
609 	struct ber_oid oid;
610 	struct ber_element *value, *varbind, *varbindlist;
611 	long long nonrepeaters, maxrepetitions;
612 	static uint32_t transactionid;
613 	int32_t requestid;
614 	size_t i, varbindlen = 0, repeaterlen;
615 
616 	/* pdu must be ASN.1 validated in snmpe.c */
617 	(void) ober_scanf_elements(pdu, "{diie", &requestid, &nonrepeaters,
618 	    &maxrepetitions, &varbindlist);
619 
620 	/* RFC 3413, section 3.2, processPDU, item 5, final bullet */
621 	if ((ctx = appl_context(ctxname, 0)) == NULL) {
622 		oid = BER_OID(MIB_snmpUnknownContexts, 0);
623 		snmp_target_mib.snmp_unknowncontexts++;
624 		if ((value = ober_add_integer(NULL,
625 		    snmp_target_mib.snmp_unknowncontexts)) == NULL)
626 			fatal("ober_add_integer");
627 		appl_report(statereference, requestid, &oid, value);
628 		return;
629 	}
630 
631 	if ((ureq = malloc(sizeof(*ureq))) == NULL)
632 		fatal("malloc");
633 
634 	ureq->aru_ctx = ctx;
635 	ureq->aru_statereference = statereference;
636 	ureq->aru_transactionid = transactionid++;
637 	ureq->aru_requestid = requestid;
638 	ureq->aru_error = APPL_ERROR_NOERROR;
639 	ureq->aru_index = 0;
640 	ureq->aru_nonrepeaters = nonrepeaters;
641 	ureq->aru_maxrepetitions = maxrepetitions;
642 	ureq->aru_varbindlen = 0;
643 	ureq->aru_locked = 0;
644 	ureq->aru_pduversion = pduversion;
645 	ureq->aru_pdu = pdu;
646 
647 	varbind = varbindlist->be_sub;
648 	for (; varbind != NULL; varbind = varbind->be_next)
649 		varbindlen++;
650 
651 	repeaterlen = varbindlen - nonrepeaters;
652 	if (pdu->be_type == SNMP_C_GETBULKREQ)
653 		ureq->aru_varbindlen = nonrepeaters +
654 		    (repeaterlen * maxrepetitions);
655 	else
656 		ureq->aru_varbindlen = varbindlen;
657 	if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen,
658 	    sizeof(*ureq->aru_vblist))) == NULL)
659 		fatal("malloc");
660 
661 	varbind = varbindlist->be_sub;
662 	/* Use aru_varbindlen in case maxrepetitions == 0 */
663 	for (i = 0; i < ureq->aru_varbindlen; i++) {
664 		ureq->aru_vblist[i].avi_request_upstream = ureq;
665 		ureq->aru_vblist[i].avi_index = i + 1;
666 		ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW;
667 		/* This can only happen with bulkreq */
668 		if (varbind == NULL) {
669 			ureq->aru_vblist[i - repeaterlen].avi_sub =
670 			    &(ureq->aru_vblist[i]);
671 			ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL;
672 			continue;
673 		}
674 		ober_get_oid(varbind->be_sub,
675 		    &(ureq->aru_vblist[i].avi_varbind.av_oid));
676 		if (i + 1 < ureq->aru_varbindlen) {
677 			ureq->aru_vblist[i].avi_next =
678 			    &(ureq->aru_vblist[i + 1]);
679 			ureq->aru_vblist[i].avi_varbind.av_next =
680 			    &(ureq->aru_vblist[i + 1].avi_varbind);
681 		} else {
682 			ureq->aru_vblist[i].avi_next = NULL;
683 			ureq->aru_vblist[i].avi_varbind.av_next = NULL;
684 		}
685 		varbind = varbind->be_next;
686 	}
687 
688 	appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters,
689 	    maxrepetitions, &(ureq->aru_vblist[0].avi_varbind));
690 
691 	appl_request_upstream_resolve(ureq);
692 }
693 
694 void
695 appl_request_upstream_free(struct appl_request_upstream *ureq)
696 {
697 	size_t i;
698 	struct appl_varbind_internal *vb;
699 
700 	if (ureq == NULL)
701 		return;
702 
703 	for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) {
704 		vb = &(ureq->aru_vblist[i]);
705 		ober_free_elements(vb->avi_varbind.av_value);
706 		appl_request_downstream_free(vb->avi_request_downstream);
707 	}
708 	free(ureq->aru_vblist);
709 
710 	assert(ureq->aru_statereference == NULL);
711 
712 	free(ureq);
713 }
714 
715 void
716 appl_request_downstream_free(struct appl_request_downstream *dreq)
717 {
718 	struct appl_varbind_internal *vb;
719 
720 	if (dreq == NULL)
721 		return;
722 
723 	RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq);
724 	evtimer_del(&(dreq->ard_timer));
725 
726 	for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next)
727 		vb->avi_request_downstream = NULL;
728 
729 	free(dreq);
730 }
731 
732 void
733 appl_request_upstream_resolve(struct appl_request_upstream *ureq)
734 {
735 	struct appl_varbind_internal *vb, *lvb, *tvb;
736 	struct appl_request_downstream *dreq;
737 	struct appl_region *region, *lregion;
738 	struct timeval tv;
739 	int done;
740 	size_t i;
741 	int32_t maxrepetitions;
742 	int32_t timeout;
743 
744 	if (ureq->aru_locked)
745 		return;
746 	ureq->aru_locked = 1;
747 
748 	if (ureq->aru_pdu->be_type == SNMP_C_SETREQ) {
749 		ureq->aru_error = APPL_ERROR_NOTWRITABLE;
750 		ureq->aru_index = 1;
751 		appl_request_upstream_reply(ureq);
752 		return;
753 	}
754 
755  next:
756 	dreq = NULL;
757 	lvb = NULL;
758 	done = 1;
759 	timeout = 0;
760 
761 	if (ureq->aru_error != APPL_ERROR_NOERROR) {
762 		appl_request_upstream_reply(ureq);
763 		return;
764 	}
765 	for (i = 0; i < ureq->aru_varbindlen; i++) {
766 		vb = &(ureq->aru_vblist[i]);
767 
768 		switch (vb->avi_state) {
769 		case APPL_VBSTATE_MUSTFILL:
770 		case APPL_VBSTATE_PENDING:
771 			done = 0;
772 			continue;
773 		case APPL_VBSTATE_DONE:
774 			continue;
775 		case APPL_VBSTATE_NEW:
776 			break;
777 		}
778 		if (appl_varbind_backend(vb) == -1)
779 			fatal("appl_varbind_backend");
780 		if (vb->avi_state != APPL_VBSTATE_DONE)
781 			done = 0;
782 	}
783 
784 	for (i = 0; i < ureq->aru_varbindlen; i++) {
785 		vb = &(ureq->aru_vblist[i]);
786 
787 		if (vb->avi_state != APPL_VBSTATE_NEW)
788 			continue;
789 
790 		vb = &(ureq->aru_vblist[i]);
791 		region = vb->avi_region;
792 		lregion = lvb != NULL ? lvb->avi_region : NULL;
793 		if (lvb != NULL && region->ar_backend != lregion->ar_backend)
794 			continue;
795 
796 		vb->avi_varbind.av_next = NULL;
797 		vb->avi_next = NULL;
798 		tvb = vb;
799 		for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub)
800 			maxrepetitions++;
801 		if (dreq == NULL) {
802 			if ((dreq = malloc(sizeof(*dreq))) == NULL)
803 				fatal("malloc");
804 
805 			dreq->ard_request = ureq;
806 			dreq->ard_vblist = vb;
807 			dreq->ard_backend = vb->avi_region->ar_backend;
808 			dreq->ard_retries = dreq->ard_backend->ab_retries;
809 			dreq->ard_requesttype = ureq->aru_pdu->be_type;
810 			/*
811 			 * We don't yet fully handle bulkrequest responses.
812 			 * It's completely valid to map onto getrequest.
813 			 * maxrepetitions calculated in preparation of support.
814 			 */
815 			if (dreq->ard_requesttype == SNMP_C_GETBULKREQ &&
816 			    dreq->ard_backend->ab_fn->ab_getbulk == NULL)
817 				dreq->ard_requesttype = SNMP_C_GETNEXTREQ;
818 			/*
819 			 * If first varbind is nonrepeater, set maxrepetitions
820 			 * to 0, so that the next varbind with
821 			 * maxrepetitions > 1 determines length.
822 			 */
823 			if (maxrepetitions == 1) {
824 				dreq->ard_maxrepetitions = 0;
825 				dreq->ard_nonrepeaters = 1;
826 			} else {
827 				dreq->ard_maxrepetitions = maxrepetitions;
828 				dreq->ard_nonrepeaters = 0;
829 			}
830 			do {
831 				dreq->ard_requestid = arc4random();
832 			} while (RB_INSERT(appl_requests,
833 			    &(dreq->ard_backend->ab_requests), dreq) != NULL);
834 			lvb = vb;
835 		/* avi_sub isn't set on !bulkrequest, so we always enter here */
836 		} else if (maxrepetitions == 1) {
837 			dreq->ard_nonrepeaters++;
838 			vb->avi_varbind.av_next =
839 			    &(dreq->ard_vblist->avi_varbind);
840 			vb->avi_next = dreq->ard_vblist;
841 			dreq->ard_vblist = vb;
842 		} else {
843 			lvb->avi_varbind.av_next = &(vb->avi_varbind);
844 			lvb->avi_next = vb;
845 			/* RFC 2741 section 7.2.1.3:
846 			 * The value of g.max_repetitions in the GetBulk-PDU may
847 			 * be less than (but not greater than) the value in the
848 			 * original request PDU.
849 			 */
850 			if (dreq->ard_maxrepetitions > maxrepetitions ||
851 			    dreq->ard_maxrepetitions == 0)
852 				dreq->ard_maxrepetitions = maxrepetitions;
853 			lvb = vb;
854 		}
855 		vb->avi_request_downstream = dreq;
856 		vb->avi_state = APPL_VBSTATE_PENDING;
857 		if (region->ar_timeout > timeout)
858 			timeout = region->ar_timeout;
859 	}
860 
861 	if (dreq == NULL) {
862 		ureq->aru_locked = 0;
863 		if (done)
864 			appl_request_upstream_reply(ureq);
865 		return;
866 	}
867 
868 	tv.tv_sec = timeout / 100;
869 	tv.tv_usec = (timeout % 100) * 10000;
870 	evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq);
871 	evtimer_add(&(dreq->ard_timer), &tv);
872 
873 	appl_request_downstream_send(dreq);
874 	goto next;
875 }
876 
877 void
878 appl_request_downstream_send(struct appl_request_downstream *dreq)
879 {
880 
881 	appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype,
882 	    dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind));
883 
884 	if (dreq->ard_requesttype == SNMP_C_GETREQ) {
885 		dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend,
886 		    dreq->ard_request->aru_transactionid,
887 		    dreq->ard_requestid,
888 		    APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
889 		    &(dreq->ard_vblist->avi_varbind));
890 	} else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) {
891 		dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend,
892 		    dreq->ard_request->aru_transactionid,
893 		    dreq->ard_requestid,
894 		    APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
895 		    &(dreq->ard_vblist->avi_varbind));
896 	}
897 }
898 
899 void
900 appl_request_downstream_timeout(__unused int fd, __unused short event,
901     void *cookie)
902 {
903 	struct appl_request_downstream *dreq = cookie;
904 
905 	log_info("%s: %"PRIu32" timed out%s",
906 	    dreq->ard_backend->ab_name, dreq->ard_requestid,
907 	    dreq->ard_retries > 0 ? ": retrying" : "");
908 	if (dreq->ard_retries > 0) {
909 		dreq->ard_retries--;
910 		appl_request_downstream_send(dreq);
911 	} else
912 		appl_response(dreq->ard_backend, dreq->ard_requestid,
913 		    APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind));
914 }
915 
916 void
917 appl_request_upstream_reply(struct appl_request_upstream *ureq)
918 {
919 	struct ber_element *varbindlist = NULL, *varbind = NULL, *value;
920 	struct appl_varbind_internal *vb;
921 	size_t i, repvarbinds, varbindlen;
922 	ssize_t match = -1;
923 
924 	varbindlen = ureq->aru_varbindlen;
925 
926 	if (ureq->aru_pduversion == SNMP_V1) {
927 		/* RFC 3584 section 4.2.2.2 Map exceptions */
928 		for (i = 0; i < varbindlen; i++) {
929 			vb = &(ureq->aru_vblist[i]);
930 			value = vb->avi_varbind.av_value;
931 			if (value != NULL &&
932 			    value->be_class == BER_CLASS_CONTEXT)
933 				appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME);
934 		}
935 		/* RFC 3584 section 4.4 Map errors */
936 		switch (ureq->aru_error) {
937 		case APPL_ERROR_WRONGVALUE:
938 		case APPL_ERROR_WRONGENCODING:
939 		case APPL_ERROR_WRONGTYPE:
940 		case APPL_ERROR_WRONGLENGTH:
941 		case APPL_ERROR_INCONSISTENTVALUE:
942 			ureq->aru_error = APPL_ERROR_BADVALUE;
943 			break;
944 		case APPL_ERROR_NOACCESS:
945 		case APPL_ERROR_NOTWRITABLE:
946 		case APPL_ERROR_NOCREATION:
947 		case APPL_ERROR_INCONSISTENTNAME:
948 		case APPL_ERROR_AUTHORIZATIONERROR:
949 			ureq->aru_error = APPL_ERROR_NOSUCHNAME;
950 			break;
951 		case APPL_ERROR_RESOURCEUNAVAILABLE:
952 		case APPL_ERROR_COMMITFAILED:
953 		case APPL_ERROR_UNDOFAILED:
954 			ureq->aru_error = APPL_ERROR_GENERR;
955 			break;
956 		default:
957 			break;
958 		}
959 	}
960 	/* RFC 3416 section 4.2.{1,2,3} reset original varbinds */
961 	if (ureq->aru_error != APPL_ERROR_NOERROR) {
962 		ober_scanf_elements(ureq->aru_pdu, "{SSS{e", &varbind);
963 		for (varbindlen = 0; varbind != NULL;
964 		    varbindlen++, varbind = varbind->be_next) {
965 			vb = &(ureq->aru_vblist[varbindlen]);
966 			ober_get_oid(varbind->be_sub,
967 			    &(vb->avi_varbind.av_oid));
968 			ober_free_elements(vb->avi_varbind.av_value);
969 			vb->avi_varbind.av_value = ober_add_null(NULL);;
970 		}
971 	/* RFC 3416 section 4.2.3: Strip excessive EOMV */
972 	} else if (ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ) {
973 		repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) /
974 		    ureq->aru_maxrepetitions;
975 		for (i = ureq->aru_nonrepeaters;
976 		    i < ureq->aru_varbindlen - repvarbinds; i++) {
977 			value = ureq->aru_vblist[i].avi_varbind.av_value;
978 			if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 &&
979 			    value->be_class == BER_CLASS_CONTEXT &&
980 			    value->be_type == APPL_EXC_ENDOFMIBVIEW) {
981 				if (match != -1)
982 					break;
983 				match = i;
984 			}
985 			if (value->be_class != BER_CLASS_CONTEXT ||
986 			    value->be_type != APPL_EXC_ENDOFMIBVIEW)
987 				match = -1;
988 		}
989 		if (match != -1)
990 			varbindlen = match + repvarbinds;
991 	}
992 
993 	for (i = 0; i < varbindlen; i++) {
994 		vb = &(ureq->aru_vblist[i]);
995 		vb->avi_varbind.av_next =
996 		    &(ureq->aru_vblist[i + 1].avi_varbind);
997 	}
998 
999 	ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL;
1000 	appl_pdu_log(NULL, SNMP_C_RESPONSE, ureq->aru_requestid,
1001 	    ureq->aru_error, ureq->aru_index,
1002 	    &(ureq->aru_vblist[0].avi_varbind));
1003 
1004 	for (i = 0; i < varbindlen; i++) {
1005 		varbind = ober_printf_elements(varbind, "{Oe}",
1006 		    &(ureq->aru_vblist[i].avi_varbind.av_oid),
1007 		    ureq->aru_vblist[i].avi_varbind.av_value);
1008 		ureq->aru_vblist[i].avi_varbind.av_value = NULL;
1009 		if (varbind == NULL)
1010 			fatal("ober_printf_elements");
1011 		if (varbindlist == NULL)
1012 			varbindlist = varbind;
1013 	}
1014 
1015 	snmpe_send(ureq->aru_statereference, SNMP_C_RESPONSE,
1016 	    ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist);
1017 	ureq->aru_statereference = NULL;
1018 	appl_request_upstream_free(ureq);
1019 }
1020 
1021 /* Name from RFC 2741 section 6.2.16 */
1022 void
1023 appl_response(struct appl_backend *backend, int32_t requestid,
1024     enum appl_error error, int16_t index, struct appl_varbind *vblist)
1025 {
1026 	struct appl_request_downstream *dreq, search;
1027 	struct appl_request_upstream *ureq = NULL;
1028 	struct appl_region *nregion;
1029 	const char *errstr;
1030 	char oidbuf[1024];
1031 	enum snmp_pdutype pdutype;
1032 	struct appl_varbind *vb;
1033 	struct appl_varbind_internal *origvb = NULL;
1034 	int invalid = 0;
1035 	int next = 0;
1036 	int32_t i;
1037 
1038 	appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist);
1039 
1040 	search.ard_requestid = requestid;
1041 	dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search);
1042 	if (dreq == NULL) {
1043 		log_debug("%s: %"PRIu32" not outstanding",
1044 		    backend->ab_name, requestid);
1045 		/* Continue to verify validity */
1046 	} else {
1047 		ureq = dreq->ard_request;
1048 		pdutype = ureq->aru_pdu->be_type;
1049 		next = pdutype == SNMP_C_GETNEXTREQ ||
1050 		    pdutype == SNMP_C_GETBULKREQ;
1051 		origvb = dreq->ard_vblist;
1052 	}
1053 
1054 	vb = vblist;
1055 	for (i = 1; vb != NULL; vb = vb->av_next, i++) {
1056                 if (!appl_varbind_valid(vb, origvb == NULL ?
1057 		    NULL : &(origvb->avi_varbind), next,
1058                     error != APPL_ERROR_NOERROR, &errstr)) {
1059 			smi_oid2string(&(vb->av_oid), oidbuf,
1060 			    sizeof(oidbuf), 0);
1061 			log_warnx("%s: %"PRIu32" %s: %s",
1062 			    backend->ab_name, requestid, oidbuf, errstr);
1063 			invalid = 1;
1064 		}
1065 		/* Transfer av_value */
1066 		if (origvb != NULL) {
1067 			if (error != APPL_ERROR_NOERROR && i == index)
1068 				appl_varbind_error(origvb, error);
1069 			origvb->avi_state = APPL_VBSTATE_DONE;
1070 			origvb->avi_varbind.av_oid = vb->av_oid;
1071 			if (vb->av_value != NULL &&
1072 			    vb->av_value->be_class == BER_CLASS_CONTEXT &&
1073 			    vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) {
1074 				nregion = appl_region_next(ureq->aru_ctx,
1075 				    &(vb->av_oid), origvb->avi_region);
1076 				if (nregion != NULL) {
1077 					ober_free_elements(vb->av_value);
1078 					origvb->avi_varbind.av_oid =
1079 					    nregion->ar_oid;
1080 					origvb->avi_varbind.av_include = 1;
1081 					vb->av_value = NULL;
1082 					origvb->avi_state = APPL_VBSTATE_NEW;
1083 				}
1084 			}
1085 			/* RFC 3584 section 4.2.2.1 */
1086 			if (ureq->aru_pduversion == SNMP_V1 &&
1087 			    vb->av_value != NULL &&
1088 			    vb->av_value->be_class == BER_CLASS_APPLICATION &&
1089 			    vb->av_value->be_type == SNMP_COUNTER64) {
1090 				if (ureq->aru_pdu->be_type == SNMP_C_GETREQ) {
1091 					appl_varbind_error(origvb,
1092 					    APPL_ERROR_NOSUCHNAME);
1093 				} else {
1094 					ober_free_elements(vb->av_value);
1095 					vb->av_value = NULL;
1096 					origvb->avi_varbind.av_oid = vb->av_oid;
1097 					origvb->avi_varbind.av_include = 0;
1098 					origvb->avi_state = APPL_VBSTATE_NEW;
1099 				}
1100 			}
1101 			origvb->avi_varbind.av_value = vb->av_value;
1102 			if (origvb->avi_varbind.av_next == NULL &&
1103 			    vb->av_next != NULL) {
1104 				log_warnx("%s: Request %"PRIu32" returned more "
1105 				    "varbinds then requested",
1106 				    backend->ab_name, requestid);
1107 				invalid = 1;
1108 			}
1109 			if (origvb->avi_sub != NULL &&
1110 			    origvb->avi_state == APPL_VBSTATE_DONE) {
1111 				origvb->avi_sub->avi_varbind.av_oid =
1112 				    origvb->avi_varbind.av_oid;
1113 				origvb->avi_sub->avi_state = APPL_VBSTATE_NEW;
1114 			}
1115 			origvb = origvb->avi_next;
1116 		} else {
1117 			ober_free_elements(vb->av_value);
1118 			vb->av_value = NULL;
1119 		}
1120 	}
1121 	if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) {
1122 		log_warnx("Invalid error index");
1123 		invalid = 1;
1124 	}
1125 	if (error == APPL_ERROR_NOERROR && index != 0) {
1126 		log_warnx("error index with no error");
1127 		invalid = 1;
1128 	}
1129 	if (vb == NULL && origvb != NULL) {
1130 		log_warnx("%s: Request %"PRIu32" returned less varbinds then "
1131 		    "requested", backend->ab_name, requestid);
1132 		invalid = 1;
1133 	}
1134 
1135 	if (dreq != NULL) {
1136 		if (invalid)
1137 			appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR);
1138 		appl_request_downstream_free(dreq);
1139 	}
1140 
1141 	if (invalid && backend->ab_fn->ab_close != NULL) {
1142 		log_warnx("%s: Closing: Too many parse errors",
1143 		    backend->ab_name);
1144 		backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR);
1145 	}
1146 
1147 	if (ureq != NULL)
1148 		appl_request_upstream_resolve(ureq);
1149 }
1150 
1151 int
1152 appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request,
1153     int next, int null, const char **errstr)
1154 {
1155 	int cmp;
1156 	int eomv = 0;
1157 
1158 	if (varbind->av_value == NULL) {
1159 		*errstr = "missing value";
1160 		return 0;
1161 	}
1162 	if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) {
1163 		switch (varbind->av_value->be_type) {
1164 		case BER_TYPE_NULL:
1165 			if (null)
1166 				break;
1167 			*errstr = "not expecting null value";
1168 			return 0;
1169 		case BER_TYPE_INTEGER:
1170 		case BER_TYPE_OCTETSTRING:
1171 		case BER_TYPE_OBJECT:
1172 			if (!null)
1173 				break;
1174 			/* FALLTHROUGH */
1175 		default:
1176 			*errstr = "invalid value";
1177 			return 0;
1178 		}
1179 	} else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) {
1180 		switch (varbind->av_value->be_type) {
1181 		case SNMP_T_IPADDR:
1182 		case SNMP_T_COUNTER32:
1183 		case SNMP_T_GAUGE32:
1184 		case SNMP_T_TIMETICKS:
1185 		case SNMP_T_OPAQUE:
1186 		case SNMP_T_COUNTER64:
1187 			if (!null)
1188 				break;
1189 			/* FALLTHROUGH */
1190 		default:
1191 			*errstr = "expecting null value";
1192 			return 0;
1193 		}
1194 	} else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) {
1195 		switch (varbind->av_value->be_type) {
1196 		case APPL_EXC_NOSUCHOBJECT:
1197 			if (next && request != NULL) {
1198 				*errstr = "Unexpected noSuchObject";
1199 				return 0;
1200 			}
1201 			/* FALLTHROUGH */
1202 		case APPL_EXC_NOSUCHINSTANCE:
1203 			if (null) {
1204 				*errstr = "expecting null value";
1205 				return 0;
1206 			}
1207 			if (next && request != NULL) {
1208 				*errstr = "Unexpected noSuchInstance";
1209 				return 0;
1210 			}
1211 			break;
1212 		case APPL_EXC_ENDOFMIBVIEW:
1213 			if (null) {
1214 				*errstr = "expecting null value";
1215 				return 0;
1216 			}
1217 			if (!next && request != NULL) {
1218 				*errstr = "Unexpected endOfMibView";
1219 				return 0;
1220 			}
1221 			eomv = 1;
1222 			break;
1223 		default:
1224 			*errstr = "invalid exception";
1225 			return 0;
1226 		}
1227 	} else {
1228 		*errstr = "invalid value";
1229 		return 0;
1230 	}
1231 
1232 	if (request == NULL)
1233 		return 1;
1234 
1235 	cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid));
1236 	if (next && !eomv) {
1237 		if (request->av_include) {
1238 			if (cmp > 0) {
1239 				*errstr = "oid not incrementing";
1240 				return 0;
1241 			}
1242 		} else {
1243 			if (cmp >= 0) {
1244 				*errstr = "oid not incrementing";
1245 				return 0;
1246 			}
1247 		}
1248 	} else {
1249 		if (cmp != 0) {
1250 			*errstr = "oids not equal";
1251 			return 0;
1252 		}
1253 	}
1254 	return 1;
1255 }
1256 
1257 int
1258 appl_varbind_backend(struct appl_varbind_internal *ivb)
1259 {
1260 	struct appl_request_upstream *ureq = ivb->avi_request_upstream;
1261 	struct appl_region search, *region, *pregion;
1262 	struct appl_varbind *vb = &(ivb->avi_varbind);
1263 	int next, cmp;
1264 
1265 	next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ ||
1266 	    ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ;
1267 
1268 	region = appl_region_find(ureq->aru_ctx, &(vb->av_oid));
1269 	if (region == NULL) {
1270 		if (!next) {
1271 			vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT);
1272 			ivb->avi_state = APPL_VBSTATE_DONE;
1273 			if (vb->av_value == NULL)
1274 				return -1;
1275 			return 0;
1276 		}
1277 		search.ar_oid = vb->av_oid;
1278 		region = RB_NFIND(appl_regions,
1279 		    &(ureq->aru_ctx->ac_regions), &search);
1280 		if (region == NULL)
1281 			goto eomv;
1282 		vb->av_oid = region->ar_oid;
1283 		vb->av_include = 1;
1284 	}
1285 	cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid));
1286 	if (cmp == -2) {
1287 		if (region->ar_instance) {
1288 			if (!next) {
1289 				vb->av_value =
1290 				    appl_exception(APPL_EXC_NOSUCHINSTANCE);
1291 				ivb->avi_state = APPL_VBSTATE_DONE;
1292 				if (vb->av_value == NULL)
1293 					return -1;
1294 				return 0;
1295 			}
1296 			if ((region = appl_region_next(ureq->aru_ctx,
1297 			    &(vb->av_oid), region)) == NULL)
1298 				goto eomv;
1299 			vb->av_oid = region->ar_oid;
1300 			vb->av_include = 1;
1301 		}
1302 	} else if (cmp == 0) {
1303 		if (region->ar_instance && next && !vb->av_include) {
1304 			if ((region = appl_region_next(ureq->aru_ctx,
1305 			    &(vb->av_oid), region)) == NULL)
1306 				goto eomv;
1307 			vb->av_oid = region->ar_oid;
1308 			vb->av_include = 1;
1309 		}
1310 	}
1311 	ivb->avi_region = region;
1312 	if (next) {
1313 		do {
1314 			pregion = region;
1315 			region = appl_region_next(ureq->aru_ctx,
1316 			    &(region->ar_oid), pregion);
1317 		} while (region != NULL &&
1318 		    region->ar_backend == pregion->ar_backend);
1319 		if (region == NULL) {
1320 			vb->av_oid_end = pregion->ar_oid;
1321 			if (pregion->ar_instance &&
1322 			    vb->av_oid_end.bo_n < BER_MAX_OID_LEN)
1323 				vb->av_oid_end.bo_id[vb->av_oid_end.bo_n++] = 0;
1324 			else
1325 				ober_oid_nextsibling(&(vb->av_oid_end));
1326 		} else {
1327 			if (ober_oid_cmp(&(region->ar_oid),
1328 			    &(ivb->avi_region->ar_oid)) == 2)
1329 				vb->av_oid_end = region->ar_oid;
1330 			else {
1331 				vb->av_oid_end = ivb->avi_region->ar_oid;
1332 				ober_oid_nextsibling(&(vb->av_oid_end));
1333 			}
1334 		}
1335 	}
1336 	return 0;
1337 
1338  eomv:
1339 	do {
1340 		ivb->avi_varbind.av_value =
1341 		    appl_exception(APPL_EXC_ENDOFMIBVIEW);
1342 		ivb->avi_state = APPL_VBSTATE_DONE;
1343 		if (ivb->avi_varbind.av_value == NULL)
1344 			return -1;
1345 		if (ivb->avi_sub != NULL)
1346 			ivb->avi_sub->avi_varbind.av_oid =
1347 			    ivb->avi_varbind.av_oid;
1348 		ivb = ivb->avi_sub;
1349 	} while (ivb != NULL);
1350 
1351 	return 0;
1352 }
1353 
1354 void
1355 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error)
1356 {
1357 	struct appl_request_upstream *ureq = avi->avi_request_upstream;
1358 
1359 	if (ureq->aru_error == APPL_ERROR_GENERR)
1360 		return;
1361 	if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR)
1362 		return;
1363 	ureq->aru_error = error;
1364 	ureq->aru_index = avi->avi_index;
1365 }
1366 
1367 void
1368 appl_report(struct snmp_message *msg, int32_t requestid, struct ber_oid *oid,
1369     struct ber_element *value)
1370 {
1371 	struct ber_element *varbind;
1372 
1373 	varbind = ober_printf_elements(NULL, "{Oe}", oid, value);
1374 	if (varbind == NULL) {
1375 		log_warn("%"PRId32": ober_printf_elements", requestid);
1376 		ober_free_elements(value);
1377 		snmp_msgfree(msg);
1378 		return;
1379 	}
1380 
1381 	snmpe_send(msg, SNMP_C_REPORT, requestid, 0, 0, varbind);
1382 }
1383 
1384 struct ber_element *
1385 appl_exception(enum appl_exception type)
1386 {
1387 	struct ber_element *value;
1388 
1389 	if ((value = ober_add_null(NULL)) == NULL) {
1390 		log_warn("malloc");
1391 		return NULL;
1392 	}
1393 	ober_set_header(value, BER_CLASS_CONTEXT, type);
1394 
1395 	return value;
1396 }
1397 
1398 void
1399 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype,
1400     int32_t requestid, uint16_t error, uint16_t index,
1401     struct appl_varbind *vblist)
1402 {
1403 	struct appl_varbind *vb;
1404 	char buf[1024], oidbuf[1024], *str;
1405 	int next;
1406 
1407 	if (log_getverbose() < 2)
1408 		return;
1409 
1410 	next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ);
1411 
1412 	buf[0] = '\0';
1413 	for (vb = vblist; vb != NULL; vb = vb->av_next) {
1414 		strlcat(buf, "{", sizeof(buf));
1415 		strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf,
1416 		    sizeof(oidbuf), 0), sizeof(buf));
1417 		if (next) {
1418 			if (vb->av_include)
1419 				strlcat(buf, "(incl)", sizeof(buf));
1420 			if (vb->av_oid_end.bo_n > 0) {
1421 				strlcat(buf, "-", sizeof(buf));
1422 				strlcat(buf, smi_oid2string(&(vb->av_oid_end),
1423 				    oidbuf, sizeof(oidbuf), 0), sizeof(buf));
1424 			}
1425 		}
1426 		strlcat(buf, ":", sizeof(buf));
1427 		if (vb->av_value != NULL) {
1428 			str = smi_print_element(vb->av_value);
1429 			strlcat(buf, str == NULL ? "???" : str, sizeof(buf));
1430 			free(str);
1431 		} else
1432 			strlcat(buf, "null", sizeof(buf));
1433 		strlcat(buf, "}", sizeof(buf));
1434 	}
1435 	log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}",
1436 	    backend != NULL ? backend->ab_name : "",
1437 	    backend != NULL ? ": " : "",
1438 	    snmpe_pdutype2string(pdutype), requestid, error, index, buf);
1439 }
1440 
1441 void
1442 ober_oid_nextsibling(struct ber_oid *oid)
1443 {
1444 	while (oid->bo_n > 0) {
1445 		oid->bo_id[oid->bo_n - 1]++;
1446 		/* Overflow check */
1447 		if (oid->bo_id[oid->bo_n - 1] != 0)
1448 			return;
1449 		oid->bo_n--;
1450 	}
1451 }
1452 
1453 int
1454 appl_region_cmp(struct appl_region *r1, struct appl_region *r2)
1455 {
1456 	return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid));
1457 }
1458 
1459 int
1460 appl_request_cmp(struct appl_request_downstream *r1,
1461     struct appl_request_downstream *r2)
1462 {
1463 	return r1->ard_requestid < r2->ard_requestid ? -1 :
1464 	    r1->ard_requestid > r2->ard_requestid;
1465 }
1466 
1467 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
1468 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry,
1469     appl_request_cmp);
1470