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