1145522Sdarrenr/*	$OpenBSD: application.c,v 1.43 2024/02/08 17:34:09 martijn Exp $	*/
2145522Sdarrenr
357126Sguido/*
457126Sguido * Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org>
5145522Sdarrenr *
6145522Sdarrenr * Permission to use, copy, modify, and distribute this software for any
7145522Sdarrenr * purpose with or without fee is hereby granted, provided that the above
8145522Sdarrenr * copyright notice and this permission notice appear in all copies.
9170268Sdarrenr *
1057126Sguido * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1153642Sguido * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1253642Sguido * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1353642Sguido * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1453642Sguido * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1553642Sguido * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16145522Sdarrenr * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17145522Sdarrenr */
18145522Sdarrenr
19145522Sdarrenr#include <sys/queue.h>
2053642Sguido#include <sys/time.h>
2153642Sguido#include <sys/tree.h>
2253642Sguido#include <sys/types.h>
23145522Sdarrenr
2453642Sguido#include <assert.h>
25145522Sdarrenr#include <errno.h>
2653642Sguido#include <event.h>
2753642Sguido#include <inttypes.h>
2853642Sguido#include <stdlib.h>
2953642Sguido#include <stdio.h>
3053642Sguido#include <string.h>
3153642Sguido
3253642Sguido#include "application.h"
3353642Sguido#include "log.h"
34145522Sdarrenr#include "mib.h"
35145522Sdarrenr#include "smi.h"
36145522Sdarrenr#include "snmp.h"
3753642Sguido#include "snmpd.h"
3853642Sguido#include "snmpe.h"
3953642Sguido
4053642Sguido#define OID(...)		(struct ber_oid){ { __VA_ARGS__ },	\
41145522Sdarrenr    (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) }
42145522Sdarrenr
43145522SdarrenrTAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts);
44145522Sdarrenr
45145522Sdarrenrstruct appl_agentcap {
46145522Sdarrenr	struct appl_backend *aa_backend;
47145522Sdarrenr	struct appl_context *aa_context;
48145522Sdarrenr	uint32_t aa_index;
49145522Sdarrenr	struct ber_oid aa_oid;
5053642Sguido	char aa_descr[256];
5153642Sguido	int aa_uptime;
5253642Sguido
53145522Sdarrenr	TAILQ_ENTRY(appl_agentcap) aa_entry;
5453642Sguido};
5553642Sguido
5653642Sguidostruct appl_context {
5753642Sguido	char ac_name[APPL_CONTEXTNAME_MAX + 1];
5853642Sguido
5953642Sguido	RB_HEAD(appl_regions, appl_region) ac_regions;
6053642Sguido	TAILQ_HEAD(, appl_agentcap) ac_agentcaps;
6160857Sdarrenr	int ac_agentcap_lastid;
6260857Sdarrenr	int ac_agentcap_lastchange;
6360857Sdarrenr
64145522Sdarrenr	TAILQ_ENTRY(appl_context) ac_entries;
65145522Sdarrenr};
66145522Sdarrenr
6760857Sdarrenrstruct appl_region {
6860857Sdarrenr	struct ber_oid ar_oid;
6960857Sdarrenr	uint8_t ar_priority;
7060857Sdarrenr	int32_t ar_timeout;
7153642Sguido	int ar_instance;
7253642Sguido	int ar_subtree; /* Claim entire subtree */
7353642Sguido	struct appl_backend *ar_backend;
7453642Sguido	struct appl_region *ar_next; /* Sorted by priority */
7553642Sguido
76145522Sdarrenr	RB_ENTRY(appl_region) ar_entry;
7753642Sguido};
7853642Sguido
7953642Sguidostruct appl_request_upstream {
8053642Sguido	struct appl_context *aru_ctx;
8153642Sguido	struct snmp_message *aru_statereference;
8255929Sguido	enum snmp_pdutype aru_requesttype;
8355929Sguido	enum snmp_pdutype aru_responsetype;
84145522Sdarrenr	int32_t aru_requestid; /* upstream requestid */
8580482Sdarrenr	int32_t aru_transactionid; /* RFC 2741 section 6.1 */
8653642Sguido	uint16_t aru_nonrepeaters;
8753642Sguido	uint16_t aru_maxrepetitions;
8853642Sguido	struct appl_varbind_internal *aru_vblist;
89145522Sdarrenr	size_t aru_varbindlen;
90145522Sdarrenr	enum appl_error aru_error;
9153642Sguido	int16_t aru_index;
9253642Sguido	int aru_locked; /* Prevent recursion through appl_request_send */
9353642Sguido
9453642Sguido	enum snmp_version aru_pduversion;
9553642Sguido};
9653642Sguido
9753642Sguidostruct appl_request_downstream {
98145522Sdarrenr	struct appl_request_upstream *ard_request;
9953642Sguido	struct appl_backend *ard_backend;
100145522Sdarrenr	enum snmp_pdutype ard_requesttype;
101145522Sdarrenr	uint16_t ard_nonrepeaters;
10253642Sguido	uint16_t ard_maxrepetitions;
103145522Sdarrenr	int32_t ard_requestid;
104145522Sdarrenr	uint8_t ard_retries;
105145522Sdarrenr
106145522Sdarrenr	struct appl_varbind_internal *ard_vblist;
107145522Sdarrenr	struct event ard_timer;
10853642Sguido
10953642Sguido	RB_ENTRY(appl_request_downstream) ard_entry;
11053642Sguido};
111145522Sdarrenr
112145522Sdarrenrenum appl_varbind_state {
113145522Sdarrenr	APPL_VBSTATE_MUSTFILL,
114145522Sdarrenr	APPL_VBSTATE_NEW,
115145522Sdarrenr	APPL_VBSTATE_PENDING,
11653642Sguido	APPL_VBSTATE_DONE
11753642Sguido};
11853642Sguido
11953642Sguidostruct appl_varbind_internal {
12053642Sguido	enum appl_varbind_state avi_state;
12153642Sguido	struct appl_varbind avi_varbind;
12253642Sguido	struct appl_region *avi_region;
12353642Sguido	struct ber_oid avi_origid;
12455929Sguido	int16_t avi_index;
12553642Sguido	struct appl_request_upstream *avi_request_upstream;
12653642Sguido	struct appl_request_downstream *avi_request_downstream;
12753642Sguido	struct appl_varbind_internal *avi_next;
12853642Sguido	struct appl_varbind_internal *avi_sub;
12953642Sguido};
13053642Sguido
13153642Sguido/* SNMP-TARGET-MIB (RFC 3413) */
13253642Sguidostruct snmp_target_mib {
13353642Sguido	uint32_t		snmp_unavailablecontexts;
13453642Sguido	uint32_t		snmp_unknowncontexts;
13553642Sguido} snmp_target_mib;
13653642Sguido
13753642Sguidovoid appl_agentcap_free(struct appl_agentcap *);
13853642Sguidoenum appl_error appl_region(struct appl_context *, uint32_t, uint8_t,
13953642Sguido    struct ber_oid *, uint8_t, int, int, struct appl_backend *);
14053642Sguidovoid appl_region_free(struct appl_context *, struct appl_region *);
14153642Sguidoenum appl_error appl_region_unregister_match(struct appl_context *, uint8_t,
14253642Sguido    struct ber_oid *, char *, struct appl_backend *, int);
14353642Sguidostruct appl_region *appl_region_find(struct appl_context *,
14453642Sguido    const struct ber_oid *);
14553642Sguidostruct appl_region *appl_region_next(struct appl_context *,
14653642Sguido    struct ber_oid *, struct appl_region *);
14753642Sguidovoid appl_request_upstream_free(struct appl_request_upstream *);
14853642Sguidovoid appl_request_downstream_free(struct appl_request_downstream *);
14953642Sguidovoid appl_request_upstream_resolve(struct appl_request_upstream *);
15053642Sguidovoid appl_request_downstream_send(struct appl_request_downstream *);
15153642Sguidovoid appl_request_downstream_timeout(int, short, void *);
15253642Sguidovoid appl_request_upstream_reply(struct appl_request_upstream *);
15353642Sguidoint appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *,
15453642Sguido    int, int, int, const char **);
15553642Sguidoint appl_error_valid(enum appl_error, enum snmp_pdutype);
15653642Sguidounsigned int appl_ber_any(struct ber_element *);
15753642Sguidoint appl_varbind_backend(struct appl_varbind_internal *);
15853642Sguidovoid appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
15953642Sguidovoid appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t,
16053642Sguido    uint16_t, struct appl_varbind *);
16153642Sguidovoid ober_oid_nextsibling(struct ber_oid *);
16253642Sguido
16353642Sguidoint appl_region_cmp(struct appl_region *, struct appl_region *);
16453642Sguidoint appl_request_cmp(struct appl_request_downstream *,
16553642Sguido    struct appl_request_downstream *);
16653642Sguido
16753642SguidoRB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
16853642SguidoRB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry,
16953642Sguido    appl_request_cmp);
17053642Sguido
17153642Sguido#define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name)
17253642Sguido
17353642Sguidovoid
17453642Sguidoappl(void)
17553642Sguido{
17653642Sguido	appl_agentx();
17753642Sguido}
17855929Sguido
17953642Sguidovoid
18053642Sguidoappl_init(void)
18153642Sguido{
182145522Sdarrenr	appl_blocklist_init();
18353642Sguido	appl_internal_init();
18453642Sguido	appl_agentx_init();
18553642Sguido}
18653642Sguido
18755929Sguidovoid
18855929Sguidoappl_shutdown(void)
18953642Sguido{
19055929Sguido	struct appl_context *ctx, *tctx;
191145522Sdarrenr
19267614Sdarrenr	appl_blocklist_shutdown();
19355929Sguido	appl_internal_shutdown();
19455929Sguido	appl_agentx_shutdown();
19553642Sguido
196145522Sdarrenr	TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
19755929Sguido		assert(RB_EMPTY(&(ctx->ac_regions)));
198145522Sdarrenr		assert(TAILQ_EMPTY(&(ctx->ac_agentcaps)));
19953642Sguido		TAILQ_REMOVE(&contexts, ctx, ac_entries);
20053642Sguido		free(ctx);
20155929Sguido	}
20255929Sguido}
20355929Sguido
20455929Sguidostruct appl_context *
20555929Sguidoappl_context(const char *name, int create)
20655929Sguido{
20753642Sguido	struct appl_context *ctx;
20853642Sguido
209145522Sdarrenr	if (name == NULL)
21053642Sguido		name = "";
211145522Sdarrenr
212145522Sdarrenr	if (strlen(name) > APPL_CONTEXTNAME_MAX) {
21353642Sguido		errno = EINVAL;
214145522Sdarrenr		return NULL;
215145522Sdarrenr	}
21653642Sguido
217145522Sdarrenr	TAILQ_FOREACH(ctx, &contexts, ac_entries) {
218145522Sdarrenr		if (strcmp(name, ctx->ac_name) == 0)
21953642Sguido			return ctx;
22053642Sguido	}
22153642Sguido
222145522Sdarrenr	/* Always allow the default namespace */
223145522Sdarrenr	if (!create && name[0] != '\0') {
224145522Sdarrenr		errno = ENOENT;
225145522Sdarrenr		return NULL;
226145522Sdarrenr	}
227145522Sdarrenr
22853642Sguido	if ((ctx = malloc(sizeof(*ctx))) == NULL)
22953642Sguido		return NULL;
23053642Sguido
23153642Sguido	strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name));
23253642Sguido	RB_INIT(&(ctx->ac_regions));
23353642Sguido	TAILQ_INIT(&(ctx->ac_agentcaps));
234145522Sdarrenr	ctx->ac_agentcap_lastid = 0;
23553642Sguido	ctx->ac_agentcap_lastchange = 0;
23653642Sguido
23753642Sguido	TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries);
23853642Sguido	return ctx;
23953642Sguido}
24053642Sguido
24153642Sguido/* Name from RFC 2741 section 6.2.14 */
24253642Sguidoenum appl_error
24353642Sguidoappl_addagentcaps(const char *ctxname, struct ber_oid *oid, const char *descr,
24453642Sguido    struct appl_backend *backend)
24553642Sguido{
24653642Sguido	struct appl_context *ctx;
24753642Sguido	struct appl_agentcap *cap;
24853642Sguido	char oidbuf[1024];
24953642Sguido
25053642Sguido	if (ctxname == NULL)
25153642Sguido		ctxname = "";
25253642Sguido
25353642Sguido	mib_oid2string(oid, oidbuf, sizeof(oidbuf), snmpd_env->sc_oidfmt);
25455929Sguido	log_info("%s: Adding agent capabilities %s context(%s)",
25553642Sguido		backend->ab_name, oidbuf, ctxname);
25653642Sguido
25753642Sguido	if ((ctx = appl_context(ctxname, 0)) == NULL) {
25855929Sguido		log_info("%s: Can't add agent capabilities %s: "
25955929Sguido		    "Unsupported context \"%s\"", backend->ab_name, oidbuf,
26055929Sguido		    ctxname);
26155929Sguido		return APPL_ERROR_UNSUPPORTEDCONTEXT;
26255929Sguido	}
26355929Sguido
26455929Sguido	if ((cap = malloc(sizeof(*cap))) == NULL) {
26555929Sguido		log_warn("%s: Can't add agent capabilities %s",
26653642Sguido		    backend->ab_name, oidbuf);
26753642Sguido		return APPL_ERROR_PROCESSINGERROR;
26855929Sguido	}
269145522Sdarrenr
27055929Sguido	cap->aa_backend = backend;
27155929Sguido	cap->aa_context = ctx;
27255929Sguido	cap->aa_index = ++ctx->ac_agentcap_lastid;
27355929Sguido	cap->aa_oid = *oid;
27455929Sguido	cap->aa_uptime = smi_getticks();
27555929Sguido	if (strlcpy(cap->aa_descr, descr,
27655929Sguido	    sizeof(cap->aa_descr)) >= sizeof(cap->aa_descr)) {
27755929Sguido		log_info("%s: Can't add agent capabilities %s: "
27855929Sguido		    "Invalid description", backend->ab_name, oidbuf);
27955929Sguido		free(cap);
280145522Sdarrenr		return APPL_ERROR_PARSEERROR;
281145522Sdarrenr	}
282145522Sdarrenr
283145522Sdarrenr	TAILQ_INSERT_TAIL(&(ctx->ac_agentcaps), cap, aa_entry);
28455929Sguido	ctx->ac_agentcap_lastchange = cap->aa_uptime;
28555929Sguido
28667853Sdarrenr	return APPL_ERROR_NOERROR;
287145522Sdarrenr}
28855929Sguido
28960857Sdarrenr/* Name from RFC2741 section 6.2.15 */
29060857Sdarrenrenum appl_error
29155929Sguidoappl_removeagentcaps(const char *ctxname, struct ber_oid *oid,
29255929Sguido    struct appl_backend *backend)
29355929Sguido{
29455929Sguido	struct appl_context *ctx;
29555929Sguido	struct appl_agentcap *cap, *tmp;
29655929Sguido	char oidbuf[1024];
29755929Sguido	int found = 0;
29855929Sguido
29955929Sguido	if (ctxname == NULL)
30080482Sdarrenr		ctxname = "";
301145522Sdarrenr
302145522Sdarrenr	mib_oid2string(oid, oidbuf, sizeof(oidbuf), snmpd_env->sc_oidfmt);
303145522Sdarrenr	log_info("%s: Removing agent capabilities %s context(%s)",
304145522Sdarrenr	    backend->ab_name, oidbuf, ctxname);
305145522Sdarrenr
306145522Sdarrenr	if ((ctx = appl_context(ctxname, 0)) == NULL) {
307145522Sdarrenr		log_info("%s: Can't remove agent capabilities %s: "
308145522Sdarrenr		    "Unsupported context \"%s\"", backend->ab_name, oidbuf,
309145522Sdarrenr		    ctxname);
310170268Sdarrenr		return APPL_ERROR_UNSUPPORTEDCONTEXT;
31155929Sguido	}
31255929Sguido
31355929Sguido	TAILQ_FOREACH_SAFE(cap, &(ctx->ac_agentcaps), aa_entry, tmp) {
31455929Sguido		/* No duplicate oid check, just continue */
31555929Sguido		if (cap->aa_backend != backend ||
31655929Sguido		    ober_oid_cmp(oid, &(cap->aa_oid)) != 0)
31755929Sguido			continue;
31855929Sguido		found = 1;
31955929Sguido		appl_agentcap_free(cap);
32080482Sdarrenr	}
321145522Sdarrenr
322145522Sdarrenr	if (found)
323145522Sdarrenr		return APPL_ERROR_NOERROR;
324145522Sdarrenr
325145522Sdarrenr	log_info("%s: Can't remove agent capabilities %s: not found",
326145522Sdarrenr	    backend->ab_name, oidbuf);
327145522Sdarrenr	return APPL_ERROR_UNKNOWNAGENTCAPS;
328145522Sdarrenr}
329145522Sdarrenr
330170268Sdarrenrvoid
33155929Sguidoappl_agentcap_free(struct appl_agentcap *cap)
33255929Sguido{
33360857Sdarrenr	TAILQ_REMOVE(&(cap->aa_context->ac_agentcaps), cap, aa_entry);
33455929Sguido	cap->aa_context->ac_agentcap_lastchange = smi_getticks();
33560857Sdarrenr	free(cap);
33655929Sguido}
33755929Sguido
33853642Sguidostruct ber_element *
33953642Sguidoappl_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
355struct ber_element *
356appl_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
392struct ber_element *
393appl_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
435struct ber_element *
436appl_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
452enum appl_error
453appl_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 */
541enum appl_error
542appl_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 */
652enum appl_error
653appl_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
733enum appl_error
734appl_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
758void
759appl_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 */
780void
781appl_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
809struct appl_region *
810appl_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
825struct appl_region *
826appl_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 */
865void
866appl_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
953void
954appl_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
975void
976appl_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
996void
997appl_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
1141void
1142appl_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
1163void
1164appl_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
1180void
1181appl_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 */
1291void
1292appl_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
1424int
1425appl_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
1622unsigned int
1623appl_ber_any(struct ber_element *elm)
1624{
1625	return BER_TYPE_OCTETSTRING;
1626}
1627
1628int
1629appl_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
1659int
1660appl_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
1770void
1771appl_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
1783void
1784appl_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
1821struct ber_element *
1822appl_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
1835void
1836appl_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
1879void
1880ober_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
1891int
1892appl_region_cmp(struct appl_region *r1, struct appl_region *r2)
1893{
1894	return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid));
1895}
1896
1897int
1898appl_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
1905RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
1906RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry,
1907    appl_request_cmp);
1908