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