1108983Simp/* 2108983Simp * Copyright (c) 2004-2005 3108983Simp * Hartmut Brandt. 4108983Simp * All rights reserved. 5108983Simp * Copyright (c) 2001-2003 6108983Simp * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 7108983Simp * All rights reserved. 8108983Simp * 9108983Simp * Author: Harti Brandt <harti@freebsd.org> 10108983Simp * Kendy Kutzner 11225861Swblock * 12225861Swblock * Redistribution and use in source and binary forms, with or without 13225861Swblock * modification, are permitted provided that the following conditions 14225861Swblock * are met: 15108983Simp * 1. Redistributions of source code must retain the above copyright 16108983Simp * notice, this list of conditions and the following disclaimer. 17108983Simp * 2. Redistributions in binary form must reproduce the above copyright 18108983Simp * notice, this list of conditions and the following disclaimer in the 19108983Simp * documentation and/or other materials provided with the distribution. 20148471Simp * 21108983Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22139027Sbrueffer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23146969Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24139027Sbrueffer * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25108983Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26108983Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27108983Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28108983Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29108983Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30108983Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31159126Sthompsa * SUCH DAMAGE. 32159126Sthompsa * 33108983Simp * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 34187743Ssam * 35225861Swblock * Support functions for SNMP clients. 36225861Swblock */ 37187743Ssam#include <sys/types.h> 38187743Ssam#include <sys/time.h> 39159126Sthompsa#include <sys/queue.h> 40159126Sthompsa#include <sys/socket.h> 41159126Sthompsa#include <sys/un.h> 42159126Sthompsa#include <stdio.h> 43108983Simp#include <stdlib.h> 44108983Simp#include <stddef.h> 45147088Sbrooks#include <stdarg.h> 46225861Swblock#include <string.h> 47147088Sbrooks#include <errno.h> 48216983Sjpaetzel#include <unistd.h> 49147088Sbrooks#include <fcntl.h> 50147088Sbrooks#include <netdb.h> 51147088Sbrooks#ifdef HAVE_STDINT_H 52147088Sbrooks#include <stdint.h> 53147088Sbrooks#elif defined(HAVE_INTTYPES_H) 54148642Ssam#include <inttypes.h> 55175683Smtm#endif 56147088Sbrooks#include <limits.h> 57147088Sbrooks#ifdef HAVE_ERR_H 58148642Ssam#include <err.h> 59148642Ssam#endif 60148642Ssam 61148642Ssam#include "support.h" 62148642Ssam#include "asn1.h" 63148642Ssam#include "snmp.h" 64148642Ssam#include "snmpclient.h" 65148642Ssam#include "snmppriv.h" 66148642Ssam 67148642Ssam/* global context */ 68148642Ssamstruct snmp_client snmp_client; 69148642Ssam 70148642Ssam/* List of all outstanding requests */ 71148642Ssamstruct sent_pdu { 72148642Ssam int reqid; 73148642Ssam struct snmp_pdu *pdu; 74148642Ssam struct timeval time; 75175683Smtm u_int retrycount; 76148642Ssam snmp_send_cb_f callback; 77148642Ssam void *arg; 78108983Simp void *timeout_id; 79108983Simp LIST_ENTRY(sent_pdu) entries; 80108983Simp}; 81139281SbruefferLIST_HEAD(sent_pdu_list, sent_pdu); 82108983Simp 83108983Simpstatic struct sent_pdu_list sent_pdus; 84108983Simp 85108983Simp/* 86108983Simp * Prototype table entry. All C-structure produced by the table function must 87108983Simp * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 88108983Simp * are compatible with each other in the sense implied by ANSI-C. 89108983Simp */ 90225861Swblockstruct entry { 91152326Semax TAILQ_ENTRY(entry) link; 92152326Semax uint64_t found; 93175683Smtm}; 94152326SemaxTAILQ_HEAD(table, entry); 95152326Semax 96152326Semax/* 97175683Smtm * working list entry. This list is used to hold the Index part of the 98152326Semax * table row's. The entry list and the work list parallel each other. 99152326Semax */ 100215195Semaxstruct work { 101215195Semax TAILQ_ENTRY(work) link; 102215195Semax struct asn_oid index; 103215195Semax}; 104215195SemaxTAILQ_HEAD(worklist, work); 105215195Semax 106215195Semax/* 107139281Sbrueffer * Table working data 108134584Sbrooks */ 109134584Sbrooksstruct tabwork { 110156782Semax const struct snmp_table *descr; 111134584Sbrooks struct table *table; 112134584Sbrooks struct worklist worklist; 113134584Sbrooks uint32_t last_change; 114156331Semax int first; 115134584Sbrooks u_int iter; 116134584Sbrooks snmp_table_cb_f callback; 117138175Siedowse void *arg; 118138175Siedowse struct snmp_pdu pdu; 119175683Smtm}; 120138175Siedowse 121138175Siedowse/* 122208060Sdougb * Set the error string 123208060Sdougb */ 124208060Sdougbstatic void 125208060Sdougbseterr(struct snmp_client *sc, const char *fmt, ...) 126179804Skmacy{ 127153300Siedowse va_list ap; 128225861Swblock 129153300Siedowse va_start(ap, fmt); 130153300Siedowse vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 131153300Siedowse va_end(ap); 132153300Siedowse} 133153300Siedowse 134153300Siedowse/* 135153300Siedowse * Free the entire table and work list. If table is NULL only the worklist 136153300Siedowse * is freed. 137153300Siedowse */ 138153300Siedowsestatic void 139153300Siedowsetable_free(struct tabwork *work, int all) 140153300Siedowse{ 141153300Siedowse struct work *w; 142192198Smaxim struct entry *e; 143153300Siedowse const struct snmp_table_entry *d; 144153300Siedowse u_int i; 145153300Siedowse 146153300Siedowse while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 147153300Siedowse TAILQ_REMOVE(&work->worklist, w, link); 148207020Sthompsa free(w); 149207020Sthompsa } 150207020Sthompsa 151207020Sthompsa if (all == 0) 152207020Sthompsa return; 153207020Sthompsa 154207020Sthompsa while ((e = TAILQ_FIRST(work->table)) != NULL) { 155207020Sthompsa for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 156153300Siedowse i++) { 157153300Siedowse d = &work->descr->entries[i]; 158108983Simp if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 159148471Simp (e->found & ((uint64_t)1 << i))) 160148471Simp free(*(void **)(void *) 161108983Simp ((u_char *)e + d->offset)); 162108983Simp } 163108983Simp TAILQ_REMOVE(work->table, e, link); 164131646Simp free(e); 165108983Simp } 166108983Simp} 167108983Simp 168114799Simp/* 169166754Simp * Find the correct table entry for the given variable. If non exists, 170166754Simp * create one. 171119254Simp */ 172114852Simpstatic struct entry * 173119254Simptable_find(struct tabwork *work, const struct asn_oid *var) 174108983Simp{ 175166701Sjoerg struct entry *e, *e1; 176166701Sjoerg struct work *w, *w1; 177166701Sjoerg u_int i, p, j; 178166701Sjoerg size_t len; 179166701Sjoerg u_char *ptr; 180166701Sjoerg struct asn_oid oid; 181166701Sjoerg 182166701Sjoerg /* get index */ 183166701Sjoerg asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 184166701Sjoerg 185166701Sjoerg e = TAILQ_FIRST(work->table); 186166701Sjoerg w = TAILQ_FIRST(&work->worklist); 187166701Sjoerg while (e != NULL) { 188166701Sjoerg if (asn_compare_oid(&w->index, &oid) == 0) 189166701Sjoerg return (e); 190166701Sjoerg e = TAILQ_NEXT(e, link); 191166701Sjoerg w = TAILQ_NEXT(w, link); 192166701Sjoerg } 193166701Sjoerg 194166701Sjoerg /* Not found create new one */ 195166701Sjoerg if ((e = malloc(work->descr->entry_size)) == NULL) { 196166701Sjoerg seterr(&snmp_client, "no memory for table entry"); 197166701Sjoerg return (NULL); 198166701Sjoerg } 199166701Sjoerg if ((w = malloc(sizeof(*w))) == NULL) { 200166701Sjoerg seterr(&snmp_client, "no memory for table entry"); 201166701Sjoerg free(e); 202166701Sjoerg return (NULL); 203166701Sjoerg } 204166701Sjoerg w->index = oid; 205139281Sbrueffer memset(e, 0, work->descr->entry_size); 206123626Snjl 207123626Snjl /* decode index */ 208123626Snjl p = work->descr->table.len + 2; 209125366Snjl for (i = 0; i < work->descr->index_size; i++) { 210123626Snjl switch (work->descr->entries[i].syntax) { 211123626Snjl 212125366Snjl case SNMP_SYNTAX_INTEGER: 213125366Snjl if (var->len < p + 1) { 214125366Snjl seterr(&snmp_client, "bad index: need integer"); 215125366Snjl goto err; 216125366Snjl } 217125366Snjl if (var->subs[p] > INT32_MAX) { 218125366Snjl seterr(&snmp_client, 219125366Snjl "bad index: integer too large"); 220125366Snjl goto err; 221125366Snjl } 222168495Spjd *(int32_t *)(void *)((u_char *)e + 223168495Spjd work->descr->entries[i].offset) = var->subs[p++]; 224168495Spjd break; 225168495Spjd 226168495Spjd case SNMP_SYNTAX_OCTETSTRING: 227168495Spjd if (var->len < p + 1) { 228168495Spjd seterr(&snmp_client, 229168495Spjd "bad index: need string length"); 230168495Spjd goto err; 231168495Spjd } 232168497Spjd len = var->subs[p++]; 233168495Spjd if (var->len < p + len) { 234168495Spjd seterr(&snmp_client, 235168495Spjd "bad index: string too short"); 236168495Spjd goto err; 237168495Spjd } 238168495Spjd if ((ptr = malloc(len + 1)) == NULL) { 239168495Spjd seterr(&snmp_client, 240168495Spjd "no memory for index string"); 241168495Spjd goto err; 242168495Spjd } 243168495Spjd for (j = 0; j < len; j++) { 244168495Spjd if (var->subs[p] > UCHAR_MAX) { 245168495Spjd seterr(&snmp_client, 246168495Spjd "bad index: char too large"); 247168495Spjd free(ptr); 248168495Spjd goto err; 249168495Spjd } 250168495Spjd ptr[j] = var->subs[p++]; 251168495Spjd } 252168495Spjd ptr[j] = '\0'; 253170976Snjl *(u_char **)(void *)((u_char *)e + 254170976Snjl work->descr->entries[i].offset) = ptr; 255170976Snjl *(size_t *)(void *)((u_char *)e + 256170976Snjl work->descr->entries[i].offset + sizeof(u_char *)) 257170976Snjl = len; 258170976Snjl break; 259170976Snjl 260170976Snjl case SNMP_SYNTAX_OID: 261170976Snjl if (var->len < p + 1) { 262170976Snjl seterr(&snmp_client, 263170976Snjl "bad index: need oid length"); 264170976Snjl goto err; 265170976Snjl } 266108983Simp oid.len = var->subs[p++]; 267108983Simp if (var->len < p + oid.len) { 268225861Swblock seterr(&snmp_client, 269225861Swblock "bad index: oid too short"); 270225861Swblock goto err; 271225861Swblock } 272225861Swblock for (j = 0; j < oid.len; j++) 273108983Simp oid.subs[j] = var->subs[p++]; 274108983Simp *(struct asn_oid *)(void *)((u_char *)e + 275108983Simp work->descr->entries[i].offset) = oid; 276108983Simp break; 277108983Simp 278108983Simp case SNMP_SYNTAX_IPADDRESS: 279108983Simp if (var->len < p + 4) { 280108983Simp seterr(&snmp_client, 281108983Simp "bad index: need ip-address"); 282108983Simp goto err; 283108983Simp } 284108983Simp for (j = 0; j < 4; j++) { 285108983Simp if (var->subs[p] > 0xff) { 286108983Simp seterr(&snmp_client, 287108983Simp "bad index: ipaddress too large"); 288121493Snjl goto err; 289121493Snjl } 290225861Swblock ((u_char *)e + 291121493Snjl work->descr->entries[i].offset)[j] = 292121493Snjl var->subs[p++]; 293121493Snjl } 294121493Snjl break; 295121493Snjl 296121493Snjl case SNMP_SYNTAX_GAUGE: 297121493Snjl if (var->len < p + 1) { 298121493Snjl seterr(&snmp_client, 299121493Snjl "bad index: need unsigned"); 300121493Snjl goto err; 301121493Snjl } 302121493Snjl if (var->subs[p] > UINT32_MAX) { 303220168Strasz seterr(&snmp_client, 304170976Snjl "bad index: unsigned too large"); 305121493Snjl goto err; 306121493Snjl } 307121493Snjl *(uint32_t *)(void *)((u_char *)e + 308121493Snjl work->descr->entries[i].offset) = var->subs[p++]; 309121493Snjl break; 310121493Snjl 311121493Snjl case SNMP_SYNTAX_COUNTER: 312121493Snjl case SNMP_SYNTAX_TIMETICKS: 313121493Snjl case SNMP_SYNTAX_COUNTER64: 314121493Snjl case SNMP_SYNTAX_NULL: 315121493Snjl case SNMP_SYNTAX_NOSUCHOBJECT: 316220168Strasz case SNMP_SYNTAX_NOSUCHINSTANCE: 317220168Strasz case SNMP_SYNTAX_ENDOFMIBVIEW: 318225861Swblock abort(); 319220168Strasz } 320220168Strasz e->found |= (uint64_t)1 << i; 321220168Strasz } 322220168Strasz 323220168Strasz /* link into the correct place */ 324220168Strasz e1 = TAILQ_FIRST(work->table); 325108983Simp w1 = TAILQ_FIRST(&work->worklist); 326 while (e1 != NULL) { 327 if (asn_compare_oid(&w1->index, &w->index) > 0) 328 break; 329 e1 = TAILQ_NEXT(e1, link); 330 w1 = TAILQ_NEXT(w1, link); 331 } 332 if (e1 == NULL) { 333 TAILQ_INSERT_TAIL(work->table, e, link); 334 TAILQ_INSERT_TAIL(&work->worklist, w, link); 335 } else { 336 TAILQ_INSERT_BEFORE(e1, e, link); 337 TAILQ_INSERT_BEFORE(w1, w, link); 338 } 339 340 return (e); 341 342 err: 343 /* 344 * Error happend. Free all octet string index parts and the entry 345 * itself. 346 */ 347 for (i = 0; i < work->descr->index_size; i++) { 348 if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 349 (e->found & ((uint64_t)1 << i))) 350 free(*(void **)(void *)((u_char *)e + 351 work->descr->entries[i].offset)); 352 } 353 free(e); 354 free(w); 355 return (NULL); 356} 357 358/* 359 * Assign the value 360 */ 361static int 362table_value(const struct snmp_table *descr, struct entry *e, 363 const struct snmp_value *b) 364{ 365 u_int i; 366 u_char *ptr; 367 368 for (i = descr->index_size; 369 descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 370 if (descr->entries[i].subid == 371 b->var.subs[descr->table.len + 1]) 372 break; 373 if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 374 return (0); 375 376 /* check syntax */ 377 if (b->syntax != descr->entries[i].syntax) { 378 seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 379 descr->entries[i].syntax); 380 return (-1); 381 } 382 383 switch (b->syntax) { 384 385 case SNMP_SYNTAX_INTEGER: 386 *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 387 b->v.integer; 388 break; 389 390 case SNMP_SYNTAX_OCTETSTRING: 391 if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 392 seterr(&snmp_client, "no memory for string"); 393 return (-1); 394 } 395 memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 396 ptr[b->v.octetstring.len] = '\0'; 397 *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 398 ptr; 399 *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 400 sizeof(u_char *)) = b->v.octetstring.len; 401 break; 402 403 case SNMP_SYNTAX_OID: 404 *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 405 b->v.oid; 406 break; 407 408 case SNMP_SYNTAX_IPADDRESS: 409 memcpy((u_char *)e + descr->entries[i].offset, 410 b->v.ipaddress, 4); 411 break; 412 413 case SNMP_SYNTAX_COUNTER: 414 case SNMP_SYNTAX_GAUGE: 415 case SNMP_SYNTAX_TIMETICKS: 416 *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 417 b->v.uint32; 418 break; 419 420 case SNMP_SYNTAX_COUNTER64: 421 *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 422 b->v.counter64; 423 break; 424 425 case SNMP_SYNTAX_NULL: 426 case SNMP_SYNTAX_NOSUCHOBJECT: 427 case SNMP_SYNTAX_NOSUCHINSTANCE: 428 case SNMP_SYNTAX_ENDOFMIBVIEW: 429 abort(); 430 } 431 e->found |= (uint64_t)1 << i; 432 433 return (0); 434} 435 436/* 437 * Initialize the first PDU to send 438 */ 439static void 440table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 441{ 442 if (snmp_client.version == SNMP_V1) 443 snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 444 else { 445 snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 446 pdu->error_index = 10; 447 } 448 if (descr->last_change.len != 0) { 449 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 450 pdu->bindings[pdu->nbindings].var = descr->last_change; 451 pdu->nbindings++; 452 if (pdu->version != SNMP_V1) 453 pdu->error_status++; 454 } 455 pdu->bindings[pdu->nbindings].var = descr->table; 456 pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 457 pdu->nbindings++; 458} 459 460/* 461 * Return code: 462 * 0 - End Of Table 463 * -1 - Error 464 * -2 - Last change changed - again 465 * +1 - ok, continue 466 */ 467static int 468table_check_response(struct tabwork *work, const struct snmp_pdu *resp) 469{ 470 const struct snmp_value *b; 471 struct entry *e; 472 473 if (resp->error_status != SNMP_ERR_NOERROR) { 474 if (snmp_client.version == SNMP_V1 && 475 resp->error_status == SNMP_ERR_NOSUCHNAME && 476 resp->error_index == 477 ((work->descr->last_change.len == 0) ? 1 : 2)) 478 /* EOT */ 479 return (0); 480 /* Error */ 481 seterr(&snmp_client, "error fetching table: status=%d index=%d", 482 resp->error_status, resp->error_index); 483 return (-1); 484 } 485 486 for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 487 if (work->descr->last_change.len != 0 && b == resp->bindings) { 488 if (!asn_is_suboid(&work->descr->last_change, &b->var) || 489 b->var.len != work->descr->last_change.len + 1 || 490 b->var.subs[work->descr->last_change.len] != 0) { 491 seterr(&snmp_client, 492 "last_change: bad response"); 493 return (-1); 494 } 495 if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 496 seterr(&snmp_client, 497 "last_change: bad syntax %u", b->syntax); 498 return (-1); 499 } 500 if (work->first) { 501 work->last_change = b->v.uint32; 502 work->first = 0; 503 504 } else if (work->last_change != b->v.uint32) { 505 if (++work->iter >= work->descr->max_iter) { 506 seterr(&snmp_client, 507 "max iteration count exceeded"); 508 return (-1); 509 } 510 table_free(work, 1); 511 return (-2); 512 } 513 514 continue; 515 } 516 if (!asn_is_suboid(&work->descr->table, &b->var) || 517 b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 518 return (0); 519 520 if ((e = table_find(work, &b->var)) == NULL) 521 return (-1); 522 if (table_value(work->descr, e, b)) 523 return (-1); 524 } 525 return (+1); 526} 527 528/* 529 * Check table consistency 530 */ 531static int 532table_check_cons(struct tabwork *work) 533{ 534 struct entry *e; 535 536 TAILQ_FOREACH(e, work->table, link) 537 if ((e->found & work->descr->req_mask) != 538 work->descr->req_mask) { 539 if (work->descr->last_change.len == 0) { 540 if (++work->iter >= work->descr->max_iter) { 541 seterr(&snmp_client, 542 "max iteration count exceeded"); 543 return (-1); 544 } 545 return (-2); 546 } 547 seterr(&snmp_client, "inconsistency detected %llx %llx", 548 e->found, work->descr->req_mask); 549 return (-1); 550 } 551 return (0); 552} 553 554/* 555 * Fetch a table. Returns 0 if ok, -1 on errors. 556 * This is the synchronous variant. 557 */ 558int 559snmp_table_fetch(const struct snmp_table *descr, void *list) 560{ 561 struct snmp_pdu resp; 562 struct tabwork work; 563 int ret; 564 565 work.descr = descr; 566 work.table = (struct table *)list; 567 work.iter = 0; 568 TAILQ_INIT(work.table); 569 TAILQ_INIT(&work.worklist); 570 work.callback = NULL; 571 work.arg = NULL; 572 573 again: 574 /* 575 * We come to this label when the code detects that the table 576 * has changed while fetching it. 577 */ 578 work.first = 1; 579 work.last_change = 0; 580 table_init_pdu(descr, &work.pdu); 581 582 for (;;) { 583 if (snmp_dialog(&work.pdu, &resp)) { 584 table_free(&work, 1); 585 return (-1); 586 } 587 if ((ret = table_check_response(&work, &resp)) == 0) { 588 snmp_pdu_free(&resp); 589 break; 590 } 591 if (ret == -1) { 592 snmp_pdu_free(&resp); 593 table_free(&work, 1); 594 return (-1); 595 } 596 if (ret == -2) { 597 snmp_pdu_free(&resp); 598 goto again; 599 } 600 601 work.pdu.bindings[work.pdu.nbindings - 1].var = 602 resp.bindings[resp.nbindings - 1].var; 603 604 snmp_pdu_free(&resp); 605 } 606 607 if ((ret = table_check_cons(&work)) == -1) { 608 table_free(&work, 1); 609 return (-1); 610 } 611 if (ret == -2) { 612 table_free(&work, 1); 613 goto again; 614 } 615 /* 616 * Free index list 617 */ 618 table_free(&work, 0); 619 return (0); 620} 621 622/* 623 * Callback for table 624 */ 625static void 626table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 627{ 628 struct tabwork *work = arg; 629 int ret; 630 631 if (resp == NULL) { 632 /* timeout */ 633 seterr(&snmp_client, "no response to fetch table request"); 634 table_free(work, 1); 635 work->callback(work->table, work->arg, -1); 636 free(work); 637 return; 638 } 639 640 if ((ret = table_check_response(work, resp)) == 0) { 641 /* EOT */ 642 snmp_pdu_free(resp); 643 644 if ((ret = table_check_cons(work)) == -1) { 645 /* error happend */ 646 table_free(work, 1); 647 work->callback(work->table, work->arg, -1); 648 free(work); 649 return; 650 } 651 if (ret == -2) { 652 /* restart */ 653 again: 654 table_free(work, 1); 655 work->first = 1; 656 work->last_change = 0; 657 table_init_pdu(work->descr, &work->pdu); 658 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 659 work->callback(work->table, work->arg, -1); 660 free(work); 661 return; 662 } 663 return; 664 } 665 /* 666 * Free index list 667 */ 668 table_free(work, 0); 669 work->callback(work->table, work->arg, 0); 670 free(work); 671 return; 672 } 673 674 if (ret == -1) { 675 /* error */ 676 snmp_pdu_free(resp); 677 table_free(work, 1); 678 work->callback(work->table, work->arg, -1); 679 free(work); 680 return; 681 } 682 683 if (ret == -2) { 684 /* again */ 685 snmp_pdu_free(resp); 686 goto again; 687 } 688 689 /* next part */ 690 691 work->pdu.bindings[work->pdu.nbindings - 1].var = 692 resp->bindings[resp->nbindings - 1].var; 693 694 snmp_pdu_free(resp); 695 696 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 697 table_free(work, 1); 698 work->callback(work->table, work->arg, -1); 699 free(work); 700 return; 701 } 702} 703 704int 705snmp_table_fetch_async(const struct snmp_table *descr, void *list, 706 snmp_table_cb_f func, void *arg) 707{ 708 struct tabwork *work; 709 710 if ((work = malloc(sizeof(*work))) == NULL) { 711 seterr(&snmp_client, "%s", strerror(errno)); 712 return (-1); 713 } 714 715 work->descr = descr; 716 work->table = (struct table *)list; 717 work->iter = 0; 718 TAILQ_INIT(work->table); 719 TAILQ_INIT(&work->worklist); 720 721 work->callback = func; 722 work->arg = arg; 723 724 /* 725 * Start by sending the first PDU 726 */ 727 work->first = 1; 728 work->last_change = 0; 729 table_init_pdu(descr, &work->pdu); 730 731 if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 732 free(work); 733 work = NULL; 734 return (-1); 735 } 736 return (0); 737} 738 739/* 740 * Append an index to an oid 741 */ 742int 743snmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 744{ 745 va_list va; 746 int size; 747 char *nextptr; 748 const u_char *str; 749 size_t len; 750 struct in_addr ina; 751 int ret; 752 753 va_start(va, fmt); 754 755 size = 0; 756 757 ret = 0; 758 while (*fmt != '\0') { 759 switch (*fmt++) { 760 case 'i': 761 /* just an integer more */ 762 if (oid->len + 1 > ASN_MAXOIDLEN) { 763 warnx("%s: OID too long for integer", __func__); 764 ret = -1; 765 break; 766 } 767 oid->subs[oid->len++] = va_arg(va, asn_subid_t); 768 break; 769 770 case 'a': 771 /* append an IP address */ 772 if (oid->len + 4 > ASN_MAXOIDLEN) { 773 warnx("%s: OID too long for ip-addr", __func__); 774 ret = -1; 775 break; 776 } 777 ina = va_arg(va, struct in_addr); 778 ina.s_addr = ntohl(ina.s_addr); 779 oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 780 oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 781 oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 782 oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 783 break; 784 785 case 's': 786 /* append a null-terminated string, 787 * length is computed */ 788 str = (const u_char *)va_arg(va, const char *); 789 len = strlen((const char *)str); 790 if (oid->len + len + 1 > ASN_MAXOIDLEN) { 791 warnx("%s: OID too long for string", __func__); 792 ret = -1; 793 break; 794 } 795 oid->subs[oid->len++] = len; 796 while (len--) 797 oid->subs[oid->len++] = *str++; 798 break; 799 800 case '(': 801 /* the integer value between ( and ) is stored 802 * in size */ 803 size = strtol(fmt, &nextptr, 10); 804 if (*nextptr != ')') 805 abort(); 806 fmt = ++nextptr; 807 break; 808 809 case 'b': 810 /* append `size` characters */ 811 str = (const u_char *)va_arg(va, const char *); 812 if (oid->len + size > ASN_MAXOIDLEN) { 813 warnx("%s: OID too long for string", __func__); 814 ret = -1; 815 break; 816 } 817 while (size--) 818 oid->subs[oid->len++] = *str++; 819 break; 820 821 case 'c': 822 /* get size and the octets from the arguments */ 823 size = va_arg(va, size_t); 824 str = va_arg(va, const u_char *); 825 if (oid->len + size + 1 > ASN_MAXOIDLEN) { 826 warnx("%s: OID too long for string", __func__); 827 ret = -1; 828 break; 829 } 830 oid->subs[oid->len++] = size; 831 while (size--) 832 oid->subs[oid->len++] = *str++; 833 break; 834 835 default: 836 abort(); 837 } 838 } 839 va_end(va); 840 return (ret); 841} 842 843/* 844 * Initialize a client structure 845 */ 846void 847snmp_client_init(struct snmp_client *c) 848{ 849 memset(c, 0, sizeof(*c)); 850 851 c->version = SNMP_V2c; 852 c->trans = SNMP_TRANS_UDP; 853 c->chost = NULL; 854 c->cport = NULL; 855 856 strcpy(c->read_community, "public"); 857 strcpy(c->write_community, "private"); 858 859 c->security_model = SNMP_SECMODEL_USM; 860 strcpy(c->cname, ""); 861 862 c->timeout.tv_sec = 3; 863 c->timeout.tv_usec = 0; 864 c->retries = 3; 865 c->dump_pdus = 0; 866 c->txbuflen = c->rxbuflen = 10000; 867 868 c->fd = -1; 869 870 c->max_reqid = INT32_MAX; 871 c->min_reqid = 0; 872 c->next_reqid = 0; 873 874 c->engine.max_msg_size = 1500; /* XXX */ 875} 876 877 878/* 879 * Open UDP client socket 880 */ 881static int 882open_client_udp(const char *host, const char *port) 883{ 884 int error; 885 char *ptr; 886 struct addrinfo hints, *res0, *res; 887 888 /* copy host- and portname */ 889 if (snmp_client.chost == NULL) { 890 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 891 == NULL) { 892 seterr(&snmp_client, "%s", strerror(errno)); 893 return (-1); 894 } 895 strcpy(snmp_client.chost, DEFAULT_HOST); 896 } 897 if (host != NULL) { 898 if ((ptr = malloc(1 + strlen(host))) == NULL) { 899 seterr(&snmp_client, "%s", strerror(errno)); 900 return (-1); 901 } 902 free(snmp_client.chost); 903 snmp_client.chost = ptr; 904 strcpy(snmp_client.chost, host); 905 } 906 if (snmp_client.cport == NULL) { 907 if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 908 == NULL) { 909 seterr(&snmp_client, "%s", strerror(errno)); 910 return (-1); 911 } 912 strcpy(snmp_client.cport, DEFAULT_PORT); 913 } 914 if (port != NULL) { 915 if ((ptr = malloc(1 + strlen(port))) == NULL) { 916 seterr(&snmp_client, "%s", strerror(errno)); 917 return (-1); 918 } 919 free(snmp_client.cport); 920 snmp_client.cport = ptr; 921 strcpy(snmp_client.cport, port); 922 } 923 924 /* open connection */ 925 memset(&hints, 0, sizeof(hints)); 926 hints.ai_flags = AI_CANONNAME; 927 hints.ai_family = AF_INET; 928 hints.ai_socktype = SOCK_DGRAM; 929 hints.ai_protocol = 0; 930 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 931 if (error != 0) { 932 seterr(&snmp_client, "%s: %s", snmp_client.chost, 933 gai_strerror(error)); 934 return (-1); 935 } 936 res = res0; 937 for (;;) { 938 if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 939 res->ai_protocol)) == -1) { 940 if ((res = res->ai_next) == NULL) { 941 seterr(&snmp_client, "%s", strerror(errno)); 942 freeaddrinfo(res0); 943 return (-1); 944 } 945 } else if (connect(snmp_client.fd, res->ai_addr, 946 res->ai_addrlen) == -1) { 947 if ((res = res->ai_next) == NULL) { 948 seterr(&snmp_client, "%s", strerror(errno)); 949 freeaddrinfo(res0); 950 (void)close(snmp_client.fd); 951 snmp_client.fd = -1; 952 return (-1); 953 } 954 } else 955 break; 956 } 957 freeaddrinfo(res0); 958 return (0); 959} 960 961static void 962remove_local(void) 963{ 964 (void)remove(snmp_client.local_path); 965} 966 967/* 968 * Open local socket 969 */ 970static int 971open_client_local(const char *path) 972{ 973 struct sockaddr_un sa; 974 char *ptr; 975 int stype; 976 977 if (snmp_client.chost == NULL) { 978 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 979 == NULL) { 980 seterr(&snmp_client, "%s", strerror(errno)); 981 return (-1); 982 } 983 strcpy(snmp_client.chost, DEFAULT_LOCAL); 984 } 985 if (path != NULL) { 986 if ((ptr = malloc(1 + strlen(path))) == NULL) { 987 seterr(&snmp_client, "%s", strerror(errno)); 988 return (-1); 989 } 990 free(snmp_client.chost); 991 snmp_client.chost = ptr; 992 strcpy(snmp_client.chost, path); 993 } 994 995 if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 996 stype = SOCK_DGRAM; 997 else 998 stype = SOCK_STREAM; 999 1000 if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 1001 seterr(&snmp_client, "%s", strerror(errno)); 1002 return (-1); 1003 } 1004 1005 snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 1006 "%s", SNMP_LOCAL_PATH); 1007 1008 if (mktemp(snmp_client.local_path) == NULL) { 1009 seterr(&snmp_client, "%s", strerror(errno)); 1010 (void)close(snmp_client.fd); 1011 snmp_client.fd = -1; 1012 return (-1); 1013 } 1014 1015 sa.sun_family = AF_LOCAL; 1016 sa.sun_len = sizeof(sa); 1017 strcpy(sa.sun_path, snmp_client.local_path); 1018 1019 if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1020 seterr(&snmp_client, "%s", strerror(errno)); 1021 (void)close(snmp_client.fd); 1022 snmp_client.fd = -1; 1023 (void)remove(snmp_client.local_path); 1024 return (-1); 1025 } 1026 atexit(remove_local); 1027 1028 sa.sun_family = AF_LOCAL; 1029 sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1030 strlen(snmp_client.chost); 1031 strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1032 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1033 1034 if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1035 seterr(&snmp_client, "%s", strerror(errno)); 1036 (void)close(snmp_client.fd); 1037 snmp_client.fd = -1; 1038 (void)remove(snmp_client.local_path); 1039 return (-1); 1040 } 1041 return (0); 1042} 1043 1044/* 1045 * SNMP_OPEN 1046 */ 1047int 1048snmp_open(const char *host, const char *port, const char *readcomm, 1049 const char *writecomm) 1050{ 1051 struct timeval tout; 1052 1053 /* still open ? */ 1054 if (snmp_client.fd != -1) { 1055 errno = EBUSY; 1056 seterr(&snmp_client, "%s", strerror(errno)); 1057 return (-1); 1058 } 1059 1060 /* copy community strings */ 1061 if (readcomm != NULL) 1062 strlcpy(snmp_client.read_community, readcomm, 1063 sizeof(snmp_client.read_community)); 1064 if (writecomm != NULL) 1065 strlcpy(snmp_client.write_community, writecomm, 1066 sizeof(snmp_client.write_community)); 1067 1068 switch (snmp_client.trans) { 1069 1070 case SNMP_TRANS_UDP: 1071 if (open_client_udp(host, port) != 0) 1072 return (-1); 1073 break; 1074 1075 case SNMP_TRANS_LOC_DGRAM: 1076 case SNMP_TRANS_LOC_STREAM: 1077 if (open_client_local(host) != 0) 1078 return (-1); 1079 break; 1080 1081 default: 1082 seterr(&snmp_client, "bad transport mapping"); 1083 return (-1); 1084 } 1085 tout.tv_sec = 0; 1086 tout.tv_usec = 0; 1087 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1088 &tout, sizeof(struct timeval)) == -1) { 1089 seterr(&snmp_client, "%s", strerror(errno)); 1090 (void)close(snmp_client.fd); 1091 snmp_client.fd = -1; 1092 if (snmp_client.local_path[0] != '\0') 1093 (void)remove(snmp_client.local_path); 1094 return (-1); 1095 } 1096 1097 /* initialize list */ 1098 LIST_INIT(&sent_pdus); 1099 1100 return (0); 1101} 1102 1103 1104/* 1105 * SNMP_CLOSE 1106 * 1107 * closes connection to snmp server 1108 * - function cannot fail 1109 * - clears connection 1110 * - clears list of sent pdus 1111 * 1112 * input: 1113 * void 1114 * return: 1115 * void 1116 */ 1117void 1118snmp_close(void) 1119{ 1120 struct sent_pdu *p1; 1121 1122 if (snmp_client.fd != -1) { 1123 (void)close(snmp_client.fd); 1124 snmp_client.fd = -1; 1125 if (snmp_client.local_path[0] != '\0') 1126 (void)remove(snmp_client.local_path); 1127 } 1128 while(!LIST_EMPTY(&sent_pdus)){ 1129 p1 = LIST_FIRST(&sent_pdus); 1130 if (p1->timeout_id != NULL) 1131 snmp_client.timeout_stop(p1->timeout_id); 1132 LIST_REMOVE(p1, entries); 1133 free(p1); 1134 } 1135 free(snmp_client.chost); 1136 free(snmp_client.cport); 1137} 1138 1139/* 1140 * initialize a snmp_pdu structure 1141 */ 1142void 1143snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1144{ 1145 memset(pdu, 0, sizeof(struct snmp_pdu)); 1146 1147 if (op == SNMP_PDU_SET) 1148 strlcpy(pdu->community, snmp_client.write_community, 1149 sizeof(pdu->community)); 1150 else 1151 strlcpy(pdu->community, snmp_client.read_community, 1152 sizeof(pdu->community)); 1153 1154 pdu->type = op; 1155 pdu->version = snmp_client.version; 1156 pdu->error_status = 0; 1157 pdu->error_index = 0; 1158 pdu->nbindings = 0; 1159 1160 if (snmp_client.version != SNMP_V3) 1161 return; 1162 1163 pdu->identifier = ++snmp_client.identifier; 1164 pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; 1165 pdu->flags = 0; 1166 pdu->security_model = snmp_client.security_model; 1167 1168 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1169 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1170 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1171 snmp_pdu_init_secparams(pdu); 1172 } else 1173 seterr(&snmp_client, "unknown security model"); 1174 1175 if (snmp_client.clen > 0) { 1176 memcpy(pdu->context_engine, snmp_client.cengine, 1177 snmp_client.clen); 1178 pdu->context_engine_len = snmp_client.clen; 1179 } else { 1180 memcpy(pdu->context_engine, snmp_client.engine.engine_id, 1181 snmp_client.engine.engine_len); 1182 pdu->context_engine_len = snmp_client.engine.engine_len; 1183 } 1184 1185 strlcpy(pdu->context_name, snmp_client.cname, 1186 sizeof(pdu->context_name)); 1187} 1188 1189/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1190/* added 10/04/02 by kek: check for MAX_BINDINGS */ 1191int 1192snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1193{ 1194 va_list ap; 1195 const struct asn_oid *oid; 1196 u_int ret; 1197 1198 va_start(ap, pdu); 1199 1200 ret = pdu->nbindings; 1201 while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1202 if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1203 va_end(ap); 1204 return (-1); 1205 } 1206 pdu->bindings[pdu->nbindings].var = *oid; 1207 pdu->bindings[pdu->nbindings].syntax = 1208 va_arg(ap, enum snmp_syntax); 1209 pdu->nbindings++; 1210 } 1211 va_end(ap); 1212 return (ret); 1213} 1214 1215 1216static int32_t 1217snmp_next_reqid(struct snmp_client * c) 1218{ 1219 int32_t i; 1220 1221 i = c->next_reqid; 1222 if (c->next_reqid >= c->max_reqid) 1223 c->next_reqid = c->min_reqid; 1224 else 1225 c->next_reqid++; 1226 return (i); 1227} 1228 1229/* 1230 * Send request and return request id. 1231 */ 1232static int32_t 1233snmp_send_packet(struct snmp_pdu * pdu) 1234{ 1235 u_char *buf; 1236 struct asn_buf b; 1237 ssize_t ret; 1238 1239 if ((buf = calloc(1, snmp_client.txbuflen)) == NULL) { 1240 seterr(&snmp_client, "%s", strerror(errno)); 1241 return (-1); 1242 } 1243 1244 pdu->request_id = snmp_next_reqid(&snmp_client); 1245 1246 b.asn_ptr = buf; 1247 b.asn_len = snmp_client.txbuflen; 1248 if (snmp_pdu_encode(pdu, &b)) { 1249 seterr(&snmp_client, "%s", strerror(errno)); 1250 free(buf); 1251 return (-1); 1252 } 1253 1254 if (snmp_client.dump_pdus) 1255 snmp_pdu_dump(pdu); 1256 1257 if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1258 seterr(&snmp_client, "%s", strerror(errno)); 1259 free(buf); 1260 return (-1); 1261 } 1262 free(buf); 1263 1264 return (pdu->request_id); 1265} 1266 1267/* 1268 * to be called when a snmp request timed out 1269 */ 1270static void 1271snmp_timeout(void * listentry_ptr) 1272{ 1273 struct sent_pdu *listentry = listentry_ptr; 1274 1275#if 0 1276 warnx("snmp request %i timed out, attempt (%i/%i)", 1277 listentry->reqid, listentry->retrycount, snmp_client.retries); 1278#endif 1279 1280 listentry->retrycount++; 1281 if (listentry->retrycount > snmp_client.retries) { 1282 /* there is no answer at all */ 1283 LIST_REMOVE(listentry, entries); 1284 listentry->callback(listentry->pdu, NULL, listentry->arg); 1285 free(listentry); 1286 } else { 1287 /* try again */ 1288 /* new request with new request ID */ 1289 listentry->reqid = snmp_send_packet(listentry->pdu); 1290 listentry->timeout_id = 1291 snmp_client.timeout_start(&snmp_client.timeout, 1292 snmp_timeout, listentry); 1293 } 1294} 1295 1296int32_t 1297snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1298{ 1299 struct sent_pdu *listentry; 1300 int32_t id; 1301 1302 if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1303 seterr(&snmp_client, "%s", strerror(errno)); 1304 return (-1); 1305 } 1306 1307 /* here we really send */ 1308 if ((id = snmp_send_packet(pdu)) == -1) { 1309 free(listentry); 1310 return (-1); 1311 } 1312 1313 /* add entry to list of sent PDUs */ 1314 listentry->pdu = pdu; 1315 if (gettimeofday(&listentry->time, NULL) == -1) 1316 warn("gettimeofday() failed"); 1317 1318 listentry->reqid = pdu->request_id; 1319 listentry->callback = func; 1320 listentry->arg = arg; 1321 listentry->retrycount=1; 1322 listentry->timeout_id = 1323 snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1324 listentry); 1325 1326 LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1327 1328 return (id); 1329} 1330 1331/* 1332 * Receive an SNMP packet. 1333 * 1334 * tv controls how we wait for a packet: if tv is a NULL pointer, 1335 * the receive blocks forever, if tv points to a structure with all 1336 * members 0 the socket is polled, in all other cases tv specifies the 1337 * maximum time to wait for a packet. 1338 * 1339 * Return: 1340 * -1 on errors 1341 * 0 on timeout 1342 * +1 if packet received 1343 */ 1344static int 1345snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1346{ 1347 int dopoll, setpoll; 1348 int flags; 1349 int saved_errno; 1350 u_char *buf; 1351 int ret; 1352 struct asn_buf abuf; 1353 int32_t ip; 1354#ifdef bsdi 1355 int optlen; 1356#else 1357 socklen_t optlen; 1358#endif 1359 1360 if ((buf = calloc(1, snmp_client.rxbuflen)) == NULL) { 1361 seterr(&snmp_client, "%s", strerror(errno)); 1362 return (-1); 1363 } 1364 dopoll = setpoll = 0; 1365 flags = 0; 1366 if (tv != NULL) { 1367 /* poll or timeout */ 1368 if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1369 /* wait with timeout */ 1370 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1371 tv, sizeof(*tv)) == -1) { 1372 seterr(&snmp_client, "setsockopt: %s", 1373 strerror(errno)); 1374 free(buf); 1375 return (-1); 1376 } 1377 optlen = sizeof(*tv); 1378 if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1379 tv, &optlen) == -1) { 1380 seterr(&snmp_client, "getsockopt: %s", 1381 strerror(errno)); 1382 free(buf); 1383 return (-1); 1384 } 1385 /* at this point tv_sec and tv_usec may appear 1386 * as 0. This happens for timeouts lesser than 1387 * the clock granularity. The kernel rounds these to 1388 * 0 and this would result in a blocking receive. 1389 * Instead of an else we check tv_sec and tv_usec 1390 * again below and if this rounding happens, 1391 * switch to a polling receive. */ 1392 } 1393 if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1394 /* poll */ 1395 dopoll = 1; 1396 if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1397 seterr(&snmp_client, "fcntl: %s", 1398 strerror(errno)); 1399 free(buf); 1400 return (-1); 1401 } 1402 if (!(flags & O_NONBLOCK)) { 1403 setpoll = 1; 1404 flags |= O_NONBLOCK; 1405 if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1406 seterr(&snmp_client, "fcntl: %s", 1407 strerror(errno)); 1408 free(buf); 1409 return (-1); 1410 } 1411 } 1412 } 1413 } 1414 ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1415 saved_errno = errno; 1416 if (tv != NULL) { 1417 if (dopoll) { 1418 if (setpoll) { 1419 flags &= ~O_NONBLOCK; 1420 (void)fcntl(snmp_client.fd, F_SETFL, flags); 1421 } 1422 } else { 1423 tv->tv_sec = 0; 1424 tv->tv_usec = 0; 1425 (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1426 tv, sizeof(*tv)); 1427 } 1428 } 1429 if (ret == -1) { 1430 free(buf); 1431 if (errno == EAGAIN || errno == EWOULDBLOCK) 1432 return (0); 1433 seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1434 return (-1); 1435 } 1436 if (ret == 0) { 1437 /* this happens when we have a streaming socket and the 1438 * remote side has closed it */ 1439 free(buf); 1440 seterr(&snmp_client, "recv: socket closed by peer"); 1441 errno = EPIPE; 1442 return (-1); 1443 } 1444 1445 abuf.asn_ptr = buf; 1446 abuf.asn_len = ret; 1447 1448 memset(pdu, 0, sizeof(*pdu)); 1449 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1450 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1451 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1452 snmp_pdu_init_secparams(pdu); 1453 } 1454 1455 if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1456 seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1457 free(buf); 1458 return (-1); 1459 } 1460 1461 free(buf); 1462 if (snmp_client.dump_pdus) 1463 snmp_pdu_dump(pdu); 1464 1465 snmp_client.engine.engine_time = pdu->engine.engine_time; 1466 snmp_client.engine.engine_boots = pdu->engine.engine_boots; 1467 1468 return (+1); 1469} 1470 1471static int 1472snmp_deliver_packet(struct snmp_pdu * resp) 1473{ 1474 struct sent_pdu *listentry; 1475 1476 if (resp->type != SNMP_PDU_RESPONSE) { 1477 warn("ignoring snmp pdu %u", resp->type); 1478 return (-1); 1479 } 1480 1481 LIST_FOREACH(listentry, &sent_pdus, entries) 1482 if (listentry->reqid == resp->request_id) 1483 break; 1484 if (listentry == NULL) 1485 return (-1); 1486 1487 LIST_REMOVE(listentry, entries); 1488 listentry->callback(listentry->pdu, resp, listentry->arg); 1489 1490 snmp_client.timeout_stop(listentry->timeout_id); 1491 1492 free(listentry); 1493 return (0); 1494} 1495 1496int 1497snmp_receive(int blocking) 1498{ 1499 int ret; 1500 1501 struct timeval tv; 1502 struct snmp_pdu * resp; 1503 1504 memset(&tv, 0, sizeof(tv)); 1505 1506 resp = malloc(sizeof(struct snmp_pdu)); 1507 if (resp == NULL) { 1508 seterr(&snmp_client, "no memory for returning PDU"); 1509 return (-1) ; 1510 } 1511 1512 if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1513 free(resp); 1514 return (ret); 1515 } 1516 ret = snmp_deliver_packet(resp); 1517 snmp_pdu_free(resp); 1518 free(resp); 1519 return (ret); 1520} 1521 1522 1523/* 1524 * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1525 * unexpected error happened. +1 response is ok and is within the table 0 1526 * response is ok, but is behind the table or error is NOSUCHNAME. The req 1527 * should point to a template PDU which contains the base OIDs and the 1528 * syntaxes. This is really only useful to sweep non-sparse tables. 1529 */ 1530static int 1531ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1532{ 1533 u_int i; 1534 1535 if (resp->version != req->version) { 1536 warnx("SNMP GETNEXT: response has wrong version"); 1537 return (-1); 1538 } 1539 1540 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1541 return (0); 1542 1543 if (resp->error_status != SNMP_ERR_NOERROR) { 1544 warnx("SNMP GETNEXT: error %d", resp->error_status); 1545 return (-1); 1546 } 1547 if (resp->nbindings != req->nbindings) { 1548 warnx("SNMP GETNEXT: bad number of bindings in response"); 1549 return (-1); 1550 } 1551 for (i = 0; i < req->nbindings; i++) { 1552 if (!asn_is_suboid(&req->bindings[i].var, 1553 &resp->bindings[i].var)) { 1554 if (i != 0) 1555 warnx("SNMP GETNEXT: inconsistent table " 1556 "response"); 1557 return (0); 1558 } 1559 if (resp->version != SNMP_V1 && 1560 resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1561 return (0); 1562 1563 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1564 warnx("SNMP GETNEXT: bad syntax in response"); 1565 return (0); 1566 } 1567 } 1568 return (1); 1569} 1570 1571/* 1572 * Check a GET response. Here we have three possible outcomes: -1 an 1573 * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1574 * point to a template PDU which contains the OIDs and the syntaxes. This 1575 * is only useful for SNMPv1 or single object GETS. 1576 */ 1577static int 1578ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1579{ 1580 u_int i; 1581 1582 if (resp->version != req->version) { 1583 warnx("SNMP GET: response has wrong version"); 1584 return (-1); 1585 } 1586 1587 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1588 return (0); 1589 1590 if (resp->error_status != SNMP_ERR_NOERROR) { 1591 warnx("SNMP GET: error %d", resp->error_status); 1592 return (-1); 1593 } 1594 1595 if (resp->nbindings != req->nbindings) { 1596 warnx("SNMP GET: bad number of bindings in response"); 1597 return (-1); 1598 } 1599 for (i = 0; i < req->nbindings; i++) { 1600 if (asn_compare_oid(&req->bindings[i].var, 1601 &resp->bindings[i].var) != 0) { 1602 warnx("SNMP GET: bad OID in response"); 1603 return (-1); 1604 } 1605 if (snmp_client.version != SNMP_V1 && 1606 (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1607 resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1608 return (0); 1609 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1610 warnx("SNMP GET: bad syntax in response"); 1611 return (-1); 1612 } 1613 } 1614 return (1); 1615} 1616 1617/* 1618 * Check the response to a SET PDU. We check: - the error status must be 0 - 1619 * the number of bindings must be equal in response and request - the 1620 * syntaxes must be the same in response and request - the OIDs must be the 1621 * same in response and request 1622 */ 1623static int 1624ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1625{ 1626 u_int i; 1627 1628 if (resp->version != req->version) { 1629 warnx("SNMP SET: response has wrong version"); 1630 return (-1); 1631 } 1632 1633 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1634 warnx("SNMP SET: error %d", resp->error_status); 1635 return (0); 1636 } 1637 if (resp->error_status != SNMP_ERR_NOERROR) { 1638 warnx("SNMP SET: error %d", resp->error_status); 1639 return (-1); 1640 } 1641 1642 if (resp->nbindings != req->nbindings) { 1643 warnx("SNMP SET: bad number of bindings in response"); 1644 return (-1); 1645 } 1646 for (i = 0; i < req->nbindings; i++) { 1647 if (asn_compare_oid(&req->bindings[i].var, 1648 &resp->bindings[i].var) != 0) { 1649 warnx("SNMP SET: wrong OID in response to SET"); 1650 return (-1); 1651 } 1652 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1653 warnx("SNMP SET: bad syntax in response"); 1654 return (-1); 1655 } 1656 } 1657 return (1); 1658} 1659 1660/* 1661 * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1662 * 0=nosuchname or similar, -1=failure, -2=no response at all 1663 */ 1664int 1665snmp_pdu_check(const struct snmp_pdu *req, 1666 const struct snmp_pdu *resp) 1667{ 1668 if (resp == NULL) 1669 return (-2); 1670 1671 switch (req->type) { 1672 1673 case SNMP_PDU_GET: 1674 return (ok_get(req, resp)); 1675 1676 case SNMP_PDU_SET: 1677 return (ok_set(req, resp)); 1678 1679 case SNMP_PDU_GETNEXT: 1680 return (ok_getnext(req, resp)); 1681 1682 } 1683 errx(1, "%s: bad pdu type %i", __func__, req->type); 1684} 1685 1686int 1687snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1688{ 1689 struct timeval tv = snmp_client.timeout; 1690 struct timeval end; 1691 struct snmp_pdu pdu; 1692 int ret; 1693 int32_t reqid; 1694 u_int i; 1695 1696 /* 1697 * Make a copy of the request and replace the syntaxes by NULL 1698 * if this is a GET,GETNEXT or GETBULK. 1699 */ 1700 pdu = *req; 1701 if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1702 pdu.type == SNMP_PDU_GETBULK) { 1703 for (i = 0; i < pdu.nbindings; i++) 1704 pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1705 } 1706 1707 for (i = 0; i <= snmp_client.retries; i++) { 1708 (void)gettimeofday(&end, NULL); 1709 timeradd(&end, &snmp_client.timeout, &end); 1710 if ((reqid = snmp_send_packet(&pdu)) == -1) 1711 return (-1); 1712 for (;;) { 1713 (void)gettimeofday(&tv, NULL); 1714 if (timercmp(&end, &tv, <=)) 1715 break; 1716 timersub(&end, &tv, &tv); 1717 if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1718 /* timeout */ 1719 break; 1720 1721 if (ret > 0) { 1722 if (reqid == resp->request_id) 1723 return (0); 1724 /* not for us */ 1725 (void)snmp_deliver_packet(resp); 1726 } 1727 if (ret < 0 && errno == EPIPE) 1728 /* stream closed */ 1729 return (-1); 1730 } 1731 } 1732 errno = ETIMEDOUT; 1733 seterr(&snmp_client, "retry count exceeded"); 1734 return (-1); 1735} 1736 1737int 1738snmp_discover_engine(char *passwd) 1739{ 1740 char cname[SNMP_ADM_STR32_SIZ]; 1741 enum snmp_authentication cap; 1742 enum snmp_privacy cpp; 1743 struct snmp_pdu req, resp; 1744 1745 if (snmp_client.version != SNMP_V3) 1746 seterr(&snmp_client, "wrong version"); 1747 1748 strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); 1749 cap = snmp_client.user.auth_proto; 1750 cpp = snmp_client.user.priv_proto; 1751 1752 snmp_client.engine.engine_len = 0; 1753 snmp_client.engine.engine_boots = 0; 1754 snmp_client.engine.engine_time = 0; 1755 snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; 1756 snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; 1757 memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); 1758 1759 snmp_pdu_create(&req, SNMP_PDU_GET); 1760 1761 if (snmp_dialog(&req, &resp) == -1) 1762 return (-1); 1763 1764 if (resp.version != req.version) { 1765 seterr(&snmp_client, "wrong version"); 1766 return (-1); 1767 } 1768 1769 if (resp.error_status != SNMP_ERR_NOERROR) { 1770 seterr(&snmp_client, "Error %d in responce", resp.error_status); 1771 return (-1); 1772 } 1773 1774 snmp_client.engine.engine_len = resp.engine.engine_len; 1775 snmp_client.engine.max_msg_size = resp.engine.max_msg_size; 1776 memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, 1777 resp.engine.engine_len); 1778 1779 strlcpy(snmp_client.user.sec_name, cname, 1780 sizeof(snmp_client.user.sec_name)); 1781 snmp_client.user.auth_proto = cap; 1782 snmp_client.user.priv_proto = cpp; 1783 1784 if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) 1785 return (0); 1786 1787 if (passwd == NULL || 1788 snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || 1789 snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, 1790 snmp_client.engine.engine_len) != SNMP_CODE_OK) 1791 return (-1); 1792 1793 if (resp.engine.engine_boots != 0) 1794 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1795 1796 if (resp.engine.engine_time != 0) { 1797 snmp_client.engine.engine_time = resp.engine.engine_time; 1798 return (0); 1799 } 1800 1801 snmp_pdu_free(&req); 1802 1803 snmp_pdu_create(&req, SNMP_PDU_GET); 1804 req.engine.engine_boots = 0; 1805 req.engine.engine_time = 0; 1806 1807 if (snmp_dialog(&req, &resp) == -1) 1808 return (-1); 1809 1810 if (resp.version != req.version) { 1811 seterr(&snmp_client, "wrong version"); 1812 return (-1); 1813 } 1814 1815 if (resp.error_status != SNMP_ERR_NOERROR) { 1816 seterr(&snmp_client, "Error %d in responce", resp.error_status); 1817 return (-1); 1818 } 1819 1820 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1821 snmp_client.engine.engine_time = resp.engine.engine_time; 1822 1823 snmp_pdu_free(&req); 1824 snmp_pdu_free(&resp); 1825 1826 return (0); 1827} 1828 1829int 1830snmp_client_set_host(struct snmp_client *cl, const char *h) 1831{ 1832 char *np; 1833 1834 if (h == NULL) { 1835 if (cl->chost != NULL) 1836 free(cl->chost); 1837 cl->chost = NULL; 1838 } else { 1839 if ((np = malloc(strlen(h) + 1)) == NULL) 1840 return (-1); 1841 strcpy(np, h); 1842 if (cl->chost != NULL) 1843 free(cl->chost); 1844 cl->chost = np; 1845 } 1846 return (0); 1847} 1848 1849int 1850snmp_client_set_port(struct snmp_client *cl, const char *p) 1851{ 1852 char *np; 1853 1854 if (p == NULL) { 1855 if (cl->cport != NULL) 1856 free(cl->cport); 1857 cl->cport = NULL; 1858 } else { 1859 if ((np = malloc(strlen(p) + 1)) == NULL) 1860 return (-1); 1861 strcpy(np, p); 1862 if (cl->cport != NULL) 1863 free(cl->cport); 1864 cl->cport = np; 1865 } 1866 return (0); 1867} 1868 1869/* 1870 * parse a server specification 1871 * 1872 * [trans::][community@][server][:port] 1873 */ 1874int 1875snmp_parse_server(struct snmp_client *sc, const char *str) 1876{ 1877 const char *p, *s = str; 1878 1879 /* look for a double colon */ 1880 for (p = s; *p != '\0'; p++) { 1881 if (*p == '\\' && p[1] != '\0') { 1882 p++; 1883 continue; 1884 } 1885 if (*p == ':' && p[1] == ':') 1886 break; 1887 } 1888 if (*p != '\0') { 1889 if (p > s) { 1890 if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1891 sc->trans = SNMP_TRANS_UDP; 1892 else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1893 sc->trans = SNMP_TRANS_LOC_STREAM; 1894 else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1895 sc->trans = SNMP_TRANS_LOC_DGRAM; 1896 else { 1897 seterr(sc, "unknown SNMP transport '%.*s'", 1898 (int)(p - s), s); 1899 return (-1); 1900 } 1901 } 1902 s = p + 2; 1903 } 1904 1905 /* look for a @ */ 1906 for (p = s; *p != '\0'; p++) { 1907 if (*p == '\\' && p[1] != '\0') { 1908 p++; 1909 continue; 1910 } 1911 if (*p == '@') 1912 break; 1913 } 1914 1915 if (*p != '\0') { 1916 if (p - s > SNMP_COMMUNITY_MAXLEN) { 1917 seterr(sc, "community string too long"); 1918 return (-1); 1919 } 1920 strncpy(sc->read_community, s, p - s); 1921 sc->read_community[p - s] = '\0'; 1922 strncpy(sc->write_community, s, p - s); 1923 sc->write_community[p - s] = '\0'; 1924 s = p + 1; 1925 } 1926 1927 /* look for a colon */ 1928 for (p = s; *p != '\0'; p++) { 1929 if (*p == '\\' && p[1] != '\0') { 1930 p++; 1931 continue; 1932 } 1933 if (*p == ':') 1934 break; 1935 } 1936 1937 if (*p == ':') { 1938 if (p > s) { 1939 /* host:port */ 1940 free(sc->chost); 1941 if ((sc->chost = malloc(p - s + 1)) == NULL) { 1942 seterr(sc, "%s", strerror(errno)); 1943 return (-1); 1944 } 1945 strncpy(sc->chost, s, p - s); 1946 sc->chost[p - s] = '\0'; 1947 } 1948 /* port */ 1949 free(sc->cport); 1950 if ((sc->cport = strdup(p + 1)) == NULL) { 1951 seterr(sc, "%s", strerror(errno)); 1952 return (-1); 1953 } 1954 1955 } else if (p > s) { 1956 /* host */ 1957 free(sc->chost); 1958 if ((sc->chost = strdup(s)) == NULL) { 1959 seterr(sc, "%s", strerror(errno)); 1960 return (-1); 1961 } 1962 } 1963 return (0); 1964} 1965