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