/contrib/bsnmp/lib/snmpclient.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1956 lines · 1496 code · 222 blank · 238 comment · 446 complexity · 688689dd963a1824beb3bb1f47558b58 MD5 · raw file

  1. /*
  2. * Copyright (c) 2004-2005
  3. * Hartmut Brandt.
  4. * All rights reserved.
  5. * Copyright (c) 2001-2003
  6. * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
  7. * All rights reserved.
  8. *
  9. * Author: Harti Brandt <harti@freebsd.org>
  10. * Kendy Kutzner
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions
  14. * are met:
  15. * 1. Redistributions of source code must retain the above copyright
  16. * notice, this list of conditions and the following disclaimer.
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the distribution.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $
  34. *
  35. * Support functions for SNMP clients.
  36. */
  37. #include <sys/types.h>
  38. #include <sys/time.h>
  39. #include <sys/queue.h>
  40. #include <sys/socket.h>
  41. #include <sys/un.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <stddef.h>
  45. #include <stdarg.h>
  46. #include <string.h>
  47. #include <errno.h>
  48. #include <unistd.h>
  49. #include <fcntl.h>
  50. #include <netdb.h>
  51. #ifdef HAVE_STDINT_H
  52. #include <stdint.h>
  53. #elif defined(HAVE_INTTYPES_H)
  54. #include <inttypes.h>
  55. #endif
  56. #include <limits.h>
  57. #ifdef HAVE_ERR_H
  58. #include <err.h>
  59. #endif
  60. #include "support.h"
  61. #include "asn1.h"
  62. #include "snmp.h"
  63. #include "snmpclient.h"
  64. #include "snmppriv.h"
  65. /* global context */
  66. struct snmp_client snmp_client;
  67. /* List of all outstanding requests */
  68. struct sent_pdu {
  69. int reqid;
  70. struct snmp_pdu *pdu;
  71. struct timeval time;
  72. u_int retrycount;
  73. snmp_send_cb_f callback;
  74. void *arg;
  75. void *timeout_id;
  76. LIST_ENTRY(sent_pdu) entries;
  77. };
  78. LIST_HEAD(sent_pdu_list, sent_pdu);
  79. static struct sent_pdu_list sent_pdus;
  80. /*
  81. * Prototype table entry. All C-structure produced by the table function must
  82. * start with these two fields. This relies on the fact, that all TAILQ_ENTRY
  83. * are compatible with each other in the sense implied by ANSI-C.
  84. */
  85. struct entry {
  86. TAILQ_ENTRY(entry) link;
  87. uint64_t found;
  88. };
  89. TAILQ_HEAD(table, entry);
  90. /*
  91. * working list entry. This list is used to hold the Index part of the
  92. * table row's. The entry list and the work list parallel each other.
  93. */
  94. struct work {
  95. TAILQ_ENTRY(work) link;
  96. struct asn_oid index;
  97. };
  98. TAILQ_HEAD(worklist, work);
  99. /*
  100. * Table working data
  101. */
  102. struct tabwork {
  103. const struct snmp_table *descr;
  104. struct table *table;
  105. struct worklist worklist;
  106. uint32_t last_change;
  107. int first;
  108. u_int iter;
  109. snmp_table_cb_f callback;
  110. void *arg;
  111. struct snmp_pdu pdu;
  112. };
  113. /*
  114. * Set the error string
  115. */
  116. static void
  117. seterr(struct snmp_client *sc, const char *fmt, ...)
  118. {
  119. va_list ap;
  120. va_start(ap, fmt);
  121. vsnprintf(sc->error, sizeof(sc->error), fmt, ap);
  122. va_end(ap);
  123. }
  124. /*
  125. * Free the entire table and work list. If table is NULL only the worklist
  126. * is freed.
  127. */
  128. static void
  129. table_free(struct tabwork *work, int all)
  130. {
  131. struct work *w;
  132. struct entry *e;
  133. const struct snmp_table_entry *d;
  134. u_int i;
  135. while ((w = TAILQ_FIRST(&work->worklist)) != NULL) {
  136. TAILQ_REMOVE(&work->worklist, w, link);
  137. free(w);
  138. }
  139. if (all == 0)
  140. return;
  141. while ((e = TAILQ_FIRST(work->table)) != NULL) {
  142. for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL;
  143. i++) {
  144. d = &work->descr->entries[i];
  145. if (d->syntax == SNMP_SYNTAX_OCTETSTRING &&
  146. (e->found & ((uint64_t)1 << i)))
  147. free(*(void **)(void *)
  148. ((u_char *)e + d->offset));
  149. }
  150. TAILQ_REMOVE(work->table, e, link);
  151. free(e);
  152. }
  153. }
  154. /*
  155. * Find the correct table entry for the given variable. If non exists,
  156. * create one.
  157. */
  158. static struct entry *
  159. table_find(struct tabwork *work, const struct asn_oid *var)
  160. {
  161. struct entry *e, *e1;
  162. struct work *w, *w1;
  163. u_int i, p, j;
  164. size_t len;
  165. u_char *ptr;
  166. struct asn_oid oid;
  167. /* get index */
  168. asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len);
  169. e = TAILQ_FIRST(work->table);
  170. w = TAILQ_FIRST(&work->worklist);
  171. while (e != NULL) {
  172. if (asn_compare_oid(&w->index, &oid) == 0)
  173. return (e);
  174. e = TAILQ_NEXT(e, link);
  175. w = TAILQ_NEXT(w, link);
  176. }
  177. /* Not found create new one */
  178. if ((e = malloc(work->descr->entry_size)) == NULL) {
  179. seterr(&snmp_client, "no memory for table entry");
  180. return (NULL);
  181. }
  182. if ((w = malloc(sizeof(*w))) == NULL) {
  183. seterr(&snmp_client, "no memory for table entry");
  184. free(e);
  185. return (NULL);
  186. }
  187. w->index = oid;
  188. memset(e, 0, work->descr->entry_size);
  189. /* decode index */
  190. p = work->descr->table.len + 2;
  191. for (i = 0; i < work->descr->index_size; i++) {
  192. switch (work->descr->entries[i].syntax) {
  193. case SNMP_SYNTAX_INTEGER:
  194. if (var->len < p + 1) {
  195. seterr(&snmp_client, "bad index: need integer");
  196. goto err;
  197. }
  198. if (var->subs[p] > INT32_MAX) {
  199. seterr(&snmp_client,
  200. "bad index: integer too large");
  201. goto err;
  202. }
  203. *(int32_t *)(void *)((u_char *)e +
  204. work->descr->entries[i].offset) = var->subs[p++];
  205. break;
  206. case SNMP_SYNTAX_OCTETSTRING:
  207. if (var->len < p + 1) {
  208. seterr(&snmp_client,
  209. "bad index: need string length");
  210. goto err;
  211. }
  212. len = var->subs[p++];
  213. if (var->len < p + len) {
  214. seterr(&snmp_client,
  215. "bad index: string too short");
  216. goto err;
  217. }
  218. if ((ptr = malloc(len + 1)) == NULL) {
  219. seterr(&snmp_client,
  220. "no memory for index string");
  221. goto err;
  222. }
  223. for (j = 0; j < len; j++) {
  224. if (var->subs[p] > UCHAR_MAX) {
  225. seterr(&snmp_client,
  226. "bad index: char too large");
  227. free(ptr);
  228. goto err;
  229. }
  230. ptr[j] = var->subs[p++];
  231. }
  232. ptr[j] = '\0';
  233. *(u_char **)(void *)((u_char *)e +
  234. work->descr->entries[i].offset) = ptr;
  235. *(size_t *)(void *)((u_char *)e +
  236. work->descr->entries[i].offset + sizeof(u_char *))
  237. = len;
  238. break;
  239. case SNMP_SYNTAX_OID:
  240. if (var->len < p + 1) {
  241. seterr(&snmp_client,
  242. "bad index: need oid length");
  243. goto err;
  244. }
  245. oid.len = var->subs[p++];
  246. if (var->len < p + oid.len) {
  247. seterr(&snmp_client,
  248. "bad index: oid too short");
  249. goto err;
  250. }
  251. for (j = 0; j < oid.len; j++)
  252. oid.subs[j] = var->subs[p++];
  253. *(struct asn_oid *)(void *)((u_char *)e +
  254. work->descr->entries[i].offset) = oid;
  255. break;
  256. case SNMP_SYNTAX_IPADDRESS:
  257. if (var->len < p + 4) {
  258. seterr(&snmp_client,
  259. "bad index: need ip-address");
  260. goto err;
  261. }
  262. for (j = 0; j < 4; j++) {
  263. if (var->subs[p] > 0xff) {
  264. seterr(&snmp_client,
  265. "bad index: ipaddress too large");
  266. goto err;
  267. }
  268. ((u_char *)e +
  269. work->descr->entries[i].offset)[j] =
  270. var->subs[p++];
  271. }
  272. break;
  273. case SNMP_SYNTAX_GAUGE:
  274. if (var->len < p + 1) {
  275. seterr(&snmp_client,
  276. "bad index: need unsigned");
  277. goto err;
  278. }
  279. if (var->subs[p] > UINT32_MAX) {
  280. seterr(&snmp_client,
  281. "bad index: unsigned too large");
  282. goto err;
  283. }
  284. *(uint32_t *)(void *)((u_char *)e +
  285. work->descr->entries[i].offset) = var->subs[p++];
  286. break;
  287. case SNMP_SYNTAX_COUNTER:
  288. case SNMP_SYNTAX_TIMETICKS:
  289. case SNMP_SYNTAX_COUNTER64:
  290. case SNMP_SYNTAX_NULL:
  291. case SNMP_SYNTAX_NOSUCHOBJECT:
  292. case SNMP_SYNTAX_NOSUCHINSTANCE:
  293. case SNMP_SYNTAX_ENDOFMIBVIEW:
  294. abort();
  295. }
  296. e->found |= (uint64_t)1 << i;
  297. }
  298. /* link into the correct place */
  299. e1 = TAILQ_FIRST(work->table);
  300. w1 = TAILQ_FIRST(&work->worklist);
  301. while (e1 != NULL) {
  302. if (asn_compare_oid(&w1->index, &w->index) > 0)
  303. break;
  304. e1 = TAILQ_NEXT(e1, link);
  305. w1 = TAILQ_NEXT(w1, link);
  306. }
  307. if (e1 == NULL) {
  308. TAILQ_INSERT_TAIL(work->table, e, link);
  309. TAILQ_INSERT_TAIL(&work->worklist, w, link);
  310. } else {
  311. TAILQ_INSERT_BEFORE(e1, e, link);
  312. TAILQ_INSERT_BEFORE(w1, w, link);
  313. }
  314. return (e);
  315. err:
  316. /*
  317. * Error happend. Free all octet string index parts and the entry
  318. * itself.
  319. */
  320. for (i = 0; i < work->descr->index_size; i++) {
  321. if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING &&
  322. (e->found & ((uint64_t)1 << i)))
  323. free(*(void **)(void *)((u_char *)e +
  324. work->descr->entries[i].offset));
  325. }
  326. free(e);
  327. free(w);
  328. return (NULL);
  329. }
  330. /*
  331. * Assign the value
  332. */
  333. static int
  334. table_value(const struct snmp_table *descr, struct entry *e,
  335. const struct snmp_value *b)
  336. {
  337. u_int i;
  338. u_char *ptr;
  339. for (i = descr->index_size;
  340. descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++)
  341. if (descr->entries[i].subid ==
  342. b->var.subs[descr->table.len + 1])
  343. break;
  344. if (descr->entries[i].syntax == SNMP_SYNTAX_NULL)
  345. return (0);
  346. /* check syntax */
  347. if (b->syntax != descr->entries[i].syntax) {
  348. seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax,
  349. descr->entries[i].syntax);
  350. return (-1);
  351. }
  352. switch (b->syntax) {
  353. case SNMP_SYNTAX_INTEGER:
  354. *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
  355. b->v.integer;
  356. break;
  357. case SNMP_SYNTAX_OCTETSTRING:
  358. if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) {
  359. seterr(&snmp_client, "no memory for string");
  360. return (-1);
  361. }
  362. memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len);
  363. ptr[b->v.octetstring.len] = '\0';
  364. *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) =
  365. ptr;
  366. *(size_t *)(void *)((u_char *)e + descr->entries[i].offset +
  367. sizeof(u_char *)) = b->v.octetstring.len;
  368. break;
  369. case SNMP_SYNTAX_OID:
  370. *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) =
  371. b->v.oid;
  372. break;
  373. case SNMP_SYNTAX_IPADDRESS:
  374. memcpy((u_char *)e + descr->entries[i].offset,
  375. b->v.ipaddress, 4);
  376. break;
  377. case SNMP_SYNTAX_COUNTER:
  378. case SNMP_SYNTAX_GAUGE:
  379. case SNMP_SYNTAX_TIMETICKS:
  380. *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
  381. b->v.uint32;
  382. break;
  383. case SNMP_SYNTAX_COUNTER64:
  384. *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) =
  385. b->v.counter64;
  386. break;
  387. case SNMP_SYNTAX_NULL:
  388. case SNMP_SYNTAX_NOSUCHOBJECT:
  389. case SNMP_SYNTAX_NOSUCHINSTANCE:
  390. case SNMP_SYNTAX_ENDOFMIBVIEW:
  391. abort();
  392. }
  393. e->found |= (uint64_t)1 << i;
  394. return (0);
  395. }
  396. /*
  397. * Initialize the first PDU to send
  398. */
  399. static void
  400. table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu)
  401. {
  402. if (snmp_client.version == SNMP_V1)
  403. snmp_pdu_create(pdu, SNMP_PDU_GETNEXT);
  404. else {
  405. snmp_pdu_create(pdu, SNMP_PDU_GETBULK);
  406. pdu->error_index = 10;
  407. }
  408. if (descr->last_change.len != 0) {
  409. pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
  410. pdu->bindings[pdu->nbindings].var = descr->last_change;
  411. pdu->nbindings++;
  412. if (pdu->version != SNMP_V1)
  413. pdu->error_status++;
  414. }
  415. pdu->bindings[pdu->nbindings].var = descr->table;
  416. pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
  417. pdu->nbindings++;
  418. }
  419. /*
  420. * Return code:
  421. * 0 - End Of Table
  422. * -1 - Error
  423. * -2 - Last change changed - again
  424. * +1 - ok, continue
  425. */
  426. static int
  427. table_check_response(struct tabwork *work, const struct snmp_pdu *resp)
  428. {
  429. const struct snmp_value *b;
  430. struct entry *e;
  431. if (resp->error_status != SNMP_ERR_NOERROR) {
  432. if (snmp_client.version == SNMP_V1 &&
  433. resp->error_status == SNMP_ERR_NOSUCHNAME &&
  434. resp->error_index ==
  435. (work->descr->last_change.len == 0) ? 1 : 2)
  436. /* EOT */
  437. return (0);
  438. /* Error */
  439. seterr(&snmp_client, "error fetching table: status=%d index=%d",
  440. resp->error_status, resp->error_index);
  441. return (-1);
  442. }
  443. for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) {
  444. if (work->descr->last_change.len != 0 && b == resp->bindings) {
  445. if (!asn_is_suboid(&work->descr->last_change, &b->var) ||
  446. b->var.len != work->descr->last_change.len + 1 ||
  447. b->var.subs[work->descr->last_change.len] != 0) {
  448. seterr(&snmp_client,
  449. "last_change: bad response");
  450. return (-1);
  451. }
  452. if (b->syntax != SNMP_SYNTAX_TIMETICKS) {
  453. seterr(&snmp_client,
  454. "last_change: bad syntax %u", b->syntax);
  455. return (-1);
  456. }
  457. if (work->first) {
  458. work->last_change = b->v.uint32;
  459. work->first = 0;
  460. } else if (work->last_change != b->v.uint32) {
  461. if (++work->iter >= work->descr->max_iter) {
  462. seterr(&snmp_client,
  463. "max iteration count exceeded");
  464. return (-1);
  465. }
  466. table_free(work, 1);
  467. return (-2);
  468. }
  469. continue;
  470. }
  471. if (!asn_is_suboid(&work->descr->table, &b->var) ||
  472. b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
  473. return (0);
  474. if ((e = table_find(work, &b->var)) == NULL)
  475. return (-1);
  476. if (table_value(work->descr, e, b))
  477. return (-1);
  478. }
  479. return (+1);
  480. }
  481. /*
  482. * Check table consistency
  483. */
  484. static int
  485. table_check_cons(struct tabwork *work)
  486. {
  487. struct entry *e;
  488. TAILQ_FOREACH(e, work->table, link)
  489. if ((e->found & work->descr->req_mask) !=
  490. work->descr->req_mask) {
  491. if (work->descr->last_change.len == 0) {
  492. if (++work->iter >= work->descr->max_iter) {
  493. seterr(&snmp_client,
  494. "max iteration count exceeded");
  495. return (-1);
  496. }
  497. return (-2);
  498. }
  499. seterr(&snmp_client, "inconsistency detected %llx %llx",
  500. e->found, work->descr->req_mask);
  501. return (-1);
  502. }
  503. return (0);
  504. }
  505. /*
  506. * Fetch a table. Returns 0 if ok, -1 on errors.
  507. * This is the synchronous variant.
  508. */
  509. int
  510. snmp_table_fetch(const struct snmp_table *descr, void *list)
  511. {
  512. struct snmp_pdu resp;
  513. struct tabwork work;
  514. int ret;
  515. work.descr = descr;
  516. work.table = (struct table *)list;
  517. work.iter = 0;
  518. TAILQ_INIT(work.table);
  519. TAILQ_INIT(&work.worklist);
  520. work.callback = NULL;
  521. work.arg = NULL;
  522. again:
  523. /*
  524. * We come to this label when the code detects that the table
  525. * has changed while fetching it.
  526. */
  527. work.first = 1;
  528. work.last_change = 0;
  529. table_init_pdu(descr, &work.pdu);
  530. for (;;) {
  531. if (snmp_dialog(&work.pdu, &resp)) {
  532. table_free(&work, 1);
  533. return (-1);
  534. }
  535. if ((ret = table_check_response(&work, &resp)) == 0) {
  536. snmp_pdu_free(&resp);
  537. break;
  538. }
  539. if (ret == -1) {
  540. snmp_pdu_free(&resp);
  541. table_free(&work, 1);
  542. return (-1);
  543. }
  544. if (ret == -2) {
  545. snmp_pdu_free(&resp);
  546. goto again;
  547. }
  548. work.pdu.bindings[work.pdu.nbindings - 1].var =
  549. resp.bindings[resp.nbindings - 1].var;
  550. snmp_pdu_free(&resp);
  551. }
  552. if ((ret = table_check_cons(&work)) == -1) {
  553. table_free(&work, 1);
  554. return (-1);
  555. }
  556. if (ret == -2) {
  557. table_free(&work, 1);
  558. goto again;
  559. }
  560. /*
  561. * Free index list
  562. */
  563. table_free(&work, 0);
  564. return (0);
  565. }
  566. /*
  567. * Callback for table
  568. */
  569. static void
  570. table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg)
  571. {
  572. struct tabwork *work = arg;
  573. int ret;
  574. if (resp == NULL) {
  575. /* timeout */
  576. seterr(&snmp_client, "no response to fetch table request");
  577. table_free(work, 1);
  578. work->callback(work->table, work->arg, -1);
  579. free(work);
  580. return;
  581. }
  582. if ((ret = table_check_response(work, resp)) == 0) {
  583. /* EOT */
  584. snmp_pdu_free(resp);
  585. if ((ret = table_check_cons(work)) == -1) {
  586. /* error happend */
  587. table_free(work, 1);
  588. work->callback(work->table, work->arg, -1);
  589. free(work);
  590. return;
  591. }
  592. if (ret == -2) {
  593. /* restart */
  594. again:
  595. table_free(work, 1);
  596. work->first = 1;
  597. work->last_change = 0;
  598. table_init_pdu(work->descr, &work->pdu);
  599. if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
  600. work->callback(work->table, work->arg, -1);
  601. free(work);
  602. return;
  603. }
  604. return;
  605. }
  606. /*
  607. * Free index list
  608. */
  609. table_free(work, 0);
  610. work->callback(work->table, work->arg, 0);
  611. free(work);
  612. return;
  613. }
  614. if (ret == -1) {
  615. /* error */
  616. snmp_pdu_free(resp);
  617. table_free(work, 1);
  618. work->callback(work->table, work->arg, -1);
  619. free(work);
  620. return;
  621. }
  622. if (ret == -2) {
  623. /* again */
  624. snmp_pdu_free(resp);
  625. goto again;
  626. }
  627. /* next part */
  628. work->pdu.bindings[work->pdu.nbindings - 1].var =
  629. resp->bindings[resp->nbindings - 1].var;
  630. snmp_pdu_free(resp);
  631. if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
  632. table_free(work, 1);
  633. work->callback(work->table, work->arg, -1);
  634. free(work);
  635. return;
  636. }
  637. }
  638. int
  639. snmp_table_fetch_async(const struct snmp_table *descr, void *list,
  640. snmp_table_cb_f func, void *arg)
  641. {
  642. struct tabwork *work;
  643. if ((work = malloc(sizeof(*work))) == NULL) {
  644. seterr(&snmp_client, "%s", strerror(errno));
  645. return (-1);
  646. }
  647. work->descr = descr;
  648. work->table = (struct table *)list;
  649. work->iter = 0;
  650. TAILQ_INIT(work->table);
  651. TAILQ_INIT(&work->worklist);
  652. work->callback = func;
  653. work->arg = arg;
  654. /*
  655. * Start by sending the first PDU
  656. */
  657. work->first = 1;
  658. work->last_change = 0;
  659. table_init_pdu(descr, &work->pdu);
  660. if (snmp_pdu_send(&work->pdu, table_cb, work) == -1)
  661. return (-1);
  662. return (0);
  663. }
  664. /*
  665. * Append an index to an oid
  666. */
  667. int
  668. snmp_oid_append(struct asn_oid *oid, const char *fmt, ...)
  669. {
  670. va_list va;
  671. int size;
  672. char *nextptr;
  673. const u_char *str;
  674. size_t len;
  675. struct in_addr ina;
  676. int ret;
  677. va_start(va, fmt);
  678. size = 0;
  679. ret = 0;
  680. while (*fmt != '\0') {
  681. switch (*fmt++) {
  682. case 'i':
  683. /* just an integer more */
  684. if (oid->len + 1 > ASN_MAXOIDLEN) {
  685. warnx("%s: OID too long for integer", __func__);
  686. ret = -1;
  687. break;
  688. }
  689. oid->subs[oid->len++] = va_arg(va, asn_subid_t);
  690. break;
  691. case 'a':
  692. /* append an IP address */
  693. if (oid->len + 4 > ASN_MAXOIDLEN) {
  694. warnx("%s: OID too long for ip-addr", __func__);
  695. ret = -1;
  696. break;
  697. }
  698. ina = va_arg(va, struct in_addr);
  699. ina.s_addr = ntohl(ina.s_addr);
  700. oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff;
  701. oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff;
  702. oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff;
  703. oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff;
  704. break;
  705. case 's':
  706. /* append a null-terminated string,
  707. * length is computed */
  708. str = (const u_char *)va_arg(va, const char *);
  709. len = strlen((const char *)str);
  710. if (oid->len + len + 1 > ASN_MAXOIDLEN) {
  711. warnx("%s: OID too long for string", __func__);
  712. ret = -1;
  713. break;
  714. }
  715. oid->subs[oid->len++] = len;
  716. while (len--)
  717. oid->subs[oid->len++] = *str++;
  718. break;
  719. case '(':
  720. /* the integer value between ( and ) is stored
  721. * in size */
  722. size = strtol(fmt, &nextptr, 10);
  723. if (*nextptr != ')')
  724. abort();
  725. fmt = ++nextptr;
  726. break;
  727. case 'b':
  728. /* append `size` characters */
  729. str = (const u_char *)va_arg(va, const char *);
  730. if (oid->len + size > ASN_MAXOIDLEN) {
  731. warnx("%s: OID too long for string", __func__);
  732. ret = -1;
  733. break;
  734. }
  735. while (size--)
  736. oid->subs[oid->len++] = *str++;
  737. break;
  738. case 'c':
  739. /* get size and the octets from the arguments */
  740. size = va_arg(va, size_t);
  741. str = va_arg(va, const u_char *);
  742. if (oid->len + size + 1 > ASN_MAXOIDLEN) {
  743. warnx("%s: OID too long for string", __func__);
  744. ret = -1;
  745. break;
  746. }
  747. oid->subs[oid->len++] = size;
  748. while (size--)
  749. oid->subs[oid->len++] = *str++;
  750. break;
  751. default:
  752. abort();
  753. }
  754. }
  755. va_end(va);
  756. return (ret);
  757. }
  758. /*
  759. * Initialize a client structure
  760. */
  761. void
  762. snmp_client_init(struct snmp_client *c)
  763. {
  764. memset(c, 0, sizeof(*c));
  765. c->version = SNMP_V2c;
  766. c->trans = SNMP_TRANS_UDP;
  767. c->chost = NULL;
  768. c->cport = NULL;
  769. strcpy(c->read_community, "public");
  770. strcpy(c->write_community, "private");
  771. c->security_model = SNMP_SECMODEL_USM;
  772. strcpy(c->cname, "");
  773. c->timeout.tv_sec = 3;
  774. c->timeout.tv_usec = 0;
  775. c->retries = 3;
  776. c->dump_pdus = 0;
  777. c->txbuflen = c->rxbuflen = 10000;
  778. c->fd = -1;
  779. c->max_reqid = INT32_MAX;
  780. c->min_reqid = 0;
  781. c->next_reqid = 0;
  782. c->engine.max_msg_size = 1500; /* XXX */
  783. }
  784. /*
  785. * Open UDP client socket
  786. */
  787. static int
  788. open_client_udp(const char *host, const char *port)
  789. {
  790. int error;
  791. char *ptr;
  792. struct addrinfo hints, *res0, *res;
  793. /* copy host- and portname */
  794. if (snmp_client.chost == NULL) {
  795. if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST)))
  796. == NULL) {
  797. seterr(&snmp_client, "%s", strerror(errno));
  798. return (-1);
  799. }
  800. strcpy(snmp_client.chost, DEFAULT_HOST);
  801. }
  802. if (host != NULL) {
  803. if ((ptr = malloc(1 + strlen(host))) == NULL) {
  804. seterr(&snmp_client, "%s", strerror(errno));
  805. return (-1);
  806. }
  807. free(snmp_client.chost);
  808. snmp_client.chost = ptr;
  809. strcpy(snmp_client.chost, host);
  810. }
  811. if (snmp_client.cport == NULL) {
  812. if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT)))
  813. == NULL) {
  814. seterr(&snmp_client, "%s", strerror(errno));
  815. return (-1);
  816. }
  817. strcpy(snmp_client.cport, DEFAULT_PORT);
  818. }
  819. if (port != NULL) {
  820. if ((ptr = malloc(1 + strlen(port))) == NULL) {
  821. seterr(&snmp_client, "%s", strerror(errno));
  822. return (-1);
  823. }
  824. free(snmp_client.cport);
  825. snmp_client.cport = ptr;
  826. strcpy(snmp_client.cport, port);
  827. }
  828. /* open connection */
  829. memset(&hints, 0, sizeof(hints));
  830. hints.ai_flags = AI_CANONNAME;
  831. hints.ai_family = AF_INET;
  832. hints.ai_socktype = SOCK_DGRAM;
  833. hints.ai_protocol = 0;
  834. error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0);
  835. if (error != 0) {
  836. seterr(&snmp_client, "%s: %s", snmp_client.chost,
  837. gai_strerror(error));
  838. return (-1);
  839. }
  840. res = res0;
  841. for (;;) {
  842. if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype,
  843. res->ai_protocol)) == -1) {
  844. if ((res = res->ai_next) == NULL) {
  845. seterr(&snmp_client, "%s", strerror(errno));
  846. freeaddrinfo(res0);
  847. return (-1);
  848. }
  849. } else if (connect(snmp_client.fd, res->ai_addr,
  850. res->ai_addrlen) == -1) {
  851. if ((res = res->ai_next) == NULL) {
  852. seterr(&snmp_client, "%s", strerror(errno));
  853. freeaddrinfo(res0);
  854. return (-1);
  855. }
  856. } else
  857. break;
  858. }
  859. freeaddrinfo(res0);
  860. return (0);
  861. }
  862. static void
  863. remove_local(void)
  864. {
  865. (void)remove(snmp_client.local_path);
  866. }
  867. /*
  868. * Open local socket
  869. */
  870. static int
  871. open_client_local(const char *path)
  872. {
  873. struct sockaddr_un sa;
  874. char *ptr;
  875. int stype;
  876. if (snmp_client.chost == NULL) {
  877. if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL)))
  878. == NULL) {
  879. seterr(&snmp_client, "%s", strerror(errno));
  880. return (-1);
  881. }
  882. strcpy(snmp_client.chost, DEFAULT_LOCAL);
  883. }
  884. if (path != NULL) {
  885. if ((ptr = malloc(1 + strlen(path))) == NULL) {
  886. seterr(&snmp_client, "%s", strerror(errno));
  887. return (-1);
  888. }
  889. free(snmp_client.chost);
  890. snmp_client.chost = ptr;
  891. strcpy(snmp_client.chost, path);
  892. }
  893. if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM)
  894. stype = SOCK_DGRAM;
  895. else
  896. stype = SOCK_STREAM;
  897. if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) {
  898. seterr(&snmp_client, "%s", strerror(errno));
  899. return (-1);
  900. }
  901. snprintf(snmp_client.local_path, sizeof(snmp_client.local_path),
  902. "%s", SNMP_LOCAL_PATH);
  903. if (mktemp(snmp_client.local_path) == NULL) {
  904. seterr(&snmp_client, "%s", strerror(errno));
  905. (void)close(snmp_client.fd);
  906. snmp_client.fd = -1;
  907. return (-1);
  908. }
  909. sa.sun_family = AF_LOCAL;
  910. sa.sun_len = sizeof(sa);
  911. strcpy(sa.sun_path, snmp_client.local_path);
  912. if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
  913. seterr(&snmp_client, "%s", strerror(errno));
  914. (void)close(snmp_client.fd);
  915. snmp_client.fd = -1;
  916. (void)remove(snmp_client.local_path);
  917. return (-1);
  918. }
  919. atexit(remove_local);
  920. sa.sun_family = AF_LOCAL;
  921. sa.sun_len = offsetof(struct sockaddr_un, sun_path) +
  922. strlen(snmp_client.chost);
  923. strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1);
  924. sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
  925. if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) {
  926. seterr(&snmp_client, "%s", strerror(errno));
  927. (void)close(snmp_client.fd);
  928. snmp_client.fd = -1;
  929. (void)remove(snmp_client.local_path);
  930. return (-1);
  931. }
  932. return (0);
  933. }
  934. /*
  935. * SNMP_OPEN
  936. */
  937. int
  938. snmp_open(const char *host, const char *port, const char *readcomm,
  939. const char *writecomm)
  940. {
  941. struct timeval tout;
  942. /* still open ? */
  943. if (snmp_client.fd != -1) {
  944. errno = EBUSY;
  945. seterr(&snmp_client, "%s", strerror(errno));
  946. return (-1);
  947. }
  948. /* copy community strings */
  949. if (readcomm != NULL)
  950. strlcpy(snmp_client.read_community, readcomm,
  951. sizeof(snmp_client.read_community));
  952. if (writecomm != NULL)
  953. strlcpy(snmp_client.write_community, writecomm,
  954. sizeof(snmp_client.write_community));
  955. switch (snmp_client.trans) {
  956. case SNMP_TRANS_UDP:
  957. if (open_client_udp(host, port))
  958. return (-1);
  959. break;
  960. case SNMP_TRANS_LOC_DGRAM:
  961. case SNMP_TRANS_LOC_STREAM:
  962. if (open_client_local(host))
  963. return (-1);
  964. break;
  965. default:
  966. seterr(&snmp_client, "bad transport mapping");
  967. return (-1);
  968. }
  969. tout.tv_sec = 0;
  970. tout.tv_usec = 0;
  971. if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO,
  972. &tout, sizeof(struct timeval)) == -1) {
  973. seterr(&snmp_client, "%s", strerror(errno));
  974. (void)close(snmp_client.fd);
  975. snmp_client.fd = -1;
  976. if (snmp_client.local_path[0] != '\0')
  977. (void)remove(snmp_client.local_path);
  978. return (-1);
  979. }
  980. /* initialize list */
  981. LIST_INIT(&sent_pdus);
  982. return (0);
  983. }
  984. /*
  985. * SNMP_CLOSE
  986. *
  987. * closes connection to snmp server
  988. * - function cannot fail
  989. * - clears connection
  990. * - clears list of sent pdus
  991. *
  992. * input:
  993. * void
  994. * return:
  995. * void
  996. */
  997. void
  998. snmp_close(void)
  999. {
  1000. struct sent_pdu *p1;
  1001. if (snmp_client.fd != -1) {
  1002. (void)close(snmp_client.fd);
  1003. snmp_client.fd = -1;
  1004. if (snmp_client.local_path[0] != '\0')
  1005. (void)remove(snmp_client.local_path);
  1006. }
  1007. while(!LIST_EMPTY(&sent_pdus)){
  1008. p1 = LIST_FIRST(&sent_pdus);
  1009. if (p1->timeout_id != NULL)
  1010. snmp_client.timeout_stop(p1->timeout_id);
  1011. LIST_REMOVE(p1, entries);
  1012. free(p1);
  1013. }
  1014. free(snmp_client.chost);
  1015. free(snmp_client.cport);
  1016. }
  1017. /*
  1018. * initialize a snmp_pdu structure
  1019. */
  1020. void
  1021. snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
  1022. {
  1023. memset(pdu, 0, sizeof(struct snmp_pdu));
  1024. if (op == SNMP_PDU_SET)
  1025. strlcpy(pdu->community, snmp_client.write_community,
  1026. sizeof(pdu->community));
  1027. else
  1028. strlcpy(pdu->community, snmp_client.read_community,
  1029. sizeof(pdu->community));
  1030. pdu->type = op;
  1031. pdu->version = snmp_client.version;
  1032. pdu->error_status = 0;
  1033. pdu->error_index = 0;
  1034. pdu->nbindings = 0;
  1035. if (snmp_client.version != SNMP_V3)
  1036. return;
  1037. pdu->identifier = ++snmp_client.identifier;
  1038. pdu->engine.max_msg_size = snmp_client.engine.max_msg_size;
  1039. pdu->flags = 0;
  1040. pdu->security_model = snmp_client.security_model;
  1041. if (snmp_client.security_model == SNMP_SECMODEL_USM) {
  1042. memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine));
  1043. memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user));
  1044. snmp_pdu_init_secparams(pdu);
  1045. } else
  1046. seterr(&snmp_client, "unknown security model");
  1047. if (snmp_client.clen > 0) {
  1048. memcpy(pdu->context_engine, snmp_client.cengine,
  1049. snmp_client.clen);
  1050. pdu->context_engine_len = snmp_client.clen;
  1051. } else {
  1052. memcpy(pdu->context_engine, snmp_client.engine.engine_id,
  1053. snmp_client.engine.engine_len);
  1054. pdu->context_engine_len = snmp_client.engine.engine_len;
  1055. }
  1056. strlcpy(pdu->context_name, snmp_client.cname,
  1057. sizeof(pdu->context_name));
  1058. }
  1059. /* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */
  1060. /* added 10/04/02 by kek: check for MAX_BINDINGS */
  1061. int
  1062. snmp_add_binding(struct snmp_v1_pdu *pdu, ...)
  1063. {
  1064. va_list ap;
  1065. const struct asn_oid *oid;
  1066. u_int ret;
  1067. va_start(ap, pdu);
  1068. ret = pdu->nbindings;
  1069. while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) {
  1070. if (pdu->nbindings >= SNMP_MAX_BINDINGS){
  1071. va_end(ap);
  1072. return (-1);
  1073. }
  1074. pdu->bindings[pdu->nbindings].var = *oid;
  1075. pdu->bindings[pdu->nbindings].syntax =
  1076. va_arg(ap, enum snmp_syntax);
  1077. pdu->nbindings++;
  1078. }
  1079. va_end(ap);
  1080. return (ret);
  1081. }
  1082. static int32_t
  1083. snmp_next_reqid(struct snmp_client * c)
  1084. {
  1085. int32_t i;
  1086. i = c->next_reqid;
  1087. if (c->next_reqid >= c->max_reqid)
  1088. c->next_reqid = c->min_reqid;
  1089. else
  1090. c->next_reqid++;
  1091. return (i);
  1092. }
  1093. /*
  1094. * Send request and return request id.
  1095. */
  1096. static int32_t
  1097. snmp_send_packet(struct snmp_pdu * pdu)
  1098. {
  1099. u_char *buf;
  1100. struct asn_buf b;
  1101. ssize_t ret;
  1102. if ((buf = malloc(snmp_client.txbuflen)) == NULL) {
  1103. seterr(&snmp_client, "%s", strerror(errno));
  1104. return (-1);
  1105. }
  1106. pdu->request_id = snmp_next_reqid(&snmp_client);
  1107. b.asn_ptr = buf;
  1108. b.asn_len = snmp_client.txbuflen;
  1109. if (snmp_pdu_encode(pdu, &b)) {
  1110. seterr(&snmp_client, "%s", strerror(errno));
  1111. free(buf);
  1112. return (-1);
  1113. }
  1114. if (snmp_client.dump_pdus)
  1115. snmp_pdu_dump(pdu);
  1116. if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) {
  1117. seterr(&snmp_client, "%s", strerror(errno));
  1118. free(buf);
  1119. return (-1);
  1120. }
  1121. free(buf);
  1122. return pdu->request_id;
  1123. }
  1124. /*
  1125. * to be called when a snmp request timed out
  1126. */
  1127. static void
  1128. snmp_timeout(void * listentry_ptr)
  1129. {
  1130. struct sent_pdu *listentry = listentry_ptr;
  1131. #if 0
  1132. warnx("snmp request %i timed out, attempt (%i/%i)",
  1133. listentry->reqid, listentry->retrycount, snmp_client.retries);
  1134. #endif
  1135. listentry->retrycount++;
  1136. if (listentry->retrycount > snmp_client.retries) {
  1137. /* there is no answer at all */
  1138. LIST_REMOVE(listentry, entries);
  1139. listentry->callback(listentry->pdu, NULL, listentry->arg);
  1140. free(listentry);
  1141. } else {
  1142. /* try again */
  1143. /* new request with new request ID */
  1144. listentry->reqid = snmp_send_packet(listentry->pdu);
  1145. listentry->timeout_id =
  1146. snmp_client.timeout_start(&snmp_client.timeout,
  1147. snmp_timeout, listentry);
  1148. }
  1149. }
  1150. int32_t
  1151. snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg)
  1152. {
  1153. struct sent_pdu *listentry;
  1154. int32_t id;
  1155. if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) {
  1156. seterr(&snmp_client, "%s", strerror(errno));
  1157. return (-1);
  1158. }
  1159. /* here we really send */
  1160. if ((id = snmp_send_packet(pdu)) == -1) {
  1161. free(listentry);
  1162. return (-1);
  1163. }
  1164. /* add entry to list of sent PDUs */
  1165. listentry->pdu = pdu;
  1166. if (gettimeofday(&listentry->time, NULL) == -1)
  1167. warn("gettimeofday() failed");
  1168. listentry->reqid = pdu->request_id;
  1169. listentry->callback = func;
  1170. listentry->arg = arg;
  1171. listentry->retrycount=1;
  1172. listentry->timeout_id =
  1173. snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout,
  1174. listentry);
  1175. LIST_INSERT_HEAD(&sent_pdus, listentry, entries);
  1176. return (id);
  1177. }
  1178. /*
  1179. * Receive an SNMP packet.
  1180. *
  1181. * tv controls how we wait for a packet: if tv is a NULL pointer,
  1182. * the receive blocks forever, if tv points to a structure with all
  1183. * members 0 the socket is polled, in all other cases tv specifies the
  1184. * maximum time to wait for a packet.
  1185. *
  1186. * Return:
  1187. * -1 on errors
  1188. * 0 on timeout
  1189. * +1 if packet received
  1190. */
  1191. static int
  1192. snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv)
  1193. {
  1194. int dopoll, setpoll;
  1195. int flags;
  1196. int saved_errno;
  1197. u_char *buf;
  1198. int ret;
  1199. struct asn_buf abuf;
  1200. int32_t ip;
  1201. #ifdef bsdi
  1202. int optlen;
  1203. #else
  1204. socklen_t optlen;
  1205. #endif
  1206. if ((buf = malloc(snmp_client.rxbuflen)) == NULL) {
  1207. seterr(&snmp_client, "%s", strerror(errno));
  1208. return (-1);
  1209. }
  1210. dopoll = setpoll = 0;
  1211. flags = 0;
  1212. if (tv != NULL) {
  1213. /* poll or timeout */
  1214. if (tv->tv_sec != 0 || tv->tv_usec != 0) {
  1215. /* wait with timeout */
  1216. if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
  1217. tv, sizeof(*tv)) == -1) {
  1218. seterr(&snmp_client, "setsockopt: %s",
  1219. strerror(errno));
  1220. free(buf);
  1221. return (-1);
  1222. }
  1223. optlen = sizeof(*tv);
  1224. if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
  1225. tv, &optlen) == -1) {
  1226. seterr(&snmp_client, "getsockopt: %s",
  1227. strerror(errno));
  1228. free(buf);
  1229. return (-1);
  1230. }
  1231. /* at this point tv_sec and tv_usec may appear
  1232. * as 0. This happens for timeouts lesser than
  1233. * the clock granularity. The kernel rounds these to
  1234. * 0 and this would result in a blocking receive.
  1235. * Instead of an else we check tv_sec and tv_usec
  1236. * again below and if this rounding happens,
  1237. * switch to a polling receive. */
  1238. }
  1239. if (tv->tv_sec == 0 && tv->tv_usec == 0) {
  1240. /* poll */
  1241. dopoll = 1;
  1242. if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) {
  1243. seterr(&snmp_client, "fcntl: %s",
  1244. strerror(errno));
  1245. free(buf);
  1246. return (-1);
  1247. }
  1248. if (!(flags & O_NONBLOCK)) {
  1249. setpoll = 1;
  1250. flags |= O_NONBLOCK;
  1251. if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) {
  1252. seterr(&snmp_client, "fcntl: %s",
  1253. strerror(errno));
  1254. free(buf);
  1255. return (-1);
  1256. }
  1257. }
  1258. }
  1259. }
  1260. ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0);
  1261. saved_errno = errno;
  1262. if (tv != NULL) {
  1263. if (dopoll) {
  1264. if (setpoll) {
  1265. flags &= ~O_NONBLOCK;
  1266. (void)fcntl(snmp_client.fd, F_SETFL, flags);
  1267. }
  1268. } else {
  1269. tv->tv_sec = 0;
  1270. tv->tv_usec = 0;
  1271. (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
  1272. tv, sizeof(*tv));
  1273. }
  1274. }
  1275. if (ret == -1) {
  1276. free(buf);
  1277. if (errno == EAGAIN || errno == EWOULDBLOCK)
  1278. return (0);
  1279. seterr(&snmp_client, "recv: %s", strerror(saved_errno));
  1280. return (-1);
  1281. }
  1282. if (ret == 0) {
  1283. /* this happens when we have a streaming socket and the
  1284. * remote side has closed it */
  1285. free(buf);
  1286. seterr(&snmp_client, "recv: socket closed by peer");
  1287. errno = EPIPE;
  1288. return (-1);
  1289. }
  1290. abuf.asn_ptr = buf;
  1291. abuf.asn_len = ret;
  1292. memset(pdu, 0, sizeof(*pdu));
  1293. if (snmp_client.security_model == SNMP_SECMODEL_USM) {
  1294. memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine));
  1295. memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user));
  1296. snmp_pdu_init_secparams(pdu);
  1297. }
  1298. if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) {
  1299. seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret);
  1300. free(buf);
  1301. return (-1);
  1302. }
  1303. free(buf);
  1304. if (snmp_client.dump_pdus)
  1305. snmp_pdu_dump(pdu);
  1306. snmp_client.engine.engine_time = pdu->engine.engine_time;
  1307. snmp_client.engine.engine_boots = pdu->engine.engine_boots;
  1308. return (+1);
  1309. }
  1310. static int
  1311. snmp_deliver_packet(struct snmp_pdu * resp)
  1312. {
  1313. struct sent_pdu *listentry;
  1314. if (resp->type != SNMP_PDU_RESPONSE) {
  1315. warn("ignoring snmp pdu %u", resp->type);
  1316. return (-1);
  1317. }
  1318. LIST_FOREACH(listentry, &sent_pdus, entries)
  1319. if (listentry->reqid == resp->request_id)
  1320. break;
  1321. if (listentry == NULL)
  1322. return (-1);
  1323. LIST_REMOVE(listentry, entries);
  1324. listentry->callback(listentry->pdu, resp, listentry->arg);
  1325. snmp_client.timeout_stop(listentry->timeout_id);
  1326. free(listentry);
  1327. return (0);
  1328. }
  1329. int
  1330. snmp_receive(int blocking)
  1331. {
  1332. int ret;
  1333. struct timeval tv;
  1334. struct snmp_pdu * resp;
  1335. memset(&tv, 0, sizeof(tv));
  1336. resp = malloc(sizeof(struct snmp_pdu));
  1337. if (resp == NULL) {
  1338. seterr(&snmp_client, "no memory for returning PDU");
  1339. return (-1) ;
  1340. }
  1341. if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) {
  1342. free(resp);
  1343. return (ret);
  1344. }
  1345. ret = snmp_deliver_packet(resp);
  1346. snmp_pdu_free(resp);
  1347. free(resp);
  1348. return (ret);
  1349. }
  1350. /*
  1351. * Check a GETNEXT response. Here we have three possible outcomes: -1 an
  1352. * unexpected error happened. +1 response is ok and is within the table 0
  1353. * response is ok, but is behind the table or error is NOSUCHNAME. The req
  1354. * should point to a template PDU which contains the base OIDs and the
  1355. * syntaxes. This is really only useful to sweep non-sparse tables.
  1356. */
  1357. static int
  1358. ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp)
  1359. {
  1360. u_int i;
  1361. if (resp->version != req->version) {
  1362. warnx("SNMP GETNEXT: response has wrong version");
  1363. return (-1);
  1364. }
  1365. if (resp->error_status == SNMP_ERR_NOSUCHNAME)
  1366. return (0);
  1367. if (resp->error_status != SNMP_ERR_NOERROR) {
  1368. warnx("SNMP GETNEXT: error %d", resp->error_status);
  1369. return (-1);
  1370. }
  1371. if (resp->nbindings != req->nbindings) {
  1372. warnx("SNMP GETNEXT: bad number of bindings in response");
  1373. return (-1);
  1374. }
  1375. for (i = 0; i < req->nbindings; i++) {
  1376. if (!asn_is_suboid(&req->bindings[i].var,
  1377. &resp->bindings[i].var)) {
  1378. if (i != 0)
  1379. warnx("SNMP GETNEXT: inconsistent table "
  1380. "response");
  1381. return (0);
  1382. }
  1383. if (resp->version != SNMP_V1 &&
  1384. resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
  1385. return (0);
  1386. if (resp->bindings[i].syntax != req->bindings[i].syntax) {
  1387. warnx("SNMP GETNEXT: bad syntax in response");
  1388. return (0);
  1389. }
  1390. }
  1391. return (1);
  1392. }
  1393. /*
  1394. * Check a GET response. Here we have three possible outcomes: -1 an
  1395. * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should
  1396. * point to a template PDU which contains the OIDs and the syntaxes. This
  1397. * is only useful for SNMPv1 or single object GETS.
  1398. */
  1399. static int
  1400. ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp)
  1401. {
  1402. u_int i;
  1403. if (resp->version != req->version) {
  1404. warnx("SNMP GET: response has wrong version");
  1405. return (-1);
  1406. }
  1407. if (resp->error_status == SNMP_ERR_NOSUCHNAME)
  1408. return (0);
  1409. if (resp->error_status != SNMP_ERR_NOERROR) {
  1410. warnx("SNMP GET: error %d", resp->error_status);
  1411. return (-1);
  1412. }
  1413. if (resp->nbindings != req->nbindings) {
  1414. warnx("SNMP GET: bad number of bindings in response");
  1415. return (-1);
  1416. }
  1417. for (i = 0; i < req->nbindings; i++) {
  1418. if (asn_compare_oid(&req->bindings[i].var,
  1419. &resp->bindings[i].var) != 0) {
  1420. warnx("SNMP GET: bad OID in response");
  1421. return (-1);
  1422. }
  1423. if (snmp_client.version != SNMP_V1 &&
  1424. (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT ||
  1425. resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE))
  1426. return (0);
  1427. if (resp->bindings[i].syntax != req->bindings[i].syntax) {
  1428. warnx("SNMP GET: bad syntax in response");
  1429. return (-1);
  1430. }
  1431. }
  1432. return (1);
  1433. }
  1434. /*
  1435. * Check the response to a SET PDU. We check: - the error status must be 0 -
  1436. * the number of bindings must be equal in response and request - the
  1437. * syntaxes must be the same in response and request - the OIDs must be the
  1438. * same in response and request
  1439. */
  1440. static int
  1441. ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp)
  1442. {
  1443. u_int i;
  1444. if (resp->version != req->version) {
  1445. warnx("SNMP SET: response has wrong version");
  1446. return (-1);
  1447. }
  1448. if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
  1449. warnx("SNMP SET: error %d", resp->error_status);
  1450. return (0);
  1451. }
  1452. if (resp->error_status != SNMP_ERR_NOERROR) {
  1453. warnx("SNMP SET: error %d", resp->error_status);
  1454. return (-1);
  1455. }
  1456. if (resp->nbindings != req->nbindings) {
  1457. warnx("SNMP SET: bad number of bindings in response");
  1458. return (-1);
  1459. }
  1460. for (i = 0; i < req->nbindings; i++) {
  1461. if (asn_compare_oid(&req->bindings[i].var,
  1462. &resp->bindings[i].var) != 0) {
  1463. warnx("SNMP SET: wrong OID in response to SET");
  1464. return (-1);
  1465. }
  1466. if (resp->bindings[i].syntax != req->bindings[i].syntax) {
  1467. warnx("SNMP SET: bad syntax in response");
  1468. return (-1);
  1469. }
  1470. }
  1471. return (1);
  1472. }
  1473. /*
  1474. * Simple checks for response PDUs against request PDUs. Return values: 1=ok,
  1475. * 0=nosuchname or similar, -1=failure, -2=no response at all
  1476. */
  1477. int
  1478. snmp_pdu_check(const struct snmp_pdu *req,
  1479. const struct snmp_pdu *resp)
  1480. {
  1481. if (resp == NULL)
  1482. return (-2);
  1483. switch (req->type) {
  1484. case SNMP_PDU_GET:
  1485. return (ok_get(req, resp));
  1486. case SNMP_PDU_SET:
  1487. return (ok_set(req, resp));
  1488. case SNMP_PDU_GETNEXT:
  1489. return (ok_getnext(req, resp));
  1490. }
  1491. errx(1, "%s: bad pdu type %i", __func__, req->type);
  1492. }
  1493. int
  1494. snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp)
  1495. {
  1496. u_int i;
  1497. int32_t reqid;
  1498. int ret;
  1499. struct timeval tv = snmp_client.timeout;
  1500. struct timeval end;
  1501. struct snmp_pdu pdu;
  1502. /*
  1503. * Make a copy of the request and replace the syntaxes by NULL
  1504. * if this is a GET,GETNEXT or GETBULK.
  1505. */
  1506. pdu = *req;
  1507. if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT ||
  1508. pdu.type == SNMP_PDU_GETBULK) {
  1509. for (i = 0; i < pdu.nbindings; i++)
  1510. pdu.bindings[i].syntax = SNMP_SYNTAX_NULL;
  1511. }
  1512. for (i = 0; i <= snmp_client.retries; i++) {
  1513. (void)gettimeofday(&end, NULL);
  1514. timeradd(&end, &snmp_client.timeout, &end);
  1515. if ((reqid = snmp_send_packet(&pdu)) == -1)
  1516. return (-1);
  1517. for (;;) {
  1518. (void)gettimeofday(&tv, NULL);
  1519. if (timercmp(&end, &tv, <=))
  1520. break;
  1521. timersub(&end, &tv, &tv);
  1522. if ((ret = snmp_receive_packet(resp, &tv)) == 0)
  1523. /* timeout */
  1524. break;
  1525. if (ret > 0) {
  1526. if (reqid == resp->request_id)
  1527. return (0);
  1528. /* not for us */
  1529. (void)snmp_deliver_packet(resp);
  1530. }
  1531. if (ret < 0 && errno == EPIPE)
  1532. /* stream closed */
  1533. return (-1);
  1534. }
  1535. }
  1536. errno = ETIMEDOUT;
  1537. seterr(&snmp_client, "retry count exceeded");
  1538. return (-1);
  1539. }
  1540. int
  1541. snmp_discover_engine(char *passwd)
  1542. {
  1543. char cname[SNMP_ADM_STR32_SIZ];
  1544. enum snmp_authentication cap;
  1545. enum snmp_privacy cpp;
  1546. struct snmp_pdu req, resp;
  1547. if (snmp_client.version != SNMP_V3)
  1548. seterr(&snmp_client, "wrong version");
  1549. strlcpy(cname, snmp_client.user.sec_name, sizeof(cname));
  1550. cap = snmp_client.user.auth_proto;
  1551. cpp = snmp_client.user.priv_proto;
  1552. snmp_client.engine.engine_len = 0;
  1553. snmp_client.engine.engine_boots = 0;
  1554. snmp_client.engine.engine_time = 0;
  1555. snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH;
  1556. snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV;
  1557. memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name));
  1558. snmp_pdu_create(&req, SNMP_PDU_GET);
  1559. if (snmp_dialog(&req, &resp) == -1)
  1560. return (-1);
  1561. if (resp.version != req.version) {
  1562. seterr(&snmp_client, "wrong version");
  1563. return (-1);
  1564. }
  1565. if (resp.error_status != SNMP_ERR_NOERROR) {
  1566. seterr(&snmp_client, "Error %d in responce", resp.error_status);
  1567. return (-1);
  1568. }
  1569. snmp_client.engine.engine_len = resp.engine.engine_len;
  1570. snmp_client.engine.max_msg_size = resp.engine.max_msg_size;
  1571. memcpy(snmp_client.engine.engine_id, resp.engine.engine_id,
  1572. resp.engine.engine_len);
  1573. strlcpy(snmp_client.user.sec_name, cname,
  1574. sizeof(snmp_client.user.sec_name));
  1575. snmp_client.user.auth_proto = cap;
  1576. snmp_client.user.priv_proto = cpp;
  1577. if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH)
  1578. return (0);
  1579. if (passwd == NULL ||
  1580. snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK ||
  1581. snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id,
  1582. snmp_client.engine.engine_len) != SNMP_CODE_OK)
  1583. return (-1);
  1584. if (resp.engine.engine_boots != 0)
  1585. snmp_client.engine.engine_boots = resp.engine.engine_boots;
  1586. if (resp.engine.engine_time != 0) {
  1587. snmp_client.engine.engine_time = resp.engine.engine_time;
  1588. return (0);
  1589. }
  1590. snmp_pdu_create(&req, SNMP_PDU_GET);
  1591. req.engine.engine_boots = 0;
  1592. req.engine.engine_time = 0;
  1593. if (snmp_dialog(&req, &resp) == -1)
  1594. return (-1);
  1595. if (resp.version != req.version) {
  1596. seterr(&snmp_client, "wrong version");
  1597. return (-1);
  1598. }
  1599. if (resp.error_status != SNMP_ERR_NOERROR) {
  1600. seterr(&snmp_client, "Error %d in responce", resp.error_status);
  1601. return (-1);
  1602. }
  1603. snmp_client.engine.engine_boots = resp.engine.engine_boots;
  1604. snmp_client.engine.engine_time = resp.engine.engine_time;
  1605. return (0);
  1606. }
  1607. int
  1608. snmp_client_set_host(struct snmp_client *cl, const char *h)
  1609. {
  1610. char *np;
  1611. if (h == NULL) {
  1612. if (cl->chost != NULL)
  1613. free(cl->chost);
  1614. cl->chost = NULL;
  1615. } else {
  1616. if ((np = malloc(strlen(h) + 1)) == NULL)
  1617. return (-1);
  1618. strcpy(np, h);
  1619. if (cl->chost != NULL)
  1620. free(cl->chost);
  1621. cl->chost = np;
  1622. }
  1623. return (0);
  1624. }
  1625. int
  1626. snmp_client_set_port(struct snmp_client *cl, const char *p)
  1627. {
  1628. char *np;
  1629. if (p == NULL) {
  1630. if (cl->cport != NULL)
  1631. free(cl->cport);
  1632. cl->cport = NULL;
  1633. } else {
  1634. if ((np = malloc(strlen(p) + 1)) == NULL)
  1635. return (-1);
  1636. strcpy(np, p);
  1637. if (cl->cport != NULL)
  1638. free(cl->cport);
  1639. cl->cport = np;
  1640. }
  1641. return (0);
  1642. }
  1643. /*
  1644. * parse a server specification
  1645. *
  1646. * [trans::][community@][server][:port]
  1647. */
  1648. int
  1649. snmp_parse_server(struct snmp_client *sc, const char *str)
  1650. {
  1651. const char *p, *s = str;
  1652. /* look for a double colon */
  1653. for (p = s; *p != '\0'; p++) {
  1654. if (*p == '\\' && p[1] != '\0') {
  1655. p++;
  1656. continue;
  1657. }
  1658. if (*p == ':' && p[1] == ':')
  1659. break;
  1660. }
  1661. if (*p != '\0') {
  1662. if (p > s) {
  1663. if (p - s == 3 && strncmp(s, "udp", 3) == 0)
  1664. sc->trans = SNMP_TRANS_UDP;
  1665. else if (p - s == 6 && strncmp(s, "stream", 6) == 0)
  1666. sc->trans = SNMP_TRANS_LOC_STREAM;
  1667. else if (p - s == 5 && strncmp(s, "dgram", 5) == 0)
  1668. sc->trans = SNMP_TRANS_LOC_DGRAM;
  1669. else {
  1670. seterr(sc, "unknown SNMP transport '%.*s'",
  1671. (int)(p - s), s);
  1672. return (-1);
  1673. }
  1674. }
  1675. s = p + 2;
  1676. }
  1677. /* look for a @ */
  1678. for (p = s; *p != '\0'; p++) {
  1679. if (*p == '\\' && p[1] != '\0') {
  1680. p++;
  1681. continue;
  1682. }
  1683. if (*p == '@')
  1684. break;
  1685. }
  1686. if (*p != '\0') {
  1687. if (p - s > SNMP_COMMUNITY_MAXLEN) {
  1688. seterr(sc, "community string too long");
  1689. return (-1);
  1690. }
  1691. strncpy(sc->read_community, s, p - s);
  1692. sc->read_community[p - s] = '\0';
  1693. strncpy(sc->write_community, s, p - s);
  1694. sc->write_community[p - s] = '\0';
  1695. s = p + 1;
  1696. }
  1697. /* look for a colon */
  1698. for (p = s; *p != '\0'; p++) {
  1699. if (*p == '\\' && p[1] != '\0') {
  1700. p++;
  1701. continue;
  1702. }
  1703. if (*p == ':')
  1704. break;
  1705. }
  1706. if (*p == ':') {
  1707. if (p > s) {
  1708. /* host:port */
  1709. free(sc->chost);
  1710. if ((sc->chost = malloc(p - s + 1)) == NULL) {
  1711. seterr(sc, "%s", strerror(errno));
  1712. return (-1);
  1713. }
  1714. strncpy(sc->chost, s, p - s);
  1715. sc->chost[p - s] = '\0';
  1716. }
  1717. /* port */
  1718. free(sc->cport);
  1719. if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) {
  1720. seterr(sc, "%s", strerror(errno));
  1721. return (-1);
  1722. }
  1723. strcpy(sc->cport, p + 1);
  1724. } else if (p > s) {
  1725. /* host */
  1726. free(sc->chost);
  1727. if ((sc->chost = malloc(strlen(s) + 1)) == NULL) {
  1728. seterr(sc, "%s", strerror(errno));
  1729. return (-1);
  1730. }
  1731. strcpy(sc->chost, s);
  1732. }
  1733. return (0);
  1734. }