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 && path == NULL) 985 path = SNMP_DEFAULT_LOCAL; 986 if (path != NULL) { 987 if ((ptr = malloc(1 + strlen(path))) == NULL) { 988 seterr(&snmp_client, "%s", strerror(errno)); 989 return (-1); 990 } 991 free(snmp_client.chost); 992 snmp_client.chost = ptr; 993 strcpy(snmp_client.chost, path); 994 } 995 996 if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 997 stype = SOCK_DGRAM; 998 else 999 stype = SOCK_STREAM; 1000 1001 if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 1002 seterr(&snmp_client, "%s", strerror(errno)); 1003 return (-1); 1004 } 1005 1006 snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 1007 "%s", SNMP_LOCAL_PATH); 1008 1009 if (mktemp(snmp_client.local_path) == NULL) { 1010 seterr(&snmp_client, "%s", strerror(errno)); 1011 (void)close(snmp_client.fd); 1012 snmp_client.fd = -1; 1013 return (-1); 1014 } 1015 1016 sa.sun_family = AF_LOCAL; 1017 sa.sun_len = sizeof(sa); 1018 strcpy(sa.sun_path, snmp_client.local_path); 1019 1020 if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1021 seterr(&snmp_client, "%s", strerror(errno)); 1022 (void)close(snmp_client.fd); 1023 snmp_client.fd = -1; 1024 (void)remove(snmp_client.local_path); 1025 return (-1); 1026 } 1027 atexit(remove_local); 1028 1029 sa.sun_family = AF_LOCAL; 1030 sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1031 strlen(snmp_client.chost); 1032 strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1033 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1034 1035 if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1036 seterr(&snmp_client, "%s", strerror(errno)); 1037 (void)close(snmp_client.fd); 1038 snmp_client.fd = -1; 1039 (void)remove(snmp_client.local_path); 1040 return (-1); 1041 } 1042 return (0); 1043 } 1044 1045 /* 1046 * SNMP_OPEN 1047 */ 1048 int 1049 snmp_open(const char *host, const char *port, const char *readcomm, 1050 const char *writecomm) 1051 { 1052 struct timeval tout; 1053 1054 /* still open ? */ 1055 if (snmp_client.fd != -1) { 1056 errno = EBUSY; 1057 seterr(&snmp_client, "%s", strerror(errno)); 1058 return (-1); 1059 } 1060 1061 /* copy community strings */ 1062 if (readcomm != NULL) 1063 strlcpy(snmp_client.read_community, readcomm, 1064 sizeof(snmp_client.read_community)); 1065 if (writecomm != NULL) 1066 strlcpy(snmp_client.write_community, writecomm, 1067 sizeof(snmp_client.write_community)); 1068 1069 switch (snmp_client.trans) { 1070 1071 case SNMP_TRANS_UDP: 1072 case SNMP_TRANS_UDP6: 1073 if (open_client_udp(host, port) != 0) 1074 return (-1); 1075 break; 1076 1077 case SNMP_TRANS_LOC_DGRAM: 1078 case SNMP_TRANS_LOC_STREAM: 1079 if (open_client_local(host) != 0) 1080 return (-1); 1081 break; 1082 1083 default: 1084 seterr(&snmp_client, "bad transport mapping"); 1085 return (-1); 1086 } 1087 tout.tv_sec = 0; 1088 tout.tv_usec = 0; 1089 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1090 &tout, sizeof(struct timeval)) == -1) { 1091 seterr(&snmp_client, "%s", strerror(errno)); 1092 (void)close(snmp_client.fd); 1093 snmp_client.fd = -1; 1094 if (snmp_client.local_path[0] != '\0') 1095 (void)remove(snmp_client.local_path); 1096 return (-1); 1097 } 1098 1099 /* initialize list */ 1100 LIST_INIT(&sent_pdus); 1101 1102 return (0); 1103 } 1104 1105 1106 /* 1107 * SNMP_CLOSE 1108 * 1109 * closes connection to snmp server 1110 * - function cannot fail 1111 * - clears connection 1112 * - clears list of sent pdus 1113 * 1114 * input: 1115 * void 1116 * return: 1117 * void 1118 */ 1119 void 1120 snmp_close(void) 1121 { 1122 struct sent_pdu *p1; 1123 1124 if (snmp_client.fd != -1) { 1125 (void)close(snmp_client.fd); 1126 snmp_client.fd = -1; 1127 if (snmp_client.local_path[0] != '\0') 1128 (void)remove(snmp_client.local_path); 1129 } 1130 while(!LIST_EMPTY(&sent_pdus)){ 1131 p1 = LIST_FIRST(&sent_pdus); 1132 if (p1->timeout_id != NULL) 1133 snmp_client.timeout_stop(p1->timeout_id); 1134 LIST_REMOVE(p1, entries); 1135 free(p1); 1136 } 1137 free(snmp_client.chost); 1138 free(snmp_client.cport); 1139 } 1140 1141 /* 1142 * initialize a snmp_pdu structure 1143 */ 1144 void 1145 snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1146 { 1147 memset(pdu, 0, sizeof(struct snmp_pdu)); 1148 1149 if (op == SNMP_PDU_SET) 1150 strlcpy(pdu->community, snmp_client.write_community, 1151 sizeof(pdu->community)); 1152 else 1153 strlcpy(pdu->community, snmp_client.read_community, 1154 sizeof(pdu->community)); 1155 1156 pdu->type = op; 1157 pdu->version = snmp_client.version; 1158 pdu->error_status = 0; 1159 pdu->error_index = 0; 1160 pdu->nbindings = 0; 1161 1162 if (snmp_client.version != SNMP_V3) 1163 return; 1164 1165 pdu->identifier = ++snmp_client.identifier; 1166 pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; 1167 pdu->flags = 0; 1168 pdu->security_model = snmp_client.security_model; 1169 1170 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1171 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1172 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1173 snmp_pdu_init_secparams(pdu); 1174 } else 1175 seterr(&snmp_client, "unknown security model"); 1176 1177 if (snmp_client.clen > 0) { 1178 memcpy(pdu->context_engine, snmp_client.cengine, 1179 snmp_client.clen); 1180 pdu->context_engine_len = snmp_client.clen; 1181 } else { 1182 memcpy(pdu->context_engine, snmp_client.engine.engine_id, 1183 snmp_client.engine.engine_len); 1184 pdu->context_engine_len = snmp_client.engine.engine_len; 1185 } 1186 1187 strlcpy(pdu->context_name, snmp_client.cname, 1188 sizeof(pdu->context_name)); 1189 } 1190 1191 /* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1192 /* added 10/04/02 by kek: check for MAX_BINDINGS */ 1193 int 1194 snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1195 { 1196 va_list ap; 1197 const struct asn_oid *oid; 1198 u_int ret; 1199 1200 va_start(ap, pdu); 1201 1202 ret = pdu->nbindings; 1203 while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1204 if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1205 va_end(ap); 1206 return (-1); 1207 } 1208 pdu->bindings[pdu->nbindings].var = *oid; 1209 pdu->bindings[pdu->nbindings].syntax = 1210 va_arg(ap, enum snmp_syntax); 1211 pdu->nbindings++; 1212 } 1213 va_end(ap); 1214 return (ret); 1215 } 1216 1217 1218 static int32_t 1219 snmp_next_reqid(struct snmp_client * c) 1220 { 1221 int32_t i; 1222 1223 i = c->next_reqid; 1224 if (c->next_reqid >= c->max_reqid) 1225 c->next_reqid = c->min_reqid; 1226 else 1227 c->next_reqid++; 1228 return (i); 1229 } 1230 1231 /* 1232 * Send request and return request id. 1233 */ 1234 static int32_t 1235 snmp_send_packet(struct snmp_pdu * pdu) 1236 { 1237 u_char *buf; 1238 struct asn_buf b; 1239 ssize_t ret; 1240 1241 if ((buf = calloc(1, snmp_client.txbuflen)) == NULL) { 1242 seterr(&snmp_client, "%s", strerror(errno)); 1243 return (-1); 1244 } 1245 1246 pdu->request_id = snmp_next_reqid(&snmp_client); 1247 1248 b.asn_ptr = buf; 1249 b.asn_len = snmp_client.txbuflen; 1250 if (snmp_pdu_encode(pdu, &b)) { 1251 seterr(&snmp_client, "%s", strerror(errno)); 1252 free(buf); 1253 return (-1); 1254 } 1255 1256 if (snmp_client.dump_pdus) 1257 snmp_pdu_dump(pdu); 1258 1259 if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1260 seterr(&snmp_client, "%s", strerror(errno)); 1261 free(buf); 1262 return (-1); 1263 } 1264 free(buf); 1265 1266 return (pdu->request_id); 1267 } 1268 1269 /* 1270 * to be called when a snmp request timed out 1271 */ 1272 static void 1273 snmp_timeout(void * listentry_ptr) 1274 { 1275 struct sent_pdu *listentry = listentry_ptr; 1276 1277 #if 0 1278 warnx("snmp request %i timed out, attempt (%i/%i)", 1279 listentry->reqid, listentry->retrycount, snmp_client.retries); 1280 #endif 1281 1282 listentry->retrycount++; 1283 if (listentry->retrycount > snmp_client.retries) { 1284 /* there is no answer at all */ 1285 LIST_REMOVE(listentry, entries); 1286 listentry->callback(listentry->pdu, NULL, listentry->arg); 1287 free(listentry); 1288 } else { 1289 /* try again */ 1290 /* new request with new request ID */ 1291 listentry->reqid = snmp_send_packet(listentry->pdu); 1292 listentry->timeout_id = 1293 snmp_client.timeout_start(&snmp_client.timeout, 1294 snmp_timeout, listentry); 1295 } 1296 } 1297 1298 int32_t 1299 snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1300 { 1301 struct sent_pdu *listentry; 1302 int32_t id; 1303 1304 if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1305 seterr(&snmp_client, "%s", strerror(errno)); 1306 return (-1); 1307 } 1308 1309 /* here we really send */ 1310 if ((id = snmp_send_packet(pdu)) == -1) { 1311 free(listentry); 1312 return (-1); 1313 } 1314 1315 /* add entry to list of sent PDUs */ 1316 listentry->pdu = pdu; 1317 if (gettimeofday(&listentry->time, NULL) == -1) 1318 warn("gettimeofday() failed"); 1319 1320 listentry->reqid = pdu->request_id; 1321 listentry->callback = func; 1322 listentry->arg = arg; 1323 listentry->retrycount=1; 1324 listentry->timeout_id = 1325 snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1326 listentry); 1327 1328 LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1329 1330 return (id); 1331 } 1332 1333 /* 1334 * Receive an SNMP packet. 1335 * 1336 * tv controls how we wait for a packet: if tv is a NULL pointer, 1337 * the receive blocks forever, if tv points to a structure with all 1338 * members 0 the socket is polled, in all other cases tv specifies the 1339 * maximum time to wait for a packet. 1340 * 1341 * Return: 1342 * -1 on errors 1343 * 0 on timeout 1344 * +1 if packet received 1345 */ 1346 static int 1347 snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1348 { 1349 int dopoll, setpoll; 1350 int flags; 1351 int saved_errno; 1352 u_char *buf; 1353 int ret; 1354 struct asn_buf abuf; 1355 int32_t ip; 1356 #ifdef bsdi 1357 int optlen; 1358 #else 1359 socklen_t optlen; 1360 #endif 1361 1362 if ((buf = calloc(1, snmp_client.rxbuflen)) == NULL) { 1363 seterr(&snmp_client, "%s", strerror(errno)); 1364 return (-1); 1365 } 1366 dopoll = setpoll = 0; 1367 flags = 0; 1368 if (tv != NULL) { 1369 /* poll or timeout */ 1370 if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1371 /* wait with timeout */ 1372 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1373 tv, sizeof(*tv)) == -1) { 1374 seterr(&snmp_client, "setsockopt: %s", 1375 strerror(errno)); 1376 free(buf); 1377 return (-1); 1378 } 1379 optlen = sizeof(*tv); 1380 if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1381 tv, &optlen) == -1) { 1382 seterr(&snmp_client, "getsockopt: %s", 1383 strerror(errno)); 1384 free(buf); 1385 return (-1); 1386 } 1387 /* at this point tv_sec and tv_usec may appear 1388 * as 0. This happens for timeouts lesser than 1389 * the clock granularity. The kernel rounds these to 1390 * 0 and this would result in a blocking receive. 1391 * Instead of an else we check tv_sec and tv_usec 1392 * again below and if this rounding happens, 1393 * switch to a polling receive. */ 1394 } 1395 if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1396 /* poll */ 1397 dopoll = 1; 1398 if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1399 seterr(&snmp_client, "fcntl: %s", 1400 strerror(errno)); 1401 free(buf); 1402 return (-1); 1403 } 1404 if (!(flags & O_NONBLOCK)) { 1405 setpoll = 1; 1406 flags |= O_NONBLOCK; 1407 if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1408 seterr(&snmp_client, "fcntl: %s", 1409 strerror(errno)); 1410 free(buf); 1411 return (-1); 1412 } 1413 } 1414 } 1415 } 1416 ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1417 saved_errno = errno; 1418 if (tv != NULL) { 1419 if (dopoll) { 1420 if (setpoll) { 1421 flags &= ~O_NONBLOCK; 1422 (void)fcntl(snmp_client.fd, F_SETFL, flags); 1423 } 1424 } else { 1425 tv->tv_sec = 0; 1426 tv->tv_usec = 0; 1427 (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1428 tv, sizeof(*tv)); 1429 } 1430 } 1431 if (ret == -1) { 1432 free(buf); 1433 if (errno == EAGAIN || errno == EWOULDBLOCK) 1434 return (0); 1435 seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1436 return (-1); 1437 } 1438 if (ret == 0) { 1439 /* this happens when we have a streaming socket and the 1440 * remote side has closed it */ 1441 free(buf); 1442 seterr(&snmp_client, "recv: socket closed by peer"); 1443 errno = EPIPE; 1444 return (-1); 1445 } 1446 1447 abuf.asn_ptr = buf; 1448 abuf.asn_len = ret; 1449 1450 memset(pdu, 0, sizeof(*pdu)); 1451 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1452 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1453 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1454 snmp_pdu_init_secparams(pdu); 1455 } 1456 1457 if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1458 seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1459 free(buf); 1460 return (-1); 1461 } 1462 1463 free(buf); 1464 if (snmp_client.dump_pdus) 1465 snmp_pdu_dump(pdu); 1466 1467 snmp_client.engine.engine_time = pdu->engine.engine_time; 1468 snmp_client.engine.engine_boots = pdu->engine.engine_boots; 1469 1470 return (+1); 1471 } 1472 1473 static int 1474 snmp_deliver_packet(struct snmp_pdu * resp) 1475 { 1476 struct sent_pdu *listentry; 1477 1478 if (resp->type != SNMP_PDU_RESPONSE) { 1479 warn("ignoring snmp pdu %u", resp->type); 1480 return (-1); 1481 } 1482 1483 LIST_FOREACH(listentry, &sent_pdus, entries) 1484 if (listentry->reqid == resp->request_id) 1485 break; 1486 if (listentry == NULL) 1487 return (-1); 1488 1489 LIST_REMOVE(listentry, entries); 1490 listentry->callback(listentry->pdu, resp, listentry->arg); 1491 1492 snmp_client.timeout_stop(listentry->timeout_id); 1493 1494 free(listentry); 1495 return (0); 1496 } 1497 1498 int 1499 snmp_receive(int blocking) 1500 { 1501 int ret; 1502 1503 struct timeval tv; 1504 struct snmp_pdu * resp; 1505 1506 memset(&tv, 0, sizeof(tv)); 1507 1508 resp = malloc(sizeof(struct snmp_pdu)); 1509 if (resp == NULL) { 1510 seterr(&snmp_client, "no memory for returning PDU"); 1511 return (-1) ; 1512 } 1513 1514 if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1515 free(resp); 1516 return (ret); 1517 } 1518 ret = snmp_deliver_packet(resp); 1519 snmp_pdu_free(resp); 1520 free(resp); 1521 return (ret); 1522 } 1523 1524 1525 /* 1526 * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1527 * unexpected error happened. +1 response is ok and is within the table 0 1528 * response is ok, but is behind the table or error is NOSUCHNAME. The req 1529 * should point to a template PDU which contains the base OIDs and the 1530 * syntaxes. This is really only useful to sweep non-sparse tables. 1531 */ 1532 static int 1533 ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1534 { 1535 u_int i; 1536 1537 if (resp->version != req->version) { 1538 warnx("SNMP GETNEXT: response has wrong version"); 1539 return (-1); 1540 } 1541 1542 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1543 return (0); 1544 1545 if (resp->error_status != SNMP_ERR_NOERROR) { 1546 warnx("SNMP GETNEXT: error %d", resp->error_status); 1547 return (-1); 1548 } 1549 if (resp->nbindings != req->nbindings) { 1550 warnx("SNMP GETNEXT: bad number of bindings in response"); 1551 return (-1); 1552 } 1553 for (i = 0; i < req->nbindings; i++) { 1554 if (!asn_is_suboid(&req->bindings[i].var, 1555 &resp->bindings[i].var)) { 1556 if (i != 0) 1557 warnx("SNMP GETNEXT: inconsistent table " 1558 "response"); 1559 return (0); 1560 } 1561 if (resp->version != SNMP_V1 && 1562 resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1563 return (0); 1564 1565 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1566 warnx("SNMP GETNEXT: bad syntax in response"); 1567 return (0); 1568 } 1569 } 1570 return (1); 1571 } 1572 1573 /* 1574 * Check a GET response. Here we have three possible outcomes: -1 an 1575 * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1576 * point to a template PDU which contains the OIDs and the syntaxes. This 1577 * is only useful for SNMPv1 or single object GETS. 1578 */ 1579 static int 1580 ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1581 { 1582 u_int i; 1583 1584 if (resp->version != req->version) { 1585 warnx("SNMP GET: response has wrong version"); 1586 return (-1); 1587 } 1588 1589 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1590 return (0); 1591 1592 if (resp->error_status != SNMP_ERR_NOERROR) { 1593 warnx("SNMP GET: error %d", resp->error_status); 1594 return (-1); 1595 } 1596 1597 if (resp->nbindings != req->nbindings) { 1598 warnx("SNMP GET: bad number of bindings in response"); 1599 return (-1); 1600 } 1601 for (i = 0; i < req->nbindings; i++) { 1602 if (asn_compare_oid(&req->bindings[i].var, 1603 &resp->bindings[i].var) != 0) { 1604 warnx("SNMP GET: bad OID in response"); 1605 return (-1); 1606 } 1607 if (snmp_client.version != SNMP_V1 && 1608 (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1609 resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1610 return (0); 1611 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1612 warnx("SNMP GET: bad syntax in response"); 1613 return (-1); 1614 } 1615 } 1616 return (1); 1617 } 1618 1619 /* 1620 * Check the response to a SET PDU. We check: - the error status must be 0 - 1621 * the number of bindings must be equal in response and request - the 1622 * syntaxes must be the same in response and request - the OIDs must be the 1623 * same in response and request 1624 */ 1625 static int 1626 ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1627 { 1628 u_int i; 1629 1630 if (resp->version != req->version) { 1631 warnx("SNMP SET: response has wrong version"); 1632 return (-1); 1633 } 1634 1635 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1636 warnx("SNMP SET: error %d", resp->error_status); 1637 return (0); 1638 } 1639 if (resp->error_status != SNMP_ERR_NOERROR) { 1640 warnx("SNMP SET: error %d", resp->error_status); 1641 return (-1); 1642 } 1643 1644 if (resp->nbindings != req->nbindings) { 1645 warnx("SNMP SET: bad number of bindings in response"); 1646 return (-1); 1647 } 1648 for (i = 0; i < req->nbindings; i++) { 1649 if (asn_compare_oid(&req->bindings[i].var, 1650 &resp->bindings[i].var) != 0) { 1651 warnx("SNMP SET: wrong OID in response to SET"); 1652 return (-1); 1653 } 1654 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1655 warnx("SNMP SET: bad syntax in response"); 1656 return (-1); 1657 } 1658 } 1659 return (1); 1660 } 1661 1662 /* 1663 * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1664 * 0=nosuchname or similar, -1=failure, -2=no response at all 1665 */ 1666 int 1667 snmp_pdu_check(const struct snmp_pdu *req, 1668 const struct snmp_pdu *resp) 1669 { 1670 if (resp == NULL) 1671 return (-2); 1672 1673 switch (req->type) { 1674 1675 case SNMP_PDU_GET: 1676 return (ok_get(req, resp)); 1677 1678 case SNMP_PDU_SET: 1679 return (ok_set(req, resp)); 1680 1681 case SNMP_PDU_GETNEXT: 1682 return (ok_getnext(req, resp)); 1683 1684 } 1685 errx(1, "%s: bad pdu type %i", __func__, req->type); 1686 } 1687 1688 int 1689 snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1690 { 1691 struct timeval tv = snmp_client.timeout; 1692 struct timeval end; 1693 struct snmp_pdu pdu; 1694 int ret; 1695 int32_t reqid; 1696 u_int i; 1697 1698 /* 1699 * Make a copy of the request and replace the syntaxes by NULL 1700 * if this is a GET,GETNEXT or GETBULK. 1701 */ 1702 pdu = *req; 1703 if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1704 pdu.type == SNMP_PDU_GETBULK) { 1705 for (i = 0; i < pdu.nbindings; i++) 1706 pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1707 } 1708 1709 for (i = 0; i <= snmp_client.retries; i++) { 1710 (void)gettimeofday(&end, NULL); 1711 timeradd(&end, &snmp_client.timeout, &end); 1712 if ((reqid = snmp_send_packet(&pdu)) == -1) 1713 return (-1); 1714 for (;;) { 1715 (void)gettimeofday(&tv, NULL); 1716 if (timercmp(&end, &tv, <=)) 1717 break; 1718 timersub(&end, &tv, &tv); 1719 if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1720 /* timeout */ 1721 break; 1722 1723 if (ret > 0) { 1724 if (reqid == resp->request_id) 1725 return (0); 1726 /* not for us */ 1727 (void)snmp_deliver_packet(resp); 1728 } 1729 if (ret < 0 && errno == EPIPE) 1730 /* stream closed */ 1731 return (-1); 1732 } 1733 } 1734 errno = ETIMEDOUT; 1735 seterr(&snmp_client, "retry count exceeded"); 1736 return (-1); 1737 } 1738 1739 int 1740 snmp_discover_engine(char *passwd) 1741 { 1742 char cname[SNMP_ADM_STR32_SIZ]; 1743 enum snmp_authentication cap; 1744 enum snmp_privacy cpp; 1745 struct snmp_pdu req, resp; 1746 1747 if (snmp_client.version != SNMP_V3) 1748 seterr(&snmp_client, "wrong version"); 1749 1750 strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); 1751 cap = snmp_client.user.auth_proto; 1752 cpp = snmp_client.user.priv_proto; 1753 1754 snmp_client.engine.engine_len = 0; 1755 snmp_client.engine.engine_boots = 0; 1756 snmp_client.engine.engine_time = 0; 1757 snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; 1758 snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; 1759 memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); 1760 1761 snmp_pdu_create(&req, SNMP_PDU_GET); 1762 1763 if (snmp_dialog(&req, &resp) == -1) 1764 return (-1); 1765 1766 if (resp.version != req.version) { 1767 seterr(&snmp_client, "wrong version"); 1768 return (-1); 1769 } 1770 1771 if (resp.error_status != SNMP_ERR_NOERROR) { 1772 seterr(&snmp_client, "Error %d in response", resp.error_status); 1773 return (-1); 1774 } 1775 1776 snmp_client.engine.engine_len = resp.engine.engine_len; 1777 snmp_client.engine.max_msg_size = resp.engine.max_msg_size; 1778 memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, 1779 resp.engine.engine_len); 1780 1781 strlcpy(snmp_client.user.sec_name, cname, 1782 sizeof(snmp_client.user.sec_name)); 1783 snmp_client.user.auth_proto = cap; 1784 snmp_client.user.priv_proto = cpp; 1785 1786 if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) 1787 return (0); 1788 1789 if (passwd == NULL || strlen(passwd) == 0 || 1790 snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || 1791 snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, 1792 snmp_client.engine.engine_len) != SNMP_CODE_OK) 1793 return (-1); 1794 1795 if (resp.engine.engine_boots != 0) 1796 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1797 1798 if (resp.engine.engine_time != 0) { 1799 snmp_client.engine.engine_time = resp.engine.engine_time; 1800 return (0); 1801 } 1802 1803 snmp_pdu_free(&req); 1804 1805 snmp_pdu_create(&req, SNMP_PDU_GET); 1806 req.engine.engine_boots = 0; 1807 req.engine.engine_time = 0; 1808 1809 if (snmp_dialog(&req, &resp) == -1) 1810 return (-1); 1811 1812 if (resp.version != req.version) { 1813 seterr(&snmp_client, "wrong version"); 1814 return (-1); 1815 } 1816 1817 if (resp.error_status != SNMP_ERR_NOERROR) { 1818 seterr(&snmp_client, "Error %d in response", resp.error_status); 1819 return (-1); 1820 } 1821 1822 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1823 snmp_client.engine.engine_time = resp.engine.engine_time; 1824 1825 snmp_pdu_free(&req); 1826 snmp_pdu_free(&resp); 1827 1828 return (0); 1829 } 1830 1831 int 1832 snmp_client_set_host(struct snmp_client *cl, const char *h) 1833 { 1834 char *np; 1835 1836 if (h == NULL) { 1837 if (cl->chost != NULL) 1838 free(cl->chost); 1839 cl->chost = NULL; 1840 } else { 1841 if ((np = malloc(strlen(h) + 1)) == NULL) 1842 return (-1); 1843 strcpy(np, h); 1844 if (cl->chost != NULL) 1845 free(cl->chost); 1846 cl->chost = np; 1847 } 1848 return (0); 1849 } 1850 1851 int 1852 snmp_client_set_port(struct snmp_client *cl, const char *p) 1853 { 1854 char *np; 1855 1856 if (p == NULL) { 1857 if (cl->cport != NULL) 1858 free(cl->cport); 1859 cl->cport = NULL; 1860 } else { 1861 if ((np = malloc(strlen(p) + 1)) == NULL) 1862 return (-1); 1863 strcpy(np, p); 1864 if (cl->cport != NULL) 1865 free(cl->cport); 1866 cl->cport = np; 1867 } 1868 return (0); 1869 } 1870 1871 static const char *const trans_list[] = { 1872 [SNMP_TRANS_UDP] = "udp::", 1873 [SNMP_TRANS_LOC_DGRAM] = "dgram::", 1874 [SNMP_TRANS_LOC_STREAM] = "stream::", 1875 [SNMP_TRANS_UDP6] = "udp6::", 1876 }; 1877 1878 /** 1879 * Try to get a transport identifier which is a leading alphanumeric string 1880 * terminated by a double colon. The string may not be empty. The transport 1881 * identifier is optional. Unknown transport identifiers are reject. 1882 * Be careful: a double colon can also occur in a numeric IPv6 address. 1883 * 1884 * \param sc client struct to set errors 1885 * \param strp possible start of transport; updated to point to 1886 * the next character to parse 1887 * 1888 * \return transport identifier 1889 */ 1890 static inline int 1891 get_transp(struct snmp_client *sc, const char **strp) 1892 { 1893 const char *p; 1894 size_t i; 1895 1896 for (i = 0; i < nitems(trans_list); i++) { 1897 p = strstr(*strp, trans_list[i]); 1898 if (p == *strp) { 1899 *strp += strlen(trans_list[i]); 1900 return ((int)i); 1901 } 1902 } 1903 1904 p = strstr(*strp, "::"); 1905 if (p == *strp) { 1906 seterr(sc, "empty transport specifier"); 1907 return (-1); 1908 } 1909 if (p == NULL) 1910 /* by default assume UDP */ 1911 return (SNMP_TRANS_UDP); 1912 1913 /* ignore :: after [ */ 1914 const char *ob = strchr(*strp, '['); 1915 if (ob != NULL && p > ob) 1916 /* by default assume UDP */ 1917 return (SNMP_TRANS_UDP); 1918 1919 seterr(sc, "unknown transport specifier '%.*s'", p - *strp, *strp); 1920 return (-1); 1921 } 1922 1923 /** 1924 * Try to get community string. Eat everything up to the last @ (if there is 1925 * any) but only if it is not longer than SNMP_COMMUNITY_MAXLEN. Empty 1926 * community strings are legal. 1927 * 1928 * \param sc client struct to set errors 1929 * \param strp possible start of community; updated to the point to 1930 * the next character to parse 1931 * 1932 * \return end of community; equals *strp if there is none; NULL if there 1933 * was an error 1934 */ 1935 static inline const char * 1936 get_comm(struct snmp_client *sc, const char **strp) 1937 { 1938 const char *p = strrchr(*strp, '@'); 1939 1940 if (p == NULL) 1941 /* no community string */ 1942 return (*strp); 1943 1944 if (p - *strp > SNMP_COMMUNITY_MAXLEN) { 1945 seterr(sc, "community string too long '%.*s'", 1946 p - *strp, *strp); 1947 return (NULL); 1948 } 1949 1950 *strp = p + 1; 1951 return (p); 1952 } 1953 1954 /** 1955 * Try to get an IPv6 address. This starts with an [ and should end with an ] 1956 * and everything between should be not longer than INET6_ADDRSTRLEN and 1957 * parseable by inet_pton(). 1958 * 1959 * \param sc client struct to set errors 1960 * \param strp possible start of IPv6 address (the '['); updated to point to 1961 * the next character to parse (the one after the closing ']') 1962 * 1963 * \return end of address (equals *strp + 1 if there is none) or NULL 1964 * on errors 1965 */ 1966 static inline const char * 1967 get_ipv6(struct snmp_client *sc, const char **strp) 1968 { 1969 char str[INET6_ADDRSTRLEN + IF_NAMESIZE]; 1970 struct addrinfo hints, *res; 1971 int error; 1972 1973 if (**strp != '[') 1974 return (*strp + 1); 1975 1976 const char *p = *strp + 1; 1977 while (*p != ']' ) { 1978 if (*p == '\0') { 1979 seterr(sc, "unterminated IPv6 address '%.*s'", 1980 p - *strp, *strp); 1981 return (NULL); 1982 } 1983 p++; 1984 } 1985 1986 if (p - *strp > INET6_ADDRSTRLEN + IF_NAMESIZE) { 1987 seterr(sc, "IPv6 address too long '%.*s'", p - *strp, *strp); 1988 return (NULL); 1989 } 1990 1991 strncpy(str, *strp + 1, p - (*strp + 1)); 1992 str[p - (*strp + 1)] = '\0'; 1993 1994 memset(&hints, 0, sizeof(hints)); 1995 hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST; 1996 hints.ai_family = AF_INET6; 1997 hints.ai_socktype = SOCK_DGRAM; 1998 hints.ai_protocol = IPPROTO_UDP; 1999 error = getaddrinfo(str, NULL, &hints, &res); 2000 if (error != 0) { 2001 seterr(sc, "%s: %s", str, gai_strerror(error)); 2002 return (NULL); 2003 } 2004 freeaddrinfo(res); 2005 *strp = p + 1; 2006 return (p); 2007 } 2008 2009 /** 2010 * Try to get an IPv4 address. This starts with a digit and consists of digits 2011 * and dots, is not longer INET_ADDRSTRLEN and must be parseable by 2012 * inet_aton(). 2013 * 2014 * \param sc client struct to set errors 2015 * \param strp possible start of IPv4 address; updated to point to the 2016 * next character to parse 2017 * 2018 * \return end of address (equals *strp if there is none) or NULL 2019 * on errors 2020 */ 2021 static inline const char * 2022 get_ipv4(struct snmp_client *sc, const char **strp) 2023 { 2024 const char *p = *strp; 2025 2026 while (isascii(*p) && (isdigit(*p) || *p == '.')) 2027 p++; 2028 2029 if (p - *strp > INET_ADDRSTRLEN) { 2030 seterr(sc, "IPv4 address too long '%.*s'", p - *strp, *strp); 2031 return (NULL); 2032 } 2033 if (*strp == p) 2034 return *strp; 2035 2036 char str[INET_ADDRSTRLEN + 1]; 2037 strncpy(str, *strp, p - *strp); 2038 str[p - *strp] = '\0'; 2039 2040 struct in_addr addr; 2041 if (inet_aton(str, &addr) != 1) { 2042 seterr(sc, "illegal IPv4 address '%s'", str); 2043 return (NULL); 2044 } 2045 2046 *strp = p; 2047 return (p); 2048 } 2049 2050 /** 2051 * Try to get a hostname. This includes everything up to but not including 2052 * the last colon (if any). There is no length restriction. 2053 * 2054 * \param sc client struct to set errors 2055 * \param strp possible start of hostname; updated to point to the next 2056 * character to parse (the trailing NUL character or the last 2057 * colon) 2058 * 2059 * \return end of address (equals *strp if there is none) 2060 */ 2061 static inline const char * 2062 get_host(struct snmp_client *sc __unused, const char **strp) 2063 { 2064 const char *p = strrchr(*strp, ':'); 2065 2066 if (p == NULL) { 2067 *strp += strlen(*strp); 2068 return (*strp); 2069 } 2070 2071 *strp = p; 2072 return (p); 2073 } 2074 2075 /** 2076 * Try to get a port number. This start with a colon and extends to the end 2077 * of string. The port number must not be empty. 2078 * 2079 * \param sc client struct to set errors 2080 * \param strp possible start of port specification; if this points to a 2081 * colon there is a port specification 2082 * 2083 * \return end of port number (equals *strp if there is none); NULL 2084 * if there is no port number 2085 */ 2086 static inline const char * 2087 get_port(struct snmp_client *sc, const char **strp) 2088 { 2089 if (**strp != ':') 2090 return (*strp + 1); 2091 2092 if ((*strp)[1] == '\0') { 2093 seterr(sc, "empty port name"); 2094 return (NULL); 2095 } 2096 2097 *strp += strlen(*strp); 2098 return (*strp); 2099 } 2100 2101 /** 2102 * Save the string in the range given by two pointers. 2103 * 2104 * \param sc client struct to set errors 2105 * \param s begin and end pointers 2106 * 2107 * \return freshly allocated copy of the string between s[0] and s[1] 2108 */ 2109 static inline char * 2110 save_str(struct snmp_client *sc, const char *const s[2]) 2111 { 2112 char *m; 2113 2114 if ((m = malloc(s[1] - s[0] + 1)) == NULL) { 2115 seterr(sc, "%s: %s", __func__, strerror(errno)); 2116 return (NULL); 2117 } 2118 strncpy(m, s[0], s[1] - s[0]); 2119 m[s[1] - s[0]] = '\0'; 2120 2121 return (m); 2122 } 2123 2124 /** 2125 * Parse a server specification. All parts are optional: 2126 * 2127 * [<trans>::][<comm>@][<host-or-ip>][:<port>] 2128 * 2129 * The transport string consists of letters, digits or '_' and starts with 2130 * a letter or digit. It is terminated by two colons and may not be empty. 2131 * 2132 * The community string is terminated by the last '@' and does not exceed 2133 * SNMP_COMMUNITY_MAXLEN. It may be empty. 2134 * 2135 * The host or ip is either an IPv4 address (as parsed by inet_pton()), an 2136 * IPv6 address in '[' and ']' and parseable by inet_aton() or a hostname 2137 * terminated by the last colon or by the NUL character. 2138 * 2139 * The port number may be specified numerically or symbolically and starts 2140 * with the last colon. 2141 * 2142 * The functions sets the chost, cport, trans, read_community and 2143 * write_community fields on success and the error field on errors. 2144 * The chost and cport fields are allocated by malloc(3), their previous 2145 * content is deallocated by free(3). 2146 * 2147 * The function explicitly allows mismatches between the transport and 2148 * the address type in order to support IPv4 in IPv6 addresses. 2149 * 2150 * \param sc client struct to fill 2151 * \param str string to parse 2152 * 2153 * \return 0 on success and -1 on errors 2154 */ 2155 int 2156 snmp_parse_server(struct snmp_client *sc, const char *str) 2157 { 2158 const char *const orig = str; 2159 2160 /* parse input */ 2161 int def_trans = 0, trans = get_transp(sc, &str); 2162 if (trans < 0) 2163 return (-1); 2164 /* choose automatically */ 2165 if (orig == str) 2166 def_trans = 1; 2167 2168 const char *const comm[2] = { 2169 str, 2170 get_comm(sc, &str), 2171 }; 2172 if (comm[1] == NULL) 2173 return (-1); 2174 2175 const char *const ipv6[2] = { 2176 str + 1, 2177 get_ipv6(sc, &str), 2178 }; 2179 if (ipv6[1] == NULL) 2180 return (-1); 2181 2182 const char *ipv4[2] = { 2183 str, 2184 str, 2185 }; 2186 2187 const char *host[2] = { 2188 str, 2189 str, 2190 }; 2191 2192 if (ipv6[0] == ipv6[1]) { 2193 ipv4[1] = get_ipv4(sc, &str); 2194 2195 if (ipv4[0] == ipv4[1]) 2196 host[1] = get_host(sc, &str); 2197 } 2198 2199 const char *port[2] = { 2200 str + 1, 2201 get_port(sc, &str), 2202 }; 2203 if (port[1] == NULL) 2204 return (-1); 2205 2206 if (*str != '\0') { 2207 seterr(sc, "junk at end of server specification '%s'", str); 2208 return (-1); 2209 } 2210 2211 #if DEBUG_PARSE 2212 printf("transp: %d (def=%d)\n", trans, def_trans); 2213 printf("comm: %zu %zu\n", comm[0] - orig, comm[1] - orig); 2214 printf("ipv6: %zu %zu\n", ipv6[0] - orig, ipv6[1] - orig); 2215 printf("ipv4: %zu %zu\n", ipv4[0] - orig, ipv4[1] - orig); 2216 printf("host: %zu %zu\n", host[0] - orig, host[1] - orig); 2217 printf("port: %zu %zu\n", port[0] - orig, port[1] - orig); 2218 #endif 2219 2220 /* analyse and allocate */ 2221 char *chost; 2222 2223 if (ipv6[0] != ipv6[1]) { 2224 if ((chost = save_str(sc, ipv6)) == NULL) 2225 return (-1); 2226 if (def_trans || trans == SNMP_TRANS_UDP) 2227 /* assume the user meant udp6:: */ 2228 trans = SNMP_TRANS_UDP6; 2229 } else if (ipv4[0] != ipv4[1]) { 2230 if ((chost = save_str(sc, ipv4)) == NULL) 2231 return (-1); 2232 if (def_trans) 2233 trans = SNMP_TRANS_UDP; 2234 } else { 2235 if ((chost = save_str(sc, host)) == NULL) 2236 return (-1); 2237 2238 if (def_trans) { 2239 /* 2240 * Default transport is UDP unless the host contains 2241 * a slash in which case we default to DGRAM. 2242 */ 2243 for (const char *p = host[0]; p < host[1]; p++) 2244 if (*p == '/') { 2245 trans = SNMP_TRANS_LOC_DGRAM; 2246 break; 2247 } 2248 } 2249 } 2250 2251 char *cport; 2252 2253 if (port[0] == port[1] && ( 2254 trans == SNMP_TRANS_UDP || trans == SNMP_TRANS_UDP6)) { 2255 /* If port was not specified, use "snmp" name by default */ 2256 cport = strdup("snmp"); 2257 } else 2258 cport = save_str(sc, port); 2259 2260 if (cport == NULL) { 2261 free(chost); 2262 return (-1); 2263 } 2264 2265 /* commit */ 2266 sc->trans = trans; 2267 2268 /* 2269 * If community string was specified and it is empty, overwrite it. 2270 * If it was not specified, use default. 2271 */ 2272 if (comm[0] != comm[1] || strrchr(comm[0], '@') != NULL) { 2273 strncpy(sc->read_community, comm[0], comm[1] - comm[0]); 2274 sc->read_community[comm[1] - comm[0]] = '\0'; 2275 strncpy(sc->write_community, comm[0], comm[1] - comm[0]); 2276 sc->write_community[comm[1] - comm[0]] = '\0'; 2277 } 2278 2279 free(sc->chost); 2280 sc->chost = chost; 2281 free(sc->cport); 2282 sc->cport = cport; 2283 2284 #if DEBUG_PARSE 2285 printf("Committed values:\n"); 2286 printf("trans: %d\n", sc->trans); 2287 printf("comm: '%s'/'%s'\n", sc->read_community, sc->write_community); 2288 printf("host: '%s'\n", sc->chost); 2289 printf("port: '%s'\n", sc->cport); 2290 #endif 2291 return (0); 2292 } 2293