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