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