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