/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. #include "asn1.h"
  46. #include "snmp.h"
  47. #include "snmppriv.h"
  48. #include "snmpagent.h"
  49. static void snmp_debug_func(const char *fmt, ...);
  50. void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
  51. struct snmp_node *tree;
  52. u_int tree_size;
  53. /*
  54. * Structure to hold dependencies during SET processing
  55. * The last two members of this structure must be the
  56. * dependency visible by the user and the user data.
  57. */
  58. struct depend {
  59. TAILQ_ENTRY(depend) link;
  60. size_t len; /* size of data part */
  61. snmp_depop_t func;
  62. struct snmp_dependency dep;
  63. #if defined(__GNUC__) && __GNUC__ < 3
  64. u_char data[0];
  65. #else
  66. u_char data[];
  67. #endif
  68. };
  69. TAILQ_HEAD(depend_list, depend);
  70. /*
  71. * Set context
  72. */
  73. struct context {
  74. struct snmp_context ctx;
  75. struct depend_list dlist;
  76. const struct snmp_node *node[SNMP_MAX_BINDINGS];
  77. struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
  78. struct depend *depend;
  79. };
  80. #define TR(W) (snmp_trace & SNMP_TRACE_##W)
  81. u_int snmp_trace = 0;
  82. static char oidbuf[ASN_OIDSTRLEN];
  83. /*
  84. * Allocate a context
  85. */
  86. struct snmp_context *
  87. snmp_init_context(void)
  88. {
  89. struct context *context;
  90. if ((context = malloc(sizeof(*context))) == NULL)
  91. return (NULL);
  92. memset(context, 0, sizeof(*context));
  93. TAILQ_INIT(&context->dlist);
  94. return (&context->ctx);
  95. }
  96. /*
  97. * Find a variable for SET/GET and the first GETBULK pass.
  98. * Return the node pointer. If the search fails, set the errp to
  99. * the correct SNMPv2 GET exception code.
  100. */
  101. static struct snmp_node *
  102. find_node(const struct snmp_value *value, enum snmp_syntax *errp)
  103. {
  104. struct snmp_node *tp;
  105. if (TR(FIND))
  106. snmp_debug("find: searching %s",
  107. asn_oid2str_r(&value->var, oidbuf));
  108. /*
  109. * If we have an exact match (the entry in the table is a
  110. * sub-oid from the variable) we have found what we are for.
  111. * If the table oid is higher than the variable, there is no match.
  112. */
  113. for (tp = tree; tp < tree + tree_size; tp++) {
  114. if (asn_is_suboid(&tp->oid, &value->var))
  115. goto found;
  116. if (asn_compare_oid(&tp->oid, &value->var) >= 0)
  117. break;
  118. }
  119. if (TR(FIND))
  120. snmp_debug("find: no match");
  121. *errp = SNMP_SYNTAX_NOSUCHOBJECT;
  122. return (NULL);
  123. found:
  124. /* leafs must have a 0 instance identifier */
  125. if (tp->type == SNMP_NODE_LEAF &&
  126. (value->var.len != tp->oid.len + 1 ||
  127. value->var.subs[tp->oid.len] != 0)) {
  128. if (TR(FIND))
  129. snmp_debug("find: bad leaf index");
  130. *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
  131. return (NULL);
  132. }
  133. if (TR(FIND))
  134. snmp_debug("find: found %s",
  135. asn_oid2str_r(&value->var, oidbuf));
  136. return (tp);
  137. }
  138. static struct snmp_node *
  139. find_subnode(const struct snmp_value *value)
  140. {
  141. struct snmp_node *tp;
  142. for (tp = tree; tp < tree + tree_size; tp++) {
  143. if (asn_is_suboid(&value->var, &tp->oid))
  144. return (tp);
  145. }
  146. return (NULL);
  147. }
  148. static void
  149. snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp)
  150. {
  151. memset(resp, 0, sizeof(*resp));
  152. strcpy(resp->community, pdu->community);
  153. resp->version = pdu->version;
  154. resp->type = SNMP_PDU_RESPONSE;
  155. resp->request_id = pdu->request_id;
  156. resp->version = pdu->version;
  157. if (resp->version != SNMP_V3)
  158. return;
  159. memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
  160. memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
  161. snmp_pdu_init_secparams(resp);
  162. resp->identifier = pdu->identifier;
  163. resp->security_model = pdu->security_model;
  164. resp->context_engine_len = pdu->context_engine_len;
  165. memcpy(resp->context_engine, pdu->context_engine,
  166. resp->context_engine_len);
  167. strlcpy(resp->context_name, pdu->context_name,
  168. sizeof(resp->context_name));
  169. }
  170. /*
  171. * Execute a GET operation. The tree is rooted at the global 'root'.
  172. * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
  173. * the pdu error status and index will be set.
  174. */
  175. enum snmp_ret
  176. snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
  177. struct snmp_pdu *resp, void *data)
  178. {
  179. int ret;
  180. u_int i;
  181. struct snmp_node *tp;
  182. enum snmp_syntax except;
  183. struct context context;
  184. enum asn_err err;
  185. memset(&context, 0, sizeof(context));
  186. context.ctx.data = data;
  187. snmp_pdu_create_response(pdu, resp);
  188. if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
  189. /* cannot even encode header - very bad */
  190. return (SNMP_RET_IGN);
  191. for (i = 0; i < pdu->nbindings; i++) {
  192. resp->bindings[i].var = pdu->bindings[i].var;
  193. if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
  194. if (pdu->version == SNMP_V1) {
  195. if (TR(GET))
  196. snmp_debug("get: nosuchname");
  197. pdu->error_status = SNMP_ERR_NOSUCHNAME;
  198. pdu->error_index = i + 1;
  199. snmp_pdu_free(resp);
  200. return (SNMP_RET_ERR);
  201. }
  202. if (TR(GET))
  203. snmp_debug("get: exception %u", except);
  204. resp->bindings[i].syntax = except;
  205. } else {
  206. /* call the action to fetch the value. */
  207. resp->bindings[i].syntax = tp->syntax;
  208. ret = (*tp->op)(&context.ctx, &resp->bindings[i],
  209. tp->oid.len, tp->index, SNMP_OP_GET);
  210. if (TR(GET))
  211. snmp_debug("get: action returns %d", ret);
  212. if (ret == SNMP_ERR_NOSUCHNAME) {
  213. if (pdu->version == SNMP_V1) {
  214. pdu->error_status = SNMP_ERR_NOSUCHNAME;
  215. pdu->error_index = i + 1;
  216. snmp_pdu_free(resp);
  217. return (SNMP_RET_ERR);
  218. }
  219. if (TR(GET))
  220. snmp_debug("get: exception noSuchInstance");
  221. resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
  222. } else if (ret != SNMP_ERR_NOERROR) {
  223. pdu->error_status = SNMP_ERR_GENERR;
  224. pdu->error_index = i + 1;
  225. snmp_pdu_free(resp);
  226. return (SNMP_RET_ERR);
  227. }
  228. }
  229. resp->nbindings++;
  230. err = snmp_binding_encode(resp_b, &resp->bindings[i]);
  231. if (err == ASN_ERR_EOBUF) {
  232. pdu->error_status = SNMP_ERR_TOOBIG;
  233. pdu->error_index = 0;
  234. snmp_pdu_free(resp);
  235. return (SNMP_RET_ERR);
  236. }
  237. if (err != ASN_ERR_OK) {
  238. if (TR(GET))
  239. snmp_debug("get: binding encoding: %u", err);
  240. pdu->error_status = SNMP_ERR_GENERR;
  241. pdu->error_index = i + 1;
  242. snmp_pdu_free(resp);
  243. return (SNMP_RET_ERR);
  244. }
  245. }
  246. return (snmp_fix_encoding(resp_b, resp));
  247. }
  248. static struct snmp_node *
  249. next_node(const struct snmp_value *value, int *pnext)
  250. {
  251. struct snmp_node *tp;
  252. if (TR(FIND))
  253. snmp_debug("next: searching %s",
  254. asn_oid2str_r(&value->var, oidbuf));
  255. *pnext = 0;
  256. for (tp = tree; tp < tree + tree_size; tp++) {
  257. if (asn_is_suboid(&tp->oid, &value->var)) {
  258. /* the tree OID is a sub-oid of the requested OID. */
  259. if (tp->type == SNMP_NODE_LEAF) {
  260. if (tp->oid.len == value->var.len) {
  261. /* request for scalar type */
  262. if (TR(FIND))
  263. snmp_debug("next: found scalar %s",
  264. asn_oid2str_r(&tp->oid, oidbuf));
  265. return (tp);
  266. }
  267. /* try next */
  268. } else {
  269. if (TR(FIND))
  270. snmp_debug("next: found column %s",
  271. asn_oid2str_r(&tp->oid, oidbuf));
  272. return (tp);
  273. }
  274. } else if (asn_is_suboid(&value->var, &tp->oid) ||
  275. asn_compare_oid(&tp->oid, &value->var) >= 0) {
  276. if (TR(FIND))
  277. snmp_debug("next: found %s",
  278. asn_oid2str_r(&tp->oid, oidbuf));
  279. *pnext = 1;
  280. return (tp);
  281. }
  282. }
  283. if (TR(FIND))
  284. snmp_debug("next: failed");
  285. return (NULL);
  286. }
  287. static enum snmp_ret
  288. do_getnext(struct context *context, const struct snmp_value *inb,
  289. struct snmp_value *outb, struct snmp_pdu *pdu)
  290. {
  291. const struct snmp_node *tp;
  292. int ret, next;
  293. if ((tp = next_node(inb, &next)) == NULL)
  294. goto eofMib;
  295. /* retain old variable if we are doing a GETNEXT on an exact
  296. * matched leaf only */
  297. if (tp->type == SNMP_NODE_LEAF || next)
  298. outb->var = tp->oid;
  299. else
  300. outb->var = inb->var;
  301. for (;;) {
  302. outb->syntax = tp->syntax;
  303. if (tp->type == SNMP_NODE_LEAF) {
  304. /* make a GET operation */
  305. outb->var.subs[outb->var.len++] = 0;
  306. ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
  307. tp->index, SNMP_OP_GET);
  308. } else {
  309. /* make a GETNEXT */
  310. ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
  311. tp->index, SNMP_OP_GETNEXT);
  312. }
  313. if (ret != SNMP_ERR_NOSUCHNAME) {
  314. /* got something */
  315. if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
  316. snmp_debug("getnext: %s returns %u",
  317. asn_oid2str(&outb->var), ret);
  318. break;
  319. }
  320. /* object has no data - try next */
  321. if (++tp == tree + tree_size)
  322. break;
  323. if (TR(GETNEXT))
  324. snmp_debug("getnext: no data - avancing to %s",
  325. asn_oid2str(&tp->oid));
  326. outb->var = tp->oid;
  327. }
  328. if (ret == SNMP_ERR_NOSUCHNAME) {
  329. eofMib:
  330. outb->var = inb->var;
  331. if (pdu->version == SNMP_V1) {
  332. pdu->error_status = SNMP_ERR_NOSUCHNAME;
  333. return (SNMP_RET_ERR);
  334. }
  335. outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
  336. } else if (ret != SNMP_ERR_NOERROR) {
  337. pdu->error_status = SNMP_ERR_GENERR;
  338. return (SNMP_RET_ERR);
  339. }
  340. return (SNMP_RET_OK);
  341. }
  342. /*
  343. * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
  344. * Build the response PDU on the fly. The return is:
  345. */
  346. enum snmp_ret
  347. snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
  348. struct snmp_pdu *resp, void *data)
  349. {
  350. struct context context;
  351. u_int i;
  352. enum asn_err err;
  353. enum snmp_ret result;
  354. memset(&context, 0, sizeof(context));
  355. context.ctx.data = data;
  356. snmp_pdu_create_response(pdu, resp);
  357. if (snmp_pdu_encode_header(resp_b, resp))
  358. return (SNMP_RET_IGN);
  359. for (i = 0; i < pdu->nbindings; i++) {
  360. result = do_getnext(&context, &pdu->bindings[i],
  361. &resp->bindings[i], pdu);
  362. if (result != SNMP_RET_OK) {
  363. pdu->error_index = i + 1;
  364. snmp_pdu_free(resp);
  365. return (result);
  366. }
  367. resp->nbindings++;
  368. err = snmp_binding_encode(resp_b, &resp->bindings[i]);
  369. if (err == ASN_ERR_EOBUF) {
  370. pdu->error_status = SNMP_ERR_TOOBIG;
  371. pdu->error_index = 0;
  372. snmp_pdu_free(resp);
  373. return (SNMP_RET_ERR);
  374. }
  375. if (err != ASN_ERR_OK) {
  376. if (TR(GET))
  377. snmp_debug("getnext: binding encoding: %u", err);
  378. pdu->error_status = SNMP_ERR_GENERR;
  379. pdu->error_index = i + 1;
  380. snmp_pdu_free(resp);
  381. return (SNMP_RET_ERR);
  382. }
  383. }
  384. return (snmp_fix_encoding(resp_b, resp));
  385. }
  386. enum snmp_ret
  387. snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
  388. struct snmp_pdu *resp, void *data)
  389. {
  390. struct context context;
  391. u_int i;
  392. int cnt;
  393. u_int non_rep;
  394. int eomib;
  395. enum snmp_ret result;
  396. enum asn_err err;
  397. memset(&context, 0, sizeof(context));
  398. context.ctx.data = data;
  399. snmp_pdu_create_response(pdu, resp);
  400. if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
  401. /* cannot even encode header - very bad */
  402. return (SNMP_RET_IGN);
  403. if ((non_rep = pdu->error_status) > pdu->nbindings)
  404. non_rep = pdu->nbindings;
  405. /* non-repeaters */
  406. for (i = 0; i < non_rep; i++) {
  407. result = do_getnext(&context, &pdu->bindings[i],
  408. &resp->bindings[resp->nbindings], pdu);
  409. if (result != SNMP_RET_OK) {
  410. pdu->error_index = i + 1;
  411. snmp_pdu_free(resp);
  412. return (result);
  413. }
  414. err = snmp_binding_encode(resp_b,
  415. &resp->bindings[resp->nbindings++]);
  416. if (err == ASN_ERR_EOBUF)
  417. goto done;
  418. if (err != ASN_ERR_OK) {
  419. if (TR(GET))
  420. snmp_debug("getnext: binding encoding: %u", err);
  421. pdu->error_status = SNMP_ERR_GENERR;
  422. pdu->error_index = i + 1;
  423. snmp_pdu_free(resp);
  424. return (SNMP_RET_ERR);
  425. }
  426. }
  427. if (non_rep == pdu->nbindings)
  428. goto done;
  429. /* repeates */
  430. for (cnt = 0; cnt < pdu->error_index; cnt++) {
  431. eomib = 1;
  432. for (i = non_rep; i < pdu->nbindings; i++) {
  433. if (cnt == 0)
  434. result = do_getnext(&context, &pdu->bindings[i],
  435. &resp->bindings[resp->nbindings], pdu);
  436. else
  437. result = do_getnext(&context,
  438. &resp->bindings[resp->nbindings -
  439. (pdu->nbindings - non_rep)],
  440. &resp->bindings[resp->nbindings], pdu);
  441. if (result != SNMP_RET_OK) {
  442. pdu->error_index = i + 1;
  443. snmp_pdu_free(resp);
  444. return (result);
  445. }
  446. if (resp->bindings[resp->nbindings].syntax !=
  447. SNMP_SYNTAX_ENDOFMIBVIEW)
  448. eomib = 0;
  449. err = snmp_binding_encode(resp_b,
  450. &resp->bindings[resp->nbindings++]);
  451. if (err == ASN_ERR_EOBUF)
  452. goto done;
  453. if (err != ASN_ERR_OK) {
  454. if (TR(GET))
  455. snmp_debug("getnext: binding encoding: %u", err);
  456. pdu->error_status = SNMP_ERR_GENERR;
  457. pdu->error_index = i + 1;
  458. snmp_pdu_free(resp);
  459. return (SNMP_RET_ERR);
  460. }
  461. }
  462. if (eomib)
  463. break;
  464. }
  465. done:
  466. return (snmp_fix_encoding(resp_b, resp));
  467. }
  468. /*
  469. * Rollback a SET operation. Failed index is 'i'.
  470. */
  471. static void
  472. rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
  473. {
  474. struct snmp_value *b;
  475. const struct snmp_node *np;
  476. int ret;
  477. while (i-- > 0) {
  478. b = &pdu->bindings[i];
  479. np = context->node[i];
  480. context->ctx.scratch = &context->scratch[i];
  481. ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
  482. SNMP_OP_ROLLBACK);
  483. if (ret != SNMP_ERR_NOERROR) {
  484. snmp_error("set: rollback failed (%d) on variable %s "
  485. "index %u", ret, asn_oid2str(&b->var), i);
  486. if (pdu->version != SNMP_V1) {
  487. pdu->error_status = SNMP_ERR_UNDO_FAILED;
  488. pdu->error_index = 0;
  489. }
  490. }
  491. }
  492. }
  493. /*
  494. * Commit dependencies.
  495. */
  496. int
  497. snmp_dep_commit(struct snmp_context *ctx)
  498. {
  499. struct context *context = (struct context *)ctx;
  500. int ret;
  501. TAILQ_FOREACH(context->depend, &context->dlist, link) {
  502. ctx->dep = &context->depend->dep;
  503. if (TR(SET))
  504. snmp_debug("set: dependency commit %s",
  505. asn_oid2str(&ctx->dep->obj));
  506. ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
  507. if (ret != SNMP_ERR_NOERROR) {
  508. if (TR(SET))
  509. snmp_debug("set: dependency failed %d", ret);
  510. return (ret);
  511. }
  512. }
  513. return (SNMP_ERR_NOERROR);
  514. }
  515. /*
  516. * Rollback dependencies
  517. */
  518. int
  519. snmp_dep_rollback(struct snmp_context *ctx)
  520. {
  521. struct context *context = (struct context *)ctx;
  522. int ret, ret1;
  523. char objbuf[ASN_OIDSTRLEN];
  524. char idxbuf[ASN_OIDSTRLEN];
  525. ret1 = SNMP_ERR_NOERROR;
  526. while ((context->depend =
  527. TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
  528. ctx->dep = &context->depend->dep;
  529. if (TR(SET))
  530. snmp_debug("set: dependency rollback %s",
  531. asn_oid2str(&ctx->dep->obj));
  532. ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
  533. if (ret != SNMP_ERR_NOERROR) {
  534. snmp_debug("set: dep rollback returns %u: %s %s", ret,
  535. asn_oid2str_r(&ctx->dep->obj, objbuf),
  536. asn_oid2str_r(&ctx->dep->idx, idxbuf));
  537. if (ret1 == SNMP_ERR_NOERROR)
  538. ret1 = ret;
  539. }
  540. }
  541. return (ret1);
  542. }
  543. void
  544. snmp_dep_finish(struct snmp_context *ctx)
  545. {
  546. struct context *context = (struct context *)ctx;
  547. struct depend *d;
  548. while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
  549. ctx->dep = &d->dep;
  550. (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
  551. TAILQ_REMOVE(&context->dlist, d, link);
  552. free(d);
  553. }
  554. }
  555. /*
  556. * Do a SET operation.
  557. */
  558. enum snmp_ret
  559. snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
  560. struct snmp_pdu *resp, void *data)
  561. {
  562. int ret;
  563. u_int i;
  564. enum asn_err asnerr;
  565. struct context context;
  566. const struct snmp_node *np;
  567. struct snmp_value *b;
  568. enum snmp_syntax except;
  569. memset(&context, 0, sizeof(context));
  570. TAILQ_INIT(&context.dlist);
  571. context.ctx.data = data;
  572. snmp_pdu_create_response(pdu, resp);
  573. if (snmp_pdu_encode_header(resp_b, resp))
  574. return (SNMP_RET_IGN);
  575. /*
  576. * 1. Find all nodes, check that they are writeable and
  577. * that the syntax is ok, copy over the binding to the response.
  578. */
  579. for (i = 0; i < pdu->nbindings; i++) {
  580. b = &pdu->bindings[i];
  581. if ((np = context.node[i] = find_node(b, &except)) == NULL) {
  582. /* not found altogether or LEAF with wrong index */
  583. if (TR(SET))
  584. snmp_debug("set: node not found %s",
  585. asn_oid2str_r(&b->var, oidbuf));
  586. if (pdu->version == SNMP_V1) {
  587. pdu->error_index = i + 1;
  588. pdu->error_status = SNMP_ERR_NOSUCHNAME;
  589. } else if ((np = find_subnode(b)) != NULL) {
  590. /* 2. intermediate object */
  591. pdu->error_index = i + 1;
  592. pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
  593. } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
  594. pdu->error_index = i + 1;
  595. pdu->error_status = SNMP_ERR_NO_ACCESS;
  596. } else {
  597. pdu->error_index = i + 1;
  598. pdu->error_status = SNMP_ERR_NO_CREATION;
  599. }
  600. snmp_pdu_free(resp);
  601. return (SNMP_RET_ERR);
  602. }
  603. /*
  604. * 2. write/createable?
  605. * Can check this for leafs only, because in v2 we have
  606. * to differentiate between NOT_WRITEABLE and NO_CREATION
  607. * and only the action routine for COLUMNS knows, whether
  608. * a column exists.
  609. */
  610. if (np->type == SNMP_NODE_LEAF &&
  611. !(np->flags & SNMP_NODE_CANSET)) {
  612. if (pdu->version == SNMP_V1) {
  613. pdu->error_index = i + 1;
  614. pdu->error_status = SNMP_ERR_NOSUCHNAME;
  615. } else {
  616. pdu->error_index = i + 1;
  617. pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
  618. }
  619. snmp_pdu_free(resp);
  620. return (SNMP_RET_ERR);
  621. }
  622. /*
  623. * 3. Ensure the right syntax
  624. */
  625. if (np->syntax != b->syntax) {
  626. if (pdu->version == SNMP_V1) {
  627. pdu->error_index = i + 1;
  628. pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
  629. } else {
  630. pdu->error_index = i + 1;
  631. pdu->error_status = SNMP_ERR_WRONG_TYPE;
  632. }
  633. snmp_pdu_free(resp);
  634. return (SNMP_RET_ERR);
  635. }
  636. /*
  637. * 4. Copy binding
  638. */
  639. if (snmp_value_copy(&resp->bindings[i], b)) {
  640. pdu->error_index = i + 1;
  641. pdu->error_status = SNMP_ERR_GENERR;
  642. snmp_pdu_free(resp);
  643. return (SNMP_RET_ERR);
  644. }
  645. asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
  646. if (asnerr == ASN_ERR_EOBUF) {
  647. pdu->error_index = i + 1;
  648. pdu->error_status = SNMP_ERR_TOOBIG;
  649. snmp_pdu_free(resp);
  650. return (SNMP_RET_ERR);
  651. } else if (asnerr != ASN_ERR_OK) {
  652. pdu->error_index = i + 1;
  653. pdu->error_status = SNMP_ERR_GENERR;
  654. snmp_pdu_free(resp);
  655. return (SNMP_RET_ERR);
  656. }
  657. resp->nbindings++;
  658. }
  659. context.ctx.code = SNMP_RET_OK;
  660. /*
  661. * 2. Call the SET method for each node. If a SET fails, rollback
  662. * everything. Map error codes depending on the version.
  663. */
  664. for (i = 0; i < pdu->nbindings; i++) {
  665. b = &pdu->bindings[i];
  666. np = context.node[i];
  667. context.ctx.var_index = i + 1;
  668. context.ctx.scratch = &context.scratch[i];
  669. ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
  670. SNMP_OP_SET);
  671. if (TR(SET))
  672. snmp_debug("set: action %s returns %d", np->name, ret);
  673. if (pdu->version == SNMP_V1) {
  674. switch (ret) {
  675. case SNMP_ERR_NO_ACCESS:
  676. ret = SNMP_ERR_NOSUCHNAME;
  677. break;
  678. case SNMP_ERR_WRONG_TYPE:
  679. /* should no happen */
  680. ret = SNMP_ERR_BADVALUE;
  681. break;
  682. case SNMP_ERR_WRONG_LENGTH:
  683. ret = SNMP_ERR_BADVALUE;
  684. break;
  685. case SNMP_ERR_WRONG_ENCODING:
  686. /* should not happen */
  687. ret = SNMP_ERR_BADVALUE;
  688. break;
  689. case SNMP_ERR_WRONG_VALUE:
  690. ret = SNMP_ERR_BADVALUE;
  691. break;
  692. case SNMP_ERR_NO_CREATION:
  693. ret = SNMP_ERR_NOSUCHNAME;
  694. break;
  695. case SNMP_ERR_INCONS_VALUE:
  696. ret = SNMP_ERR_BADVALUE;
  697. break;
  698. case SNMP_ERR_RES_UNAVAIL:
  699. ret = SNMP_ERR_GENERR;
  700. break;
  701. case SNMP_ERR_COMMIT_FAILED:
  702. ret = SNMP_ERR_GENERR;
  703. break;
  704. case SNMP_ERR_UNDO_FAILED:
  705. ret = SNMP_ERR_GENERR;
  706. break;
  707. case SNMP_ERR_AUTH_ERR:
  708. /* should not happen */
  709. ret = SNMP_ERR_GENERR;
  710. break;
  711. case SNMP_ERR_NOT_WRITEABLE:
  712. ret = SNMP_ERR_NOSUCHNAME;
  713. break;
  714. case SNMP_ERR_INCONS_NAME:
  715. ret = SNMP_ERR_BADVALUE;
  716. break;
  717. }
  718. }
  719. if (ret != SNMP_ERR_NOERROR) {
  720. pdu->error_index = i + 1;
  721. pdu->error_status = ret;
  722. rollback(&context, pdu, i);
  723. snmp_pdu_free(resp);
  724. context.ctx.code = SNMP_RET_ERR;
  725. goto errout;
  726. }
  727. }
  728. /*
  729. * 3. Call dependencies
  730. */
  731. if (TR(SET))
  732. snmp_debug("set: set operations ok");
  733. if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
  734. pdu->error_status = ret;
  735. pdu->error_index = context.ctx.var_index;
  736. if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
  737. if (pdu->version != SNMP_V1) {
  738. pdu->error_status = SNMP_ERR_UNDO_FAILED;
  739. pdu->error_index = 0;
  740. }
  741. }
  742. rollback(&context, pdu, i);
  743. snmp_pdu_free(resp);
  744. context.ctx.code = SNMP_RET_ERR;
  745. goto errout;
  746. }
  747. /*
  748. * 4. Commit and copy values from the original packet to the response.
  749. * This is not the commit operation from RFC 1905 but rather an
  750. * 'FREE RESOURCES' operation. It shouldn't fail.
  751. */
  752. if (TR(SET))
  753. snmp_debug("set: commiting");
  754. for (i = 0; i < pdu->nbindings; i++) {
  755. b = &resp->bindings[i];
  756. np = context.node[i];
  757. context.ctx.var_index = i + 1;
  758. context.ctx.scratch = &context.scratch[i];
  759. ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
  760. SNMP_OP_COMMIT);
  761. if (ret != SNMP_ERR_NOERROR)
  762. snmp_error("set: commit failed (%d) on"
  763. " variable %s index %u", ret,
  764. asn_oid2str_r(&b->var, oidbuf), i);
  765. }
  766. if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
  767. snmp_error("set: fix_encoding failed");
  768. snmp_pdu_free(resp);
  769. context.ctx.code = SNMP_RET_IGN;
  770. }
  771. /*
  772. * Done
  773. */
  774. errout:
  775. snmp_dep_finish(&context.ctx);
  776. if (TR(SET))
  777. snmp_debug("set: returning %d", context.ctx.code);
  778. return (context.ctx.code);
  779. }
  780. /*
  781. * Lookup a dependency. If it doesn't exist, create one
  782. */
  783. struct snmp_dependency *
  784. snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
  785. const struct asn_oid *idx, size_t len, snmp_depop_t func)
  786. {
  787. struct context *context;
  788. struct depend *d;
  789. context = (struct context *)(void *)
  790. ((char *)ctx - offsetof(struct context, ctx));
  791. if (TR(DEPEND)) {
  792. snmp_debug("depend: looking for %s", asn_oid2str(obj));
  793. if (idx)
  794. snmp_debug("depend: index is %s", asn_oid2str(idx));
  795. }
  796. TAILQ_FOREACH(d, &context->dlist, link)
  797. if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
  798. ((idx == NULL && d->dep.idx.len == 0) ||
  799. (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
  800. if(TR(DEPEND))
  801. snmp_debug("depend: found");
  802. return (&d->dep);
  803. }
  804. if(TR(DEPEND))
  805. snmp_debug("depend: creating");
  806. if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
  807. return (NULL);
  808. memset(&d->dep, 0, len);
  809. d->dep.obj = *obj;
  810. if (idx == NULL)
  811. d->dep.idx.len = 0;
  812. else
  813. d->dep.idx = *idx;
  814. d->len = len;
  815. d->func = func;
  816. TAILQ_INSERT_TAIL(&context->dlist, d, link);
  817. return (&d->dep);
  818. }
  819. /*
  820. * Make an error response from a PDU. We do this without decoding the
  821. * variable bindings. This means we can sent the junk back to a caller
  822. * that has sent us junk in the first place.
  823. */
  824. enum snmp_ret
  825. snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
  826. struct asn_buf *resp_b)
  827. {
  828. asn_len_t len;
  829. struct snmp_pdu resp;
  830. enum asn_err err;
  831. enum snmp_code code;
  832. memset(&resp, 0, sizeof(resp));
  833. if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
  834. return (SNMP_RET_IGN);
  835. if (pdu_b->asn_len < len)
  836. return (SNMP_RET_IGN);
  837. pdu_b->asn_len = len;
  838. err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
  839. if (ASN_ERR_STOPPED(err))
  840. return (SNMP_RET_IGN);
  841. if (pdu_b->asn_len < len)
  842. return (SNMP_RET_IGN);
  843. pdu_b->asn_len = len;
  844. /* now we have the bindings left - construct new message */
  845. resp.error_status = pdu->error_status;
  846. resp.error_index = pdu->error_index;
  847. resp.type = SNMP_PDU_RESPONSE;
  848. code = snmp_pdu_encode_header(resp_b, &resp);
  849. if (code != SNMP_CODE_OK)
  850. return (SNMP_RET_IGN);
  851. if (pdu_b->asn_len > resp_b->asn_len)
  852. /* too short */
  853. return (SNMP_RET_IGN);
  854. (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
  855. resp_b->asn_len -= pdu_b->asn_len;
  856. resp_b->asn_ptr += pdu_b->asn_len;
  857. code = snmp_fix_encoding(resp_b, &resp);
  858. if (code != SNMP_CODE_OK)
  859. return (SNMP_RET_IGN);
  860. return (SNMP_RET_OK);
  861. }
  862. static void
  863. snmp_debug_func(const char *fmt, ...)
  864. {
  865. va_list ap;
  866. va_start(ap, fmt);
  867. vfprintf(stderr, fmt, ap);
  868. va_end(ap);
  869. fprintf(stderr, "\n");
  870. }