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