PageRenderTime 101ms CodeModel.GetById 15ms app.highlight 79ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bsnmp/lib/snmpagent.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1007 lines | 734 code | 137 blank | 136 comment | 210 complexity | c4bfcddf86eaffd49a688897ca739a45 MD5 | raw file
   1/*
   2 * Copyright (c) 2001-2003
   3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
   4 *	All rights reserved.
   5 *
   6 * Author: Harti Brandt <harti@freebsd.org>
   7 * 
   8 * Redistribution and use in source and binary forms, with or without
   9 * modification, are permitted provided that the following conditions
  10 * are met:
  11 * 1. Redistributions of source code must retain the above copyright
  12 *    notice, this list of conditions and the following disclaimer.
  13 * 2. Redistributions in binary form must reproduce the above copyright
  14 *    notice, this list of conditions and the following disclaimer in the
  15 *    documentation and/or other materials provided with the distribution.
  16 * 
  17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
  21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27 * SUCH DAMAGE.
  28 *
  29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $
  30 *
  31 * SNMP Agent functions
  32 */
  33#include <sys/types.h>
  34#include <sys/queue.h>
  35#include <stdio.h>
  36#include <stdlib.h>
  37#include <stddef.h>
  38#include <stdarg.h>
  39#ifdef HAVE_STDINT_H
  40#include <stdint.h>
  41#elif defined(HAVE_INTTYPES_H)
  42#include <inttypes.h>
  43#endif
  44#include <string.h>
  45
  46#include "asn1.h"
  47#include "snmp.h"
  48#include "snmppriv.h"
  49#include "snmpagent.h"
  50
  51static void snmp_debug_func(const char *fmt, ...);
  52
  53void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
  54
  55struct snmp_node *tree;
  56u_int  tree_size;
  57
  58/*
  59 * Structure to hold dependencies during SET processing
  60 * The last two members of this structure must be the
  61 * dependency visible by the user and the user data.
  62 */
  63struct depend {
  64	TAILQ_ENTRY(depend) link;
  65	size_t	len;		/* size of data part */
  66	snmp_depop_t	func;
  67	struct snmp_dependency dep;
  68#if defined(__GNUC__) && __GNUC__ < 3
  69	u_char	data[0];
  70#else
  71	u_char	data[];
  72#endif
  73};
  74TAILQ_HEAD(depend_list, depend);
  75
  76/*
  77 * Set context
  78 */
  79struct context {
  80	struct snmp_context	ctx;
  81	struct depend_list	dlist;
  82	const struct snmp_node	*node[SNMP_MAX_BINDINGS];
  83	struct snmp_scratch	scratch[SNMP_MAX_BINDINGS];
  84	struct depend		*depend;
  85};
  86
  87#define	TR(W)	(snmp_trace & SNMP_TRACE_##W)
  88u_int snmp_trace = 0;
  89
  90static char oidbuf[ASN_OIDSTRLEN];
  91
  92/*
  93 * Allocate a context
  94 */
  95struct snmp_context *
  96snmp_init_context(void)
  97{
  98	struct context *context;
  99
 100	if ((context = malloc(sizeof(*context))) == NULL)
 101		return (NULL);
 102
 103	memset(context, 0, sizeof(*context));
 104	TAILQ_INIT(&context->dlist);
 105
 106	return (&context->ctx);
 107}
 108
 109/*
 110 * Find a variable for SET/GET and the first GETBULK pass.
 111 * Return the node pointer. If the search fails, set the errp to
 112 * the correct SNMPv2 GET exception code.
 113 */
 114static struct snmp_node *
 115find_node(const struct snmp_value *value, enum snmp_syntax *errp)
 116{
 117	struct snmp_node *tp;
 118
 119	if (TR(FIND))
 120		snmp_debug("find: searching %s",
 121		    asn_oid2str_r(&value->var, oidbuf));
 122
 123	/*
 124	 * If we have an exact match (the entry in the table is a
 125	 * sub-oid from the variable) we have found what we are for.
 126	 * If the table oid is higher than the variable, there is no match.
 127	 */
 128	for (tp = tree; tp < tree + tree_size; tp++) {
 129		if (asn_is_suboid(&tp->oid, &value->var))
 130			goto found;
 131		if (asn_compare_oid(&tp->oid, &value->var) >= 0)
 132			break;
 133	}
 134
 135	if (TR(FIND))
 136		snmp_debug("find: no match");
 137	*errp = SNMP_SYNTAX_NOSUCHOBJECT;
 138	return (NULL);
 139
 140  found:
 141	/* leafs must have a 0 instance identifier */
 142	if (tp->type == SNMP_NODE_LEAF &&
 143	    (value->var.len != tp->oid.len + 1 ||
 144	     value->var.subs[tp->oid.len] != 0)) {
 145		if (TR(FIND))
 146			snmp_debug("find: bad leaf index");
 147		*errp = SNMP_SYNTAX_NOSUCHINSTANCE;
 148		return (NULL);
 149	}
 150	if (TR(FIND))
 151		snmp_debug("find: found %s",
 152		    asn_oid2str_r(&value->var, oidbuf));
 153	return (tp);
 154}
 155
 156static struct snmp_node *
 157find_subnode(const struct snmp_value *value)
 158{
 159	struct snmp_node *tp;
 160
 161	for (tp = tree; tp < tree + tree_size; tp++) {
 162		if (asn_is_suboid(&value->var, &tp->oid))
 163			return (tp);
 164	}
 165	return (NULL);
 166}
 167
 168static void
 169snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp)
 170{
 171	memset(resp, 0, sizeof(*resp));
 172	strcpy(resp->community, pdu->community);
 173	resp->version = pdu->version;
 174	resp->type = SNMP_PDU_RESPONSE;
 175	resp->request_id = pdu->request_id;
 176	resp->version = pdu->version;
 177
 178	if (resp->version != SNMP_V3)
 179		return;
 180
 181	memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
 182	memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
 183	snmp_pdu_init_secparams(resp);
 184	resp->identifier = pdu->identifier;
 185	resp->security_model = pdu->security_model;
 186	resp->context_engine_len = pdu->context_engine_len;
 187	memcpy(resp->context_engine, pdu->context_engine,
 188	    resp->context_engine_len);
 189	strlcpy(resp->context_name, pdu->context_name,
 190	    sizeof(resp->context_name));
 191}
 192
 193/*
 194 * Execute a GET operation. The tree is rooted at the global 'root'.
 195 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
 196 * the pdu error status and index will be set.
 197 */
 198enum snmp_ret
 199snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
 200    struct snmp_pdu *resp, void *data)
 201{
 202	int ret;
 203	u_int i;
 204	struct snmp_node *tp;
 205	enum snmp_syntax except;
 206	struct context context;
 207	enum asn_err err;
 208
 209	memset(&context, 0, sizeof(context));
 210	context.ctx.data = data;
 211
 212	snmp_pdu_create_response(pdu, resp);
 213
 214	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
 215		/* cannot even encode header - very bad */
 216		return (SNMP_RET_IGN);
 217
 218	for (i = 0; i < pdu->nbindings; i++) {
 219		resp->bindings[i].var = pdu->bindings[i].var;
 220		if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
 221			if (pdu->version == SNMP_V1) {
 222				if (TR(GET))
 223					snmp_debug("get: nosuchname");
 224				pdu->error_status = SNMP_ERR_NOSUCHNAME;
 225				pdu->error_index = i + 1;
 226				snmp_pdu_free(resp);
 227				return (SNMP_RET_ERR);
 228			}
 229			if (TR(GET))
 230				snmp_debug("get: exception %u", except);
 231			resp->bindings[i].syntax = except;
 232
 233		} else {
 234			/* call the action to fetch the value. */
 235			resp->bindings[i].syntax = tp->syntax;
 236			ret = (*tp->op)(&context.ctx, &resp->bindings[i],
 237			    tp->oid.len, tp->index, SNMP_OP_GET);
 238			if (TR(GET))
 239				snmp_debug("get: action returns %d", ret);
 240
 241			if (ret == SNMP_ERR_NOSUCHNAME) {
 242				if (pdu->version == SNMP_V1) {
 243					pdu->error_status = SNMP_ERR_NOSUCHNAME;
 244					pdu->error_index = i + 1;
 245					snmp_pdu_free(resp);
 246					return (SNMP_RET_ERR);
 247				}
 248				if (TR(GET))
 249					snmp_debug("get: exception noSuchInstance");
 250				resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
 251
 252			} else if (ret != SNMP_ERR_NOERROR) {
 253				pdu->error_status = SNMP_ERR_GENERR;
 254				pdu->error_index = i + 1;
 255				snmp_pdu_free(resp);
 256				return (SNMP_RET_ERR);
 257			}
 258		}
 259		resp->nbindings++;
 260
 261		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
 262
 263		if (err == ASN_ERR_EOBUF) {
 264			pdu->error_status = SNMP_ERR_TOOBIG;
 265			pdu->error_index = 0;
 266			snmp_pdu_free(resp);
 267			return (SNMP_RET_ERR);
 268		}
 269		if (err != ASN_ERR_OK) {
 270			if (TR(GET))
 271				snmp_debug("get: binding encoding: %u", err);
 272			pdu->error_status = SNMP_ERR_GENERR;
 273			pdu->error_index = i + 1;
 274			snmp_pdu_free(resp);
 275			return (SNMP_RET_ERR);
 276		}
 277	}
 278
 279	return (snmp_fix_encoding(resp_b, resp));
 280}
 281
 282static struct snmp_node *
 283next_node(const struct snmp_value *value, int *pnext)
 284{
 285	struct snmp_node *tp;
 286
 287	if (TR(FIND))
 288		snmp_debug("next: searching %s",
 289		    asn_oid2str_r(&value->var, oidbuf));
 290
 291	*pnext = 0;
 292	for (tp = tree; tp < tree + tree_size; tp++) {
 293		if (asn_is_suboid(&tp->oid, &value->var)) {
 294			/* the tree OID is a sub-oid of the requested OID. */
 295			if (tp->type == SNMP_NODE_LEAF) {
 296				if (tp->oid.len == value->var.len) {
 297					/* request for scalar type */
 298					if (TR(FIND))
 299						snmp_debug("next: found scalar %s",
 300						    asn_oid2str_r(&tp->oid, oidbuf));
 301					return (tp);
 302				}
 303				/* try next */
 304			} else {
 305				if (TR(FIND))
 306					snmp_debug("next: found column %s",
 307					    asn_oid2str_r(&tp->oid, oidbuf));
 308				return (tp);
 309			}
 310		} else if (asn_is_suboid(&value->var, &tp->oid) ||
 311		    asn_compare_oid(&tp->oid, &value->var) >= 0) {
 312			if (TR(FIND))
 313				snmp_debug("next: found %s",
 314				    asn_oid2str_r(&tp->oid, oidbuf));
 315			*pnext = 1;
 316			return (tp);
 317		}
 318	}
 319
 320	if (TR(FIND))
 321		snmp_debug("next: failed");
 322
 323	return (NULL);
 324}
 325
 326static enum snmp_ret
 327do_getnext(struct context *context, const struct snmp_value *inb,
 328    struct snmp_value *outb, struct snmp_pdu *pdu)
 329{
 330	const struct snmp_node *tp;
 331	int ret, next;
 332
 333	if ((tp = next_node(inb, &next)) == NULL)
 334		goto eofMib;
 335
 336	/* retain old variable if we are doing a GETNEXT on an exact
 337	 * matched leaf only */
 338	if (tp->type == SNMP_NODE_LEAF || next)
 339		outb->var = tp->oid;
 340	else
 341		outb->var = inb->var;
 342
 343	for (;;) {
 344		outb->syntax = tp->syntax;
 345		if (tp->type == SNMP_NODE_LEAF) {
 346			/* make a GET operation */
 347			outb->var.subs[outb->var.len++] = 0;
 348			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
 349			    tp->index, SNMP_OP_GET);
 350		} else {
 351			/* make a GETNEXT */
 352			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
 353			     tp->index, SNMP_OP_GETNEXT);
 354		}
 355		if (ret != SNMP_ERR_NOSUCHNAME) {
 356			/* got something */
 357			if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
 358				snmp_debug("getnext: %s returns %u",
 359				    asn_oid2str(&outb->var), ret);
 360			break;
 361		}
 362
 363		/* object has no data - try next */
 364		if (++tp == tree + tree_size)
 365			break;
 366
 367		if (TR(GETNEXT))
 368			snmp_debug("getnext: no data - avancing to %s",
 369			    asn_oid2str(&tp->oid));
 370
 371		outb->var = tp->oid;
 372	}
 373
 374	if (ret == SNMP_ERR_NOSUCHNAME) {
 375  eofMib:
 376		outb->var = inb->var;
 377		if (pdu->version == SNMP_V1) {
 378			pdu->error_status = SNMP_ERR_NOSUCHNAME;
 379			return (SNMP_RET_ERR);
 380		}
 381		outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
 382
 383	} else if (ret != SNMP_ERR_NOERROR) {
 384		pdu->error_status = SNMP_ERR_GENERR;
 385		return (SNMP_RET_ERR);
 386	}
 387	return (SNMP_RET_OK);
 388}
 389
 390
 391/*
 392 * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
 393 * Build the response PDU on the fly. The return is:
 394 */
 395enum snmp_ret
 396snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
 397    struct snmp_pdu *resp, void *data)
 398{
 399	struct context context;
 400	u_int i;
 401	enum asn_err err;
 402	enum snmp_ret result;
 403
 404	memset(&context, 0, sizeof(context));
 405	context.ctx.data = data;
 406
 407	snmp_pdu_create_response(pdu, resp);
 408
 409	if (snmp_pdu_encode_header(resp_b, resp))
 410		return (SNMP_RET_IGN);
 411
 412	for (i = 0; i < pdu->nbindings; i++) {
 413		result = do_getnext(&context, &pdu->bindings[i],
 414		    &resp->bindings[i], pdu);
 415
 416		if (result != SNMP_RET_OK) {
 417			pdu->error_index = i + 1;
 418			snmp_pdu_free(resp);
 419			return (result);
 420		}
 421
 422		resp->nbindings++;
 423
 424		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
 425
 426		if (err == ASN_ERR_EOBUF) {
 427			pdu->error_status = SNMP_ERR_TOOBIG;
 428			pdu->error_index = 0;
 429			snmp_pdu_free(resp);
 430			return (SNMP_RET_ERR);
 431		}
 432		if (err != ASN_ERR_OK) {
 433			if (TR(GET))
 434				snmp_debug("getnext: binding encoding: %u", err);
 435			pdu->error_status = SNMP_ERR_GENERR;
 436			pdu->error_index = i + 1;
 437			snmp_pdu_free(resp);
 438			return (SNMP_RET_ERR);
 439		}
 440	}
 441	return (snmp_fix_encoding(resp_b, resp));
 442}
 443
 444enum snmp_ret
 445snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
 446    struct snmp_pdu *resp, void *data)
 447{
 448	struct context context;
 449	u_int i;
 450	int cnt;
 451	u_int non_rep;
 452	int eomib;
 453	enum snmp_ret result;
 454	enum asn_err err;
 455
 456	memset(&context, 0, sizeof(context));
 457	context.ctx.data = data;
 458
 459	snmp_pdu_create_response(pdu, resp);
 460
 461	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
 462		/* cannot even encode header - very bad */
 463		return (SNMP_RET_IGN);
 464
 465	if ((non_rep = pdu->error_status) > pdu->nbindings)
 466		non_rep = pdu->nbindings;
 467
 468	/* non-repeaters */
 469	for (i = 0; i < non_rep; i++) {
 470		result = do_getnext(&context, &pdu->bindings[i],
 471		    &resp->bindings[resp->nbindings], pdu);
 472
 473		if (result != SNMP_RET_OK) {
 474			pdu->error_index = i + 1;
 475			snmp_pdu_free(resp);
 476			return (result);
 477		}
 478
 479		err = snmp_binding_encode(resp_b,
 480		    &resp->bindings[resp->nbindings++]);
 481
 482		if (err == ASN_ERR_EOBUF)
 483			goto done;
 484
 485		if (err != ASN_ERR_OK) {
 486			if (TR(GET))
 487				snmp_debug("getnext: binding encoding: %u", err);
 488			pdu->error_status = SNMP_ERR_GENERR;
 489			pdu->error_index = i + 1;
 490			snmp_pdu_free(resp);
 491			return (SNMP_RET_ERR);
 492		}
 493	}
 494
 495	if (non_rep == pdu->nbindings)
 496		goto done;
 497
 498	/* repeates */
 499	for (cnt = 0; cnt < pdu->error_index; cnt++) {
 500		eomib = 1;
 501		for (i = non_rep; i < pdu->nbindings; i++) {
 502			if (cnt == 0) 
 503				result = do_getnext(&context, &pdu->bindings[i],
 504				    &resp->bindings[resp->nbindings], pdu);
 505			else
 506				result = do_getnext(&context,
 507				    &resp->bindings[resp->nbindings -
 508				    (pdu->nbindings - non_rep)],
 509				    &resp->bindings[resp->nbindings], pdu);
 510
 511			if (result != SNMP_RET_OK) {
 512				pdu->error_index = i + 1;
 513				snmp_pdu_free(resp);
 514				return (result);
 515			}
 516			if (resp->bindings[resp->nbindings].syntax !=
 517			    SNMP_SYNTAX_ENDOFMIBVIEW)
 518				eomib = 0;
 519
 520			err = snmp_binding_encode(resp_b,
 521			    &resp->bindings[resp->nbindings++]);
 522
 523			if (err == ASN_ERR_EOBUF)
 524				goto done;
 525
 526			if (err != ASN_ERR_OK) {
 527				if (TR(GET))
 528					snmp_debug("getnext: binding encoding: %u", err);
 529				pdu->error_status = SNMP_ERR_GENERR;
 530				pdu->error_index = i + 1;
 531				snmp_pdu_free(resp);
 532				return (SNMP_RET_ERR);
 533			}
 534		}
 535		if (eomib)
 536			break;
 537	}
 538
 539  done:
 540	return (snmp_fix_encoding(resp_b, resp));
 541}
 542
 543/*
 544 * Rollback a SET operation. Failed index is 'i'.
 545 */
 546static void
 547rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
 548{
 549	struct snmp_value *b;
 550	const struct snmp_node *np;
 551	int ret;
 552
 553	while (i-- > 0) {
 554		b = &pdu->bindings[i];
 555		np = context->node[i];
 556
 557		context->ctx.scratch = &context->scratch[i];
 558
 559		ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
 560		    SNMP_OP_ROLLBACK);
 561
 562		if (ret != SNMP_ERR_NOERROR) {
 563			snmp_error("set: rollback failed (%d) on variable %s "
 564			    "index %u", ret, asn_oid2str(&b->var), i);
 565			if (pdu->version != SNMP_V1) {
 566				pdu->error_status = SNMP_ERR_UNDO_FAILED;
 567				pdu->error_index = 0;
 568			}
 569		}
 570	}
 571}
 572
 573/*
 574 * Commit dependencies.
 575 */
 576int
 577snmp_dep_commit(struct snmp_context *ctx)
 578{
 579	struct context *context = (struct context *)ctx;
 580	int ret;
 581
 582	TAILQ_FOREACH(context->depend, &context->dlist, link) {
 583		ctx->dep = &context->depend->dep;
 584
 585		if (TR(SET))
 586			snmp_debug("set: dependency commit %s",
 587			    asn_oid2str(&ctx->dep->obj));
 588
 589		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
 590
 591		if (ret != SNMP_ERR_NOERROR) {
 592			if (TR(SET))
 593				snmp_debug("set: dependency failed %d", ret);
 594			return (ret);
 595		}
 596	}
 597	return (SNMP_ERR_NOERROR);
 598}
 599
 600/*
 601 * Rollback dependencies
 602 */
 603int
 604snmp_dep_rollback(struct snmp_context *ctx)
 605{
 606	struct context *context = (struct context *)ctx;
 607	int ret, ret1;
 608	char objbuf[ASN_OIDSTRLEN];
 609	char idxbuf[ASN_OIDSTRLEN];
 610
 611	ret1 = SNMP_ERR_NOERROR;
 612	while ((context->depend =
 613	    TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
 614		ctx->dep = &context->depend->dep;
 615
 616		if (TR(SET))
 617			snmp_debug("set: dependency rollback %s",
 618			    asn_oid2str(&ctx->dep->obj));
 619
 620		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
 621
 622		if (ret != SNMP_ERR_NOERROR) {
 623			snmp_debug("set: dep rollback returns %u: %s %s", ret,
 624			    asn_oid2str_r(&ctx->dep->obj, objbuf),
 625			    asn_oid2str_r(&ctx->dep->idx, idxbuf));
 626			if (ret1 == SNMP_ERR_NOERROR)
 627				ret1 = ret;
 628		}
 629	}
 630	return (ret1);
 631}
 632
 633void
 634snmp_dep_finish(struct snmp_context *ctx)
 635{
 636	struct context *context = (struct context *)ctx;
 637	struct depend *d;
 638
 639	while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
 640		ctx->dep = &d->dep;
 641		(void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
 642		TAILQ_REMOVE(&context->dlist, d, link);
 643		free(d);
 644	}
 645}
 646
 647/*
 648 * Do a SET operation.
 649 */
 650enum snmp_ret
 651snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
 652    struct snmp_pdu *resp, void *data)
 653{
 654	int ret;
 655	u_int i;
 656	enum asn_err asnerr;
 657	struct context context;
 658	const struct snmp_node *np;
 659	struct snmp_value *b;
 660	enum snmp_syntax except;
 661
 662	memset(&context, 0, sizeof(context));
 663	TAILQ_INIT(&context.dlist);
 664	context.ctx.data = data;
 665
 666	snmp_pdu_create_response(pdu, resp);
 667
 668	if (snmp_pdu_encode_header(resp_b, resp))
 669		return (SNMP_RET_IGN);
 670
 671	/* 
 672	 * 1. Find all nodes, check that they are writeable and
 673	 *    that the syntax is ok, copy over the binding to the response.
 674	 */
 675	for (i = 0; i < pdu->nbindings; i++) {
 676		b = &pdu->bindings[i];
 677
 678		if ((np = context.node[i] = find_node(b, &except)) == NULL) {
 679			/* not found altogether or LEAF with wrong index */
 680			if (TR(SET))
 681				snmp_debug("set: node not found %s",
 682				    asn_oid2str_r(&b->var, oidbuf));
 683			if (pdu->version == SNMP_V1) {
 684				pdu->error_index = i + 1;
 685				pdu->error_status = SNMP_ERR_NOSUCHNAME;
 686			} else if ((np = find_subnode(b)) != NULL) {
 687				/* 2. intermediate object */
 688				pdu->error_index = i + 1;
 689				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
 690			} else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
 691				pdu->error_index = i + 1;
 692				pdu->error_status = SNMP_ERR_NO_ACCESS;
 693			} else {
 694				pdu->error_index = i + 1;
 695				pdu->error_status = SNMP_ERR_NO_CREATION;
 696			}
 697			snmp_pdu_free(resp);
 698			return (SNMP_RET_ERR);
 699		}
 700		/*
 701		 * 2. write/createable?
 702		 * Can check this for leafs only, because in v2 we have
 703		 * to differentiate between NOT_WRITEABLE and NO_CREATION
 704		 * and only the action routine for COLUMNS knows, whether
 705		 * a column exists.
 706		 */
 707		if (np->type == SNMP_NODE_LEAF &&
 708		    !(np->flags & SNMP_NODE_CANSET)) {
 709			if (pdu->version == SNMP_V1) {
 710				pdu->error_index = i + 1;
 711				pdu->error_status = SNMP_ERR_NOSUCHNAME;
 712			} else {
 713				pdu->error_index = i + 1;
 714				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
 715			}
 716			snmp_pdu_free(resp);
 717			return (SNMP_RET_ERR);
 718		}
 719		/*
 720		 * 3. Ensure the right syntax
 721		 */
 722		if (np->syntax != b->syntax) {
 723			if (pdu->version == SNMP_V1) {
 724				pdu->error_index = i + 1;
 725				pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
 726			} else {
 727				pdu->error_index = i + 1;
 728				pdu->error_status = SNMP_ERR_WRONG_TYPE;
 729			}
 730			snmp_pdu_free(resp);
 731			return (SNMP_RET_ERR);
 732		}
 733		/*
 734		 * 4. Copy binding
 735		 */
 736		if (snmp_value_copy(&resp->bindings[i], b)) {
 737			pdu->error_index = i + 1;
 738			pdu->error_status = SNMP_ERR_GENERR;
 739			snmp_pdu_free(resp);
 740			return (SNMP_RET_ERR);
 741		}
 742		asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
 743		if (asnerr == ASN_ERR_EOBUF) {
 744			pdu->error_index = i + 1;
 745			pdu->error_status = SNMP_ERR_TOOBIG;
 746			snmp_pdu_free(resp);
 747			return (SNMP_RET_ERR);
 748		} else if (asnerr != ASN_ERR_OK) {
 749			pdu->error_index = i + 1;
 750			pdu->error_status = SNMP_ERR_GENERR;
 751			snmp_pdu_free(resp);
 752			return (SNMP_RET_ERR);
 753		}
 754		resp->nbindings++;
 755	}
 756
 757	context.ctx.code = SNMP_RET_OK;
 758
 759	/*
 760	 * 2. Call the SET method for each node. If a SET fails, rollback
 761	 *    everything. Map error codes depending on the version.
 762	 */
 763	for (i = 0; i < pdu->nbindings; i++) {
 764		b = &pdu->bindings[i];
 765		np = context.node[i];
 766
 767		context.ctx.var_index = i + 1;
 768		context.ctx.scratch = &context.scratch[i];
 769
 770		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
 771		    SNMP_OP_SET);
 772
 773		if (TR(SET))
 774			snmp_debug("set: action %s returns %d", np->name, ret);
 775
 776		if (pdu->version == SNMP_V1) {
 777			switch (ret) {
 778			  case SNMP_ERR_NO_ACCESS:
 779				ret = SNMP_ERR_NOSUCHNAME;
 780				break;
 781			  case SNMP_ERR_WRONG_TYPE:
 782				/* should no happen */
 783				ret = SNMP_ERR_BADVALUE;
 784				break;
 785			  case SNMP_ERR_WRONG_LENGTH:
 786				ret = SNMP_ERR_BADVALUE;
 787				break;
 788			  case SNMP_ERR_WRONG_ENCODING:
 789				/* should not happen */
 790				ret = SNMP_ERR_BADVALUE;
 791				break;
 792			  case SNMP_ERR_WRONG_VALUE:
 793				ret = SNMP_ERR_BADVALUE;
 794				break;
 795			  case SNMP_ERR_NO_CREATION:
 796				ret = SNMP_ERR_NOSUCHNAME;
 797				break;
 798			  case SNMP_ERR_INCONS_VALUE:
 799				ret = SNMP_ERR_BADVALUE;
 800				break;
 801			  case SNMP_ERR_RES_UNAVAIL:
 802				ret = SNMP_ERR_GENERR;
 803				break;
 804			  case SNMP_ERR_COMMIT_FAILED:
 805				ret = SNMP_ERR_GENERR;
 806				break;
 807			  case SNMP_ERR_UNDO_FAILED:
 808				ret = SNMP_ERR_GENERR;
 809				break;
 810			  case SNMP_ERR_AUTH_ERR:
 811				/* should not happen */
 812				ret = SNMP_ERR_GENERR;
 813				break;
 814			  case SNMP_ERR_NOT_WRITEABLE:
 815				ret = SNMP_ERR_NOSUCHNAME;
 816				break;
 817			  case SNMP_ERR_INCONS_NAME:
 818				ret = SNMP_ERR_BADVALUE;
 819				break;
 820			}
 821		}
 822		if (ret != SNMP_ERR_NOERROR) {
 823			pdu->error_index = i + 1;
 824			pdu->error_status = ret;
 825
 826			rollback(&context, pdu, i);
 827			snmp_pdu_free(resp);
 828
 829			context.ctx.code = SNMP_RET_ERR;
 830
 831			goto errout;
 832		}
 833	}
 834
 835	/*
 836	 * 3. Call dependencies
 837	 */
 838	if (TR(SET))
 839		snmp_debug("set: set operations ok");
 840
 841	if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
 842		pdu->error_status = ret;
 843		pdu->error_index = context.ctx.var_index;
 844
 845		if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
 846			if (pdu->version != SNMP_V1) {
 847				pdu->error_status = SNMP_ERR_UNDO_FAILED;
 848				pdu->error_index = 0;
 849			}
 850		}
 851		rollback(&context, pdu, i);
 852		snmp_pdu_free(resp);
 853
 854		context.ctx.code = SNMP_RET_ERR;
 855
 856		goto errout;
 857	}
 858
 859	/*
 860	 * 4. Commit and copy values from the original packet to the response.
 861	 *    This is not the commit operation from RFC 1905 but rather an
 862	 *    'FREE RESOURCES' operation. It shouldn't fail.
 863	 */
 864	if (TR(SET))
 865		snmp_debug("set: commiting");
 866
 867	for (i = 0; i < pdu->nbindings; i++) {
 868		b = &resp->bindings[i];
 869		np = context.node[i];
 870
 871		context.ctx.var_index = i + 1;
 872		context.ctx.scratch = &context.scratch[i];
 873
 874		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
 875		    SNMP_OP_COMMIT);
 876
 877		if (ret != SNMP_ERR_NOERROR)
 878			snmp_error("set: commit failed (%d) on"
 879			    " variable %s index %u", ret,
 880			    asn_oid2str_r(&b->var, oidbuf), i);
 881	}
 882
 883	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
 884		snmp_error("set: fix_encoding failed");
 885		snmp_pdu_free(resp);
 886		context.ctx.code = SNMP_RET_IGN;
 887	}
 888
 889	/*
 890	 * Done
 891	 */
 892  errout:
 893	snmp_dep_finish(&context.ctx);
 894
 895	if (TR(SET))
 896		snmp_debug("set: returning %d", context.ctx.code);
 897
 898	return (context.ctx.code);
 899}
 900/*
 901 * Lookup a dependency. If it doesn't exist, create one
 902 */
 903struct snmp_dependency *
 904snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
 905    const struct asn_oid *idx, size_t len, snmp_depop_t func)
 906{
 907	struct context *context;
 908	struct depend *d;
 909
 910	context = (struct context *)(void *)
 911	    ((char *)ctx - offsetof(struct context, ctx));
 912	if (TR(DEPEND)) {
 913		snmp_debug("depend: looking for %s", asn_oid2str(obj));
 914		if (idx)
 915			snmp_debug("depend: index is %s", asn_oid2str(idx));
 916	}
 917	TAILQ_FOREACH(d, &context->dlist, link)
 918		if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
 919		    ((idx == NULL && d->dep.idx.len == 0) ||
 920		     (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
 921			if(TR(DEPEND))
 922				snmp_debug("depend: found");
 923			return (&d->dep);
 924		}
 925
 926	if(TR(DEPEND))
 927		snmp_debug("depend: creating");
 928
 929	if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
 930		return (NULL);
 931	memset(&d->dep, 0, len);
 932
 933	d->dep.obj = *obj;
 934	if (idx == NULL)
 935		d->dep.idx.len = 0;
 936	else
 937		d->dep.idx = *idx;
 938	d->len = len;
 939	d->func = func;
 940
 941	TAILQ_INSERT_TAIL(&context->dlist, d, link);
 942
 943	return (&d->dep);
 944}
 945
 946/*
 947 * Make an error response from a PDU. We do this without decoding the
 948 * variable bindings. This means we can sent the junk back to a caller
 949 * that has sent us junk in the first place. 
 950 */
 951enum snmp_ret
 952snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
 953    struct asn_buf *resp_b)
 954{
 955	asn_len_t len;
 956	struct snmp_pdu resp;
 957	enum asn_err err;
 958	enum snmp_code code;
 959
 960	memset(&resp, 0, sizeof(resp));
 961	if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
 962		return (SNMP_RET_IGN);
 963
 964	if (pdu_b->asn_len < len)
 965		return (SNMP_RET_IGN);
 966	pdu_b->asn_len = len;
 967
 968	err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
 969	if (ASN_ERR_STOPPED(err))
 970		return (SNMP_RET_IGN);
 971	if (pdu_b->asn_len < len)
 972		return (SNMP_RET_IGN);
 973	pdu_b->asn_len = len;
 974
 975	/* now we have the bindings left - construct new message */
 976	resp.error_status = pdu->error_status;
 977	resp.error_index = pdu->error_index;
 978	resp.type = SNMP_PDU_RESPONSE;
 979
 980	code = snmp_pdu_encode_header(resp_b, &resp);
 981	if (code != SNMP_CODE_OK)
 982		return (SNMP_RET_IGN);
 983
 984	if (pdu_b->asn_len > resp_b->asn_len)
 985		/* too short */
 986		return (SNMP_RET_IGN);
 987	(void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
 988	resp_b->asn_len -= pdu_b->asn_len;
 989	resp_b->asn_ptr += pdu_b->asn_len;
 990
 991	code = snmp_fix_encoding(resp_b, &resp);
 992	if (code != SNMP_CODE_OK)
 993		return (SNMP_RET_IGN);
 994
 995	return (SNMP_RET_OK);
 996}
 997
 998static void
 999snmp_debug_func(const char *fmt, ...)
1000{
1001	va_list ap;
1002
1003	va_start(ap, fmt);
1004	vfprintf(stderr, fmt, ap);
1005	va_end(ap);
1006	fprintf(stderr, "\n");
1007}