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