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