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