1122394Sharti/* 2146525Sharti * Copyright (c) 2004-2005 3146525Sharti * Hartmut Brandt. 4146525Sharti * All rights reserved. 5122394Sharti * Copyright (c) 2001-2003 6122394Sharti * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 7122394Sharti * All rights reserved. 8122394Sharti * 9122394Sharti * Author: Harti Brandt <harti@freebsd.org> 10122394Sharti * Kendy Kutzner 11133211Sharti * 12133211Sharti * Redistribution and use in source and binary forms, with or without 13133211Sharti * modification, are permitted provided that the following conditions 14133211Sharti * are met: 15133211Sharti * 1. Redistributions of source code must retain the above copyright 16133211Sharti * notice, this list of conditions and the following disclaimer. 17122394Sharti * 2. Redistributions in binary form must reproduce the above copyright 18122394Sharti * notice, this list of conditions and the following disclaimer in the 19122394Sharti * documentation and/or other materials provided with the distribution. 20133211Sharti * 21133211Sharti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24133211Sharti * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27133211Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28133211Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29133211Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30133211Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31133211Sharti * SUCH DAMAGE. 32122394Sharti * 33156066Sharti * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 34122394Sharti * 35122394Sharti * Support functions for SNMP clients. 36122394Sharti */ 37122394Sharti#include <sys/types.h> 38133211Sharti#include <sys/time.h> 39122394Sharti#include <sys/queue.h> 40122394Sharti#include <sys/socket.h> 41122394Sharti#include <sys/un.h> 42122394Sharti#include <stdio.h> 43122394Sharti#include <stdlib.h> 44122394Sharti#include <stddef.h> 45122394Sharti#include <stdarg.h> 46122394Sharti#include <string.h> 47122394Sharti#include <errno.h> 48122394Sharti#include <unistd.h> 49122394Sharti#include <fcntl.h> 50122394Sharti#include <netdb.h> 51150920Sharti#ifdef HAVE_STDINT_H 52133211Sharti#include <stdint.h> 53150920Sharti#elif defined(HAVE_INTTYPES_H) 54150920Sharti#include <inttypes.h> 55150920Sharti#endif 56133211Sharti#include <limits.h> 57133211Sharti#ifdef HAVE_ERR_H 58122394Sharti#include <err.h> 59133211Sharti#endif 60122394Sharti 61133211Sharti#include "support.h" 62122394Sharti#include "asn1.h" 63122394Sharti#include "snmp.h" 64122394Sharti#include "snmpclient.h" 65122394Sharti#include "snmppriv.h" 66122394Sharti 67122394Sharti/* global context */ 68122394Shartistruct snmp_client snmp_client; 69122394Sharti 70122394Sharti/* List of all outstanding requests */ 71122394Shartistruct sent_pdu { 72122394Sharti int reqid; 73122394Sharti struct snmp_pdu *pdu; 74122394Sharti struct timeval time; 75122394Sharti u_int retrycount; 76122394Sharti snmp_send_cb_f callback; 77122394Sharti void *arg; 78122394Sharti void *timeout_id; 79122394Sharti LIST_ENTRY(sent_pdu) entries; 80122394Sharti}; 81122394ShartiLIST_HEAD(sent_pdu_list, sent_pdu); 82122394Sharti 83122394Shartistatic struct sent_pdu_list sent_pdus; 84122394Sharti 85122394Sharti/* 86122394Sharti * Prototype table entry. All C-structure produced by the table function must 87122394Sharti * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 88122394Sharti * are compatible with each other in the sense implied by ANSI-C. 89122394Sharti */ 90122394Shartistruct entry { 91122394Sharti TAILQ_ENTRY(entry) link; 92133211Sharti uint64_t found; 93122394Sharti}; 94122394ShartiTAILQ_HEAD(table, entry); 95122394Sharti 96122394Sharti/* 97122394Sharti * working list entry. This list is used to hold the Index part of the 98122394Sharti * table row's. The entry list and the work list parallel each other. 99122394Sharti */ 100122394Shartistruct work { 101122394Sharti TAILQ_ENTRY(work) link; 102122394Sharti struct asn_oid index; 103122394Sharti}; 104122394ShartiTAILQ_HEAD(worklist, work); 105122394Sharti 106122394Sharti/* 107122394Sharti * Table working data 108122394Sharti */ 109122394Shartistruct tabwork { 110122394Sharti const struct snmp_table *descr; 111122394Sharti struct table *table; 112122394Sharti struct worklist worklist; 113133211Sharti uint32_t last_change; 114122394Sharti int first; 115122394Sharti u_int iter; 116122394Sharti snmp_table_cb_f callback; 117122394Sharti void *arg; 118122394Sharti struct snmp_pdu pdu; 119122394Sharti}; 120122394Sharti 121122394Sharti/* 122122394Sharti * Set the error string 123122394Sharti */ 124122394Shartistatic void 125146525Shartiseterr(struct snmp_client *sc, const char *fmt, ...) 126122394Sharti{ 127122394Sharti va_list ap; 128122394Sharti 129122394Sharti va_start(ap, fmt); 130146525Sharti vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 131122394Sharti va_end(ap); 132122394Sharti} 133122394Sharti 134122394Sharti/* 135122394Sharti * Free the entire table and work list. If table is NULL only the worklist 136122394Sharti * is freed. 137122394Sharti */ 138122394Shartistatic void 139122394Shartitable_free(struct tabwork *work, int all) 140122394Sharti{ 141122394Sharti struct work *w; 142122394Sharti struct entry *e; 143122394Sharti const struct snmp_table_entry *d; 144122394Sharti u_int i; 145122394Sharti 146122394Sharti while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 147122394Sharti TAILQ_REMOVE(&work->worklist, w, link); 148122394Sharti free(w); 149122394Sharti } 150122394Sharti 151122394Sharti if (all == 0) 152122394Sharti return; 153122394Sharti 154122394Sharti while ((e = TAILQ_FIRST(work->table)) != NULL) { 155122394Sharti for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 156122394Sharti i++) { 157122394Sharti d = &work->descr->entries[i]; 158122394Sharti if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 159133211Sharti (e->found & ((uint64_t)1 << i))) 160122394Sharti free(*(void **)(void *) 161122394Sharti ((u_char *)e + d->offset)); 162122394Sharti } 163122394Sharti TAILQ_REMOVE(work->table, e, link); 164122394Sharti free(e); 165122394Sharti } 166122394Sharti} 167122394Sharti 168122394Sharti/* 169122394Sharti * Find the correct table entry for the given variable. If non exists, 170122394Sharti * create one. 171122394Sharti */ 172122394Shartistatic struct entry * 173122394Shartitable_find(struct tabwork *work, const struct asn_oid *var) 174122394Sharti{ 175122394Sharti struct entry *e, *e1; 176122394Sharti struct work *w, *w1; 177122394Sharti u_int i, p, j; 178122394Sharti size_t len; 179122394Sharti u_char *ptr; 180122394Sharti struct asn_oid oid; 181122394Sharti 182122394Sharti /* get index */ 183122394Sharti asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 184122394Sharti 185122394Sharti e = TAILQ_FIRST(work->table); 186122394Sharti w = TAILQ_FIRST(&work->worklist); 187122394Sharti while (e != NULL) { 188122394Sharti if (asn_compare_oid(&w->index, &oid) == 0) 189122394Sharti return (e); 190122394Sharti e = TAILQ_NEXT(e, link); 191122394Sharti w = TAILQ_NEXT(w, link); 192122394Sharti } 193122394Sharti 194122394Sharti /* Not found create new one */ 195122394Sharti if ((e = malloc(work->descr->entry_size)) == NULL) { 196146525Sharti seterr(&snmp_client, "no memory for table entry"); 197122394Sharti return (NULL); 198122394Sharti } 199122394Sharti if ((w = malloc(sizeof(*w))) == NULL) { 200146525Sharti seterr(&snmp_client, "no memory for table entry"); 201122394Sharti free(e); 202122394Sharti return (NULL); 203122394Sharti } 204122394Sharti w->index = oid; 205122394Sharti memset(e, 0, work->descr->entry_size); 206122394Sharti 207122394Sharti /* decode index */ 208122394Sharti p = work->descr->table.len + 2; 209122394Sharti for (i = 0; i < work->descr->index_size; i++) { 210122394Sharti switch (work->descr->entries[i].syntax) { 211122394Sharti 212122394Sharti case SNMP_SYNTAX_INTEGER: 213122394Sharti if (var->len < p + 1) { 214146525Sharti seterr(&snmp_client, "bad index: need integer"); 215122394Sharti goto err; 216122394Sharti } 217122394Sharti if (var->subs[p] > INT32_MAX) { 218146525Sharti seterr(&snmp_client, 219146525Sharti "bad index: integer too large"); 220122394Sharti goto err; 221122394Sharti } 222122394Sharti *(int32_t *)(void *)((u_char *)e + 223122394Sharti work->descr->entries[i].offset) = var->subs[p++]; 224122394Sharti break; 225122394Sharti 226122394Sharti case SNMP_SYNTAX_OCTETSTRING: 227122394Sharti if (var->len < p + 1) { 228146525Sharti seterr(&snmp_client, 229146525Sharti "bad index: need string length"); 230122394Sharti goto err; 231122394Sharti } 232122394Sharti len = var->subs[p++]; 233122394Sharti if (var->len < p + len) { 234146525Sharti seterr(&snmp_client, 235146525Sharti "bad index: string too short"); 236122394Sharti goto err; 237122394Sharti } 238122394Sharti if ((ptr = malloc(len + 1)) == NULL) { 239146525Sharti seterr(&snmp_client, 240146525Sharti "no memory for index string"); 241122394Sharti goto err; 242122394Sharti } 243122394Sharti for (j = 0; j < len; j++) { 244122394Sharti if (var->subs[p] > UCHAR_MAX) { 245146525Sharti seterr(&snmp_client, 246146525Sharti "bad index: char too large"); 247122394Sharti free(ptr); 248122394Sharti goto err; 249122394Sharti } 250122394Sharti ptr[j] = var->subs[p++]; 251122394Sharti } 252122394Sharti ptr[j] = '\0'; 253122394Sharti *(u_char **)(void *)((u_char *)e + 254122394Sharti work->descr->entries[i].offset) = ptr; 255122394Sharti *(size_t *)(void *)((u_char *)e + 256122394Sharti work->descr->entries[i].offset + sizeof(u_char *)) 257122394Sharti = len; 258122394Sharti break; 259122394Sharti 260122394Sharti case SNMP_SYNTAX_OID: 261122394Sharti if (var->len < p + 1) { 262146525Sharti seterr(&snmp_client, 263146525Sharti "bad index: need oid length"); 264122394Sharti goto err; 265122394Sharti } 266122394Sharti oid.len = var->subs[p++]; 267122394Sharti if (var->len < p + oid.len) { 268146525Sharti seterr(&snmp_client, 269146525Sharti "bad index: oid too short"); 270122394Sharti goto err; 271122394Sharti } 272122394Sharti for (j = 0; j < oid.len; j++) 273122394Sharti oid.subs[j] = var->subs[p++]; 274122394Sharti *(struct asn_oid *)(void *)((u_char *)e + 275122394Sharti work->descr->entries[i].offset) = oid; 276122394Sharti break; 277122394Sharti 278122394Sharti case SNMP_SYNTAX_IPADDRESS: 279122394Sharti if (var->len < p + 4) { 280146525Sharti seterr(&snmp_client, 281146525Sharti "bad index: need ip-address"); 282122394Sharti goto err; 283122394Sharti } 284122394Sharti for (j = 0; j < 4; j++) { 285122394Sharti if (var->subs[p] > 0xff) { 286146525Sharti seterr(&snmp_client, 287146525Sharti "bad index: ipaddress too large"); 288122394Sharti goto err; 289122394Sharti } 290122394Sharti ((u_char *)e + 291122394Sharti work->descr->entries[i].offset)[j] = 292122394Sharti var->subs[p++]; 293122394Sharti } 294122394Sharti break; 295122394Sharti 296122394Sharti case SNMP_SYNTAX_GAUGE: 297122394Sharti if (var->len < p + 1) { 298146525Sharti seterr(&snmp_client, 299146525Sharti "bad index: need unsigned"); 300122394Sharti goto err; 301122394Sharti } 302122394Sharti if (var->subs[p] > UINT32_MAX) { 303146525Sharti seterr(&snmp_client, 304146525Sharti "bad index: unsigned too large"); 305122394Sharti goto err; 306122394Sharti } 307133211Sharti *(uint32_t *)(void *)((u_char *)e + 308122394Sharti work->descr->entries[i].offset) = var->subs[p++]; 309122394Sharti break; 310122394Sharti 311122394Sharti case SNMP_SYNTAX_COUNTER: 312122394Sharti case SNMP_SYNTAX_TIMETICKS: 313122394Sharti case SNMP_SYNTAX_COUNTER64: 314122394Sharti case SNMP_SYNTAX_NULL: 315122394Sharti case SNMP_SYNTAX_NOSUCHOBJECT: 316122394Sharti case SNMP_SYNTAX_NOSUCHINSTANCE: 317122394Sharti case SNMP_SYNTAX_ENDOFMIBVIEW: 318122394Sharti abort(); 319122394Sharti } 320133211Sharti e->found |= (uint64_t)1 << i; 321122394Sharti } 322122394Sharti 323122394Sharti /* link into the correct place */ 324122394Sharti e1 = TAILQ_FIRST(work->table); 325122394Sharti w1 = TAILQ_FIRST(&work->worklist); 326122394Sharti while (e1 != NULL) { 327122394Sharti if (asn_compare_oid(&w1->index, &w->index) > 0) 328122394Sharti break; 329122394Sharti e1 = TAILQ_NEXT(e1, link); 330122394Sharti w1 = TAILQ_NEXT(w1, link); 331122394Sharti } 332122394Sharti if (e1 == NULL) { 333122394Sharti TAILQ_INSERT_TAIL(work->table, e, link); 334122394Sharti TAILQ_INSERT_TAIL(&work->worklist, w, link); 335122394Sharti } else { 336122394Sharti TAILQ_INSERT_BEFORE(e1, e, link); 337122394Sharti TAILQ_INSERT_BEFORE(w1, w, link); 338122394Sharti } 339122394Sharti 340122394Sharti return (e); 341122394Sharti 342122394Sharti err: 343122394Sharti /* 344122394Sharti * Error happend. Free all octet string index parts and the entry 345122394Sharti * itself. 346122394Sharti */ 347122394Sharti for (i = 0; i < work->descr->index_size; i++) { 348122394Sharti if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 349133211Sharti (e->found & ((uint64_t)1 << i))) 350122394Sharti free(*(void **)(void *)((u_char *)e + 351122394Sharti work->descr->entries[i].offset)); 352122394Sharti } 353122394Sharti free(e); 354122394Sharti free(w); 355122394Sharti return (NULL); 356122394Sharti} 357122394Sharti 358122394Sharti/* 359122394Sharti * Assign the value 360122394Sharti */ 361122394Shartistatic int 362122394Shartitable_value(const struct snmp_table *descr, struct entry *e, 363122394Sharti const struct snmp_value *b) 364122394Sharti{ 365122394Sharti u_int i; 366122394Sharti u_char *ptr; 367122394Sharti 368122394Sharti for (i = descr->index_size; 369122394Sharti descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 370122394Sharti if (descr->entries[i].subid == 371122394Sharti b->var.subs[descr->table.len + 1]) 372122394Sharti break; 373122394Sharti if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 374122394Sharti return (0); 375122394Sharti 376122394Sharti /* check syntax */ 377122394Sharti if (b->syntax != descr->entries[i].syntax) { 378146525Sharti seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 379122394Sharti descr->entries[i].syntax); 380122394Sharti return (-1); 381122394Sharti } 382122394Sharti 383122394Sharti switch (b->syntax) { 384122394Sharti 385122394Sharti case SNMP_SYNTAX_INTEGER: 386122394Sharti *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 387122394Sharti b->v.integer; 388122394Sharti break; 389122394Sharti 390122394Sharti case SNMP_SYNTAX_OCTETSTRING: 391122394Sharti if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 392146525Sharti seterr(&snmp_client, "no memory for string"); 393122394Sharti return (-1); 394122394Sharti } 395122394Sharti memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 396122394Sharti ptr[b->v.octetstring.len] = '\0'; 397122394Sharti *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 398122394Sharti ptr; 399122394Sharti *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 400122394Sharti sizeof(u_char *)) = b->v.octetstring.len; 401122394Sharti break; 402122394Sharti 403122394Sharti case SNMP_SYNTAX_OID: 404122394Sharti *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 405122394Sharti b->v.oid; 406122394Sharti break; 407122394Sharti 408122394Sharti case SNMP_SYNTAX_IPADDRESS: 409122394Sharti memcpy((u_char *)e + descr->entries[i].offset, 410122394Sharti b->v.ipaddress, 4); 411122394Sharti break; 412122394Sharti 413122394Sharti case SNMP_SYNTAX_COUNTER: 414122394Sharti case SNMP_SYNTAX_GAUGE: 415122394Sharti case SNMP_SYNTAX_TIMETICKS: 416133211Sharti *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 417122394Sharti b->v.uint32; 418122394Sharti break; 419122394Sharti 420122394Sharti case SNMP_SYNTAX_COUNTER64: 421133211Sharti *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 422122394Sharti b->v.counter64; 423122394Sharti break; 424122394Sharti 425122394Sharti case SNMP_SYNTAX_NULL: 426122394Sharti case SNMP_SYNTAX_NOSUCHOBJECT: 427122394Sharti case SNMP_SYNTAX_NOSUCHINSTANCE: 428122394Sharti case SNMP_SYNTAX_ENDOFMIBVIEW: 429122394Sharti abort(); 430122394Sharti } 431133211Sharti e->found |= (uint64_t)1 << i; 432122394Sharti 433122394Sharti return (0); 434122394Sharti} 435122394Sharti 436122394Sharti/* 437122394Sharti * Initialize the first PDU to send 438122394Sharti */ 439122394Shartistatic void 440122394Shartitable_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 441122394Sharti{ 442122394Sharti if (snmp_client.version == SNMP_V1) 443122394Sharti snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 444122394Sharti else { 445122394Sharti snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 446122394Sharti pdu->error_index = 10; 447122394Sharti } 448122394Sharti if (descr->last_change.len != 0) { 449122394Sharti pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 450122394Sharti pdu->bindings[pdu->nbindings].var = descr->last_change; 451122394Sharti pdu->nbindings++; 452122394Sharti if (pdu->version != SNMP_V1) 453122394Sharti pdu->error_status++; 454122394Sharti } 455122394Sharti pdu->bindings[pdu->nbindings].var = descr->table; 456122394Sharti pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 457122394Sharti pdu->nbindings++; 458122394Sharti} 459122394Sharti 460122394Sharti/* 461122394Sharti * Return code: 462122394Sharti * 0 - End Of Table 463122394Sharti * -1 - Error 464122394Sharti * -2 - Last change changed - again 465122394Sharti * +1 - ok, continue 466122394Sharti */ 467122394Shartistatic int 468122394Shartitable_check_response(struct tabwork *work, const struct snmp_pdu *resp) 469122394Sharti{ 470122394Sharti const struct snmp_value *b; 471122394Sharti struct entry *e; 472122394Sharti 473122394Sharti if (resp->error_status != SNMP_ERR_NOERROR) { 474122394Sharti if (snmp_client.version == SNMP_V1 && 475122394Sharti resp->error_status == SNMP_ERR_NOSUCHNAME && 476122394Sharti resp->error_index == 477122394Sharti (work->descr->last_change.len == 0) ? 1 : 2) 478122394Sharti /* EOT */ 479122394Sharti return (0); 480122394Sharti /* Error */ 481146525Sharti seterr(&snmp_client, "error fetching table: status=%d index=%d", 482122394Sharti resp->error_status, resp->error_index); 483122394Sharti return (-1); 484122394Sharti } 485122394Sharti 486122394Sharti for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 487122394Sharti if (work->descr->last_change.len != 0 && b == resp->bindings) { 488122394Sharti if (!asn_is_suboid(&work->descr->last_change, &b->var) || 489122394Sharti b->var.len != work->descr->last_change.len + 1 || 490122394Sharti b->var.subs[work->descr->last_change.len] != 0) { 491146525Sharti seterr(&snmp_client, 492146525Sharti "last_change: bad response"); 493122394Sharti return (-1); 494122394Sharti } 495122394Sharti if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 496146525Sharti seterr(&snmp_client, 497146525Sharti "last_change: bad syntax %u", b->syntax); 498122394Sharti return (-1); 499122394Sharti } 500122394Sharti if (work->first) { 501122394Sharti work->last_change = b->v.uint32; 502122394Sharti work->first = 0; 503122394Sharti 504122394Sharti } else if (work->last_change != b->v.uint32) { 505122394Sharti if (++work->iter >= work->descr->max_iter) { 506146525Sharti seterr(&snmp_client, 507146525Sharti "max iteration count exceeded"); 508122394Sharti return (-1); 509122394Sharti } 510122394Sharti table_free(work, 1); 511122394Sharti return (-2); 512122394Sharti } 513122394Sharti 514122394Sharti continue; 515122394Sharti } 516122394Sharti if (!asn_is_suboid(&work->descr->table, &b->var) || 517122394Sharti b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 518122394Sharti return (0); 519122394Sharti 520122394Sharti if ((e = table_find(work, &b->var)) == NULL) 521122394Sharti return (-1); 522122394Sharti if (table_value(work->descr, e, b)) 523122394Sharti return (-1); 524122394Sharti } 525122394Sharti return (+1); 526122394Sharti} 527122394Sharti 528122394Sharti/* 529122394Sharti * Check table consistency 530122394Sharti */ 531122394Shartistatic int 532122394Shartitable_check_cons(struct tabwork *work) 533122394Sharti{ 534122394Sharti struct entry *e; 535122394Sharti 536122394Sharti TAILQ_FOREACH(e, work->table, link) 537122394Sharti if ((e->found & work->descr->req_mask) != 538122394Sharti work->descr->req_mask) { 539122394Sharti if (work->descr->last_change.len == 0) { 540122394Sharti if (++work->iter >= work->descr->max_iter) { 541146525Sharti seterr(&snmp_client, 542146525Sharti "max iteration count exceeded"); 543122394Sharti return (-1); 544122394Sharti } 545122394Sharti return (-2); 546122394Sharti } 547146525Sharti seterr(&snmp_client, "inconsistency detected %llx %llx", 548122394Sharti e->found, work->descr->req_mask); 549122394Sharti return (-1); 550122394Sharti } 551122394Sharti return (0); 552122394Sharti} 553122394Sharti 554122394Sharti/* 555122394Sharti * Fetch a table. Returns 0 if ok, -1 on errors. 556150920Sharti * This is the synchronous variant. 557122394Sharti */ 558122394Shartiint 559122394Shartisnmp_table_fetch(const struct snmp_table *descr, void *list) 560122394Sharti{ 561122394Sharti struct snmp_pdu resp; 562122394Sharti struct tabwork work; 563122394Sharti int ret; 564122394Sharti 565122394Sharti work.descr = descr; 566122394Sharti work.table = (struct table *)list; 567122394Sharti work.iter = 0; 568122394Sharti TAILQ_INIT(work.table); 569122394Sharti TAILQ_INIT(&work.worklist); 570122394Sharti work.callback = NULL; 571122394Sharti work.arg = NULL; 572122394Sharti 573122394Sharti again: 574122394Sharti /* 575122394Sharti * We come to this label when the code detects that the table 576122394Sharti * has changed while fetching it. 577122394Sharti */ 578122394Sharti work.first = 1; 579122394Sharti work.last_change = 0; 580122394Sharti table_init_pdu(descr, &work.pdu); 581122394Sharti 582122394Sharti for (;;) { 583122394Sharti if (snmp_dialog(&work.pdu, &resp)) { 584122394Sharti table_free(&work, 1); 585122394Sharti return (-1); 586122394Sharti } 587122394Sharti if ((ret = table_check_response(&work, &resp)) == 0) { 588122394Sharti snmp_pdu_free(&resp); 589122394Sharti break; 590122394Sharti } 591122394Sharti if (ret == -1) { 592122394Sharti snmp_pdu_free(&resp); 593122394Sharti table_free(&work, 1); 594122394Sharti return (-1); 595122394Sharti } 596122394Sharti if (ret == -2) { 597122394Sharti snmp_pdu_free(&resp); 598122394Sharti goto again; 599122394Sharti } 600122394Sharti 601122394Sharti work.pdu.bindings[work.pdu.nbindings - 1].var = 602122394Sharti resp.bindings[resp.nbindings - 1].var; 603122394Sharti 604122394Sharti snmp_pdu_free(&resp); 605122394Sharti } 606122394Sharti 607122394Sharti if ((ret = table_check_cons(&work)) == -1) { 608122394Sharti table_free(&work, 1); 609122394Sharti return (-1); 610122394Sharti } 611122394Sharti if (ret == -2) { 612122394Sharti table_free(&work, 1); 613122394Sharti goto again; 614122394Sharti } 615122394Sharti /* 616122394Sharti * Free index list 617122394Sharti */ 618122394Sharti table_free(&work, 0); 619122394Sharti return (0); 620122394Sharti} 621122394Sharti 622122394Sharti/* 623122394Sharti * Callback for table 624122394Sharti */ 625122394Shartistatic void 626122394Shartitable_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 627122394Sharti{ 628122394Sharti struct tabwork *work = arg; 629122394Sharti int ret; 630122394Sharti 631122394Sharti if (resp == NULL) { 632122394Sharti /* timeout */ 633146525Sharti seterr(&snmp_client, "no response to fetch table request"); 634122394Sharti table_free(work, 1); 635122394Sharti work->callback(work->table, work->arg, -1); 636122394Sharti free(work); 637122394Sharti return; 638122394Sharti } 639122394Sharti 640122394Sharti if ((ret = table_check_response(work, resp)) == 0) { 641122394Sharti /* EOT */ 642122394Sharti snmp_pdu_free(resp); 643122394Sharti 644122394Sharti if ((ret = table_check_cons(work)) == -1) { 645122394Sharti /* error happend */ 646122394Sharti table_free(work, 1); 647122394Sharti work->callback(work->table, work->arg, -1); 648122394Sharti free(work); 649122394Sharti return; 650122394Sharti } 651122394Sharti if (ret == -2) { 652122394Sharti /* restart */ 653122394Sharti again: 654122394Sharti table_free(work, 1); 655122394Sharti work->first = 1; 656122394Sharti work->last_change = 0; 657122394Sharti table_init_pdu(work->descr, &work->pdu); 658122394Sharti if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 659122394Sharti work->callback(work->table, work->arg, -1); 660122394Sharti free(work); 661122394Sharti return; 662122394Sharti } 663122394Sharti return; 664122394Sharti } 665122394Sharti /* 666122394Sharti * Free index list 667122394Sharti */ 668122394Sharti table_free(work, 0); 669122394Sharti work->callback(work->table, work->arg, 0); 670122394Sharti free(work); 671122394Sharti return; 672122394Sharti } 673122394Sharti 674122394Sharti if (ret == -1) { 675122394Sharti /* error */ 676122394Sharti snmp_pdu_free(resp); 677122394Sharti table_free(work, 1); 678122394Sharti work->callback(work->table, work->arg, -1); 679122394Sharti free(work); 680122394Sharti return; 681122394Sharti } 682122394Sharti 683122394Sharti if (ret == -2) { 684122394Sharti /* again */ 685122394Sharti snmp_pdu_free(resp); 686122394Sharti goto again; 687122394Sharti } 688122394Sharti 689122394Sharti /* next part */ 690122394Sharti 691122394Sharti work->pdu.bindings[work->pdu.nbindings - 1].var = 692122394Sharti resp->bindings[resp->nbindings - 1].var; 693122394Sharti 694122394Sharti snmp_pdu_free(resp); 695122394Sharti 696122394Sharti if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 697122394Sharti table_free(work, 1); 698122394Sharti work->callback(work->table, work->arg, -1); 699122394Sharti free(work); 700122394Sharti return; 701122394Sharti } 702122394Sharti} 703122394Sharti 704122394Shartiint 705122394Shartisnmp_table_fetch_async(const struct snmp_table *descr, void *list, 706122394Sharti snmp_table_cb_f func, void *arg) 707122394Sharti{ 708122394Sharti struct tabwork *work; 709122394Sharti 710122394Sharti if ((work = malloc(sizeof(*work))) == NULL) { 711146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 712122394Sharti return (-1); 713122394Sharti } 714122394Sharti 715122394Sharti work->descr = descr; 716122394Sharti work->table = (struct table *)list; 717122394Sharti work->iter = 0; 718122394Sharti TAILQ_INIT(work->table); 719122394Sharti TAILQ_INIT(&work->worklist); 720122394Sharti 721122394Sharti work->callback = func; 722122394Sharti work->arg = arg; 723122394Sharti 724122394Sharti /* 725122394Sharti * Start by sending the first PDU 726122394Sharti */ 727122394Sharti work->first = 1; 728122394Sharti work->last_change = 0; 729122394Sharti table_init_pdu(descr, &work->pdu); 730122394Sharti 731122394Sharti if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) 732122394Sharti return (-1); 733122394Sharti return (0); 734122394Sharti} 735122394Sharti 736122394Sharti/* 737122394Sharti * Append an index to an oid 738122394Sharti */ 739122394Shartiint 740122394Shartisnmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 741122394Sharti{ 742122394Sharti va_list va; 743122394Sharti int size; 744122394Sharti char *nextptr; 745122394Sharti const u_char *str; 746122394Sharti size_t len; 747122394Sharti struct in_addr ina; 748122394Sharti int ret; 749122394Sharti 750122394Sharti va_start(va, fmt); 751122394Sharti 752122394Sharti size = 0; 753122394Sharti 754122394Sharti ret = 0; 755122394Sharti while (*fmt != '\0') { 756122394Sharti switch (*fmt++) { 757122394Sharti case 'i': 758122394Sharti /* just an integer more */ 759122394Sharti if (oid->len + 1 > ASN_MAXOIDLEN) { 760122394Sharti warnx("%s: OID too long for integer", __func__); 761122394Sharti ret = -1; 762122394Sharti break; 763122394Sharti } 764122394Sharti oid->subs[oid->len++] = va_arg(va, asn_subid_t); 765122394Sharti break; 766122394Sharti 767122394Sharti case 'a': 768122394Sharti /* append an IP address */ 769122394Sharti if (oid->len + 4 > ASN_MAXOIDLEN) { 770122394Sharti warnx("%s: OID too long for ip-addr", __func__); 771122394Sharti ret = -1; 772122394Sharti break; 773122394Sharti } 774122394Sharti ina = va_arg(va, struct in_addr); 775122394Sharti ina.s_addr = ntohl(ina.s_addr); 776122394Sharti oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 777122394Sharti oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 778122394Sharti oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 779122394Sharti oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 780122394Sharti break; 781122394Sharti 782122394Sharti case 's': 783122394Sharti /* append a null-terminated string, 784122394Sharti * length is computed */ 785122394Sharti str = (const u_char *)va_arg(va, const char *); 786122394Sharti len = strlen((const char *)str); 787122394Sharti if (oid->len + len + 1 > ASN_MAXOIDLEN) { 788122394Sharti warnx("%s: OID too long for string", __func__); 789122394Sharti ret = -1; 790122394Sharti break; 791122394Sharti } 792122394Sharti oid->subs[oid->len++] = len; 793122394Sharti while (len--) 794122394Sharti oid->subs[oid->len++] = *str++; 795122394Sharti break; 796122394Sharti 797122394Sharti case '(': 798122394Sharti /* the integer value between ( and ) is stored 799122394Sharti * in size */ 800122394Sharti size = strtol(fmt, &nextptr, 10); 801122394Sharti if (*nextptr != ')') 802122394Sharti abort(); 803122394Sharti fmt = ++nextptr; 804122394Sharti break; 805122394Sharti 806122394Sharti case 'b': 807122394Sharti /* append `size` characters */ 808122394Sharti str = (const u_char *)va_arg(va, const char *); 809122394Sharti if (oid->len + size > ASN_MAXOIDLEN) { 810122394Sharti warnx("%s: OID too long for string", __func__); 811122394Sharti ret = -1; 812122394Sharti break; 813122394Sharti } 814122394Sharti while (size--) 815122394Sharti oid->subs[oid->len++] = *str++; 816122394Sharti break; 817122394Sharti 818122394Sharti case 'c': 819122394Sharti /* get size and the octets from the arguments */ 820122394Sharti size = va_arg(va, size_t); 821122394Sharti str = va_arg(va, const u_char *); 822122394Sharti if (oid->len + size + 1 > ASN_MAXOIDLEN) { 823122394Sharti warnx("%s: OID too long for string", __func__); 824122394Sharti ret = -1; 825122394Sharti break; 826122394Sharti } 827122394Sharti oid->subs[oid->len++] = size; 828122394Sharti while (size--) 829122394Sharti oid->subs[oid->len++] = *str++; 830122394Sharti break; 831122394Sharti 832122394Sharti default: 833122394Sharti abort(); 834122394Sharti } 835122394Sharti } 836122394Sharti va_end(va); 837122394Sharti return (ret); 838122394Sharti} 839122394Sharti 840122394Sharti/* 841122394Sharti * Initialize a client structure 842122394Sharti */ 843122394Shartivoid 844122394Shartisnmp_client_init(struct snmp_client *c) 845122394Sharti{ 846122394Sharti memset(c, 0, sizeof(*c)); 847122394Sharti 848122394Sharti c->version = SNMP_V2c; 849124861Sharti c->trans = SNMP_TRANS_UDP; 850122394Sharti c->chost = NULL; 851122394Sharti c->cport = NULL; 852122394Sharti 853122394Sharti strcpy(c->read_community, "public"); 854122394Sharti strcpy(c->write_community, "private"); 855216294Ssyrinx 856216294Ssyrinx c->security_model = SNMP_SECMODEL_USM; 857216294Ssyrinx strcpy(c->cname, ""); 858122394Sharti 859122394Sharti c->timeout.tv_sec = 3; 860122394Sharti c->timeout.tv_usec = 0; 861122394Sharti c->retries = 3; 862122394Sharti c->dump_pdus = 0; 863122394Sharti c->txbuflen = c->rxbuflen = 10000; 864122394Sharti 865122394Sharti c->fd = -1; 866122394Sharti 867122394Sharti c->max_reqid = INT32_MAX; 868122394Sharti c->min_reqid = 0; 869122394Sharti c->next_reqid = 0; 870216294Ssyrinx 871216294Ssyrinx c->engine.max_msg_size = 1500; /* XXX */ 872122394Sharti} 873122394Sharti 874122394Sharti 875122394Sharti/* 876122394Sharti * Open UDP client socket 877122394Sharti */ 878122394Shartistatic int 879122394Shartiopen_client_udp(const char *host, const char *port) 880122394Sharti{ 881122394Sharti int error; 882122394Sharti char *ptr; 883122394Sharti struct addrinfo hints, *res0, *res; 884122394Sharti 885122394Sharti /* copy host- and portname */ 886122394Sharti if (snmp_client.chost == NULL) { 887122394Sharti if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 888122394Sharti == NULL) { 889146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 890122394Sharti return (-1); 891122394Sharti } 892122394Sharti strcpy(snmp_client.chost, DEFAULT_HOST); 893122394Sharti } 894122394Sharti if (host != NULL) { 895122394Sharti if ((ptr = malloc(1 + strlen(host))) == NULL) { 896146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 897122394Sharti return (-1); 898122394Sharti } 899122394Sharti free(snmp_client.chost); 900122394Sharti snmp_client.chost = ptr; 901122394Sharti strcpy(snmp_client.chost, host); 902122394Sharti } 903122394Sharti if (snmp_client.cport == NULL) { 904122394Sharti if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 905122394Sharti == NULL) { 906146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 907122394Sharti return (-1); 908122394Sharti } 909122394Sharti strcpy(snmp_client.cport, DEFAULT_PORT); 910122394Sharti } 911122394Sharti if (port != NULL) { 912122394Sharti if ((ptr = malloc(1 + strlen(port))) == NULL) { 913146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 914122394Sharti return (-1); 915122394Sharti } 916122394Sharti free(snmp_client.cport); 917122394Sharti snmp_client.cport = ptr; 918122394Sharti strcpy(snmp_client.cport, port); 919122394Sharti } 920122394Sharti 921122394Sharti /* open connection */ 922122394Sharti memset(&hints, 0, sizeof(hints)); 923122394Sharti hints.ai_flags = AI_CANONNAME; 924122394Sharti hints.ai_family = AF_INET; 925122394Sharti hints.ai_socktype = SOCK_DGRAM; 926122394Sharti hints.ai_protocol = 0; 927122394Sharti error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 928122394Sharti if (error != 0) { 929146525Sharti seterr(&snmp_client, "%s: %s", snmp_client.chost, 930146525Sharti gai_strerror(error)); 931122394Sharti return (-1); 932122394Sharti } 933122394Sharti res = res0; 934122394Sharti for (;;) { 935122394Sharti if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 936122394Sharti res->ai_protocol)) == -1) { 937122394Sharti if ((res = res->ai_next) == NULL) { 938146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 939122394Sharti freeaddrinfo(res0); 940122394Sharti return (-1); 941122394Sharti } 942122394Sharti } else if (connect(snmp_client.fd, res->ai_addr, 943122394Sharti res->ai_addrlen) == -1) { 944122394Sharti if ((res = res->ai_next) == NULL) { 945146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 946122394Sharti freeaddrinfo(res0); 947122394Sharti return (-1); 948122394Sharti } 949122394Sharti } else 950122394Sharti break; 951122394Sharti } 952122394Sharti freeaddrinfo(res0); 953122394Sharti return (0); 954122394Sharti} 955122394Sharti 956122394Shartistatic void 957122394Shartiremove_local(void) 958122394Sharti{ 959122394Sharti (void)remove(snmp_client.local_path); 960122394Sharti} 961122394Sharti 962122394Sharti/* 963122394Sharti * Open local socket 964122394Sharti */ 965122394Shartistatic int 966122394Shartiopen_client_local(const char *path) 967122394Sharti{ 968122394Sharti struct sockaddr_un sa; 969122394Sharti char *ptr; 970124861Sharti int stype; 971122394Sharti 972122394Sharti if (snmp_client.chost == NULL) { 973122394Sharti if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 974122394Sharti == NULL) { 975146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 976122394Sharti return (-1); 977122394Sharti } 978122394Sharti strcpy(snmp_client.chost, DEFAULT_LOCAL); 979122394Sharti } 980122394Sharti if (path != NULL) { 981122394Sharti if ((ptr = malloc(1 + strlen(path))) == NULL) { 982146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 983122394Sharti return (-1); 984122394Sharti } 985122394Sharti free(snmp_client.chost); 986122394Sharti snmp_client.chost = ptr; 987122394Sharti strcpy(snmp_client.chost, path); 988122394Sharti } 989122394Sharti 990124861Sharti if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 991124861Sharti stype = SOCK_DGRAM; 992124861Sharti else 993124861Sharti stype = SOCK_STREAM; 994124861Sharti 995124861Sharti if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 996146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 997122394Sharti return (-1); 998122394Sharti } 999122394Sharti 1000122394Sharti snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 1001122394Sharti "%s", SNMP_LOCAL_PATH); 1002122394Sharti 1003122394Sharti if (mktemp(snmp_client.local_path) == NULL) { 1004146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1005122394Sharti (void)close(snmp_client.fd); 1006122394Sharti snmp_client.fd = -1; 1007122394Sharti return (-1); 1008122394Sharti } 1009122394Sharti 1010122394Sharti sa.sun_family = AF_LOCAL; 1011122394Sharti sa.sun_len = sizeof(sa); 1012122394Sharti strcpy(sa.sun_path, snmp_client.local_path); 1013122394Sharti 1014122394Sharti if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1015146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1016122394Sharti (void)close(snmp_client.fd); 1017122394Sharti snmp_client.fd = -1; 1018122394Sharti (void)remove(snmp_client.local_path); 1019122394Sharti return (-1); 1020122394Sharti } 1021122394Sharti atexit(remove_local); 1022122394Sharti 1023122394Sharti sa.sun_family = AF_LOCAL; 1024122394Sharti sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1025122394Sharti strlen(snmp_client.chost); 1026122394Sharti strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1027122394Sharti sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1028122394Sharti 1029122394Sharti if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1030146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1031122394Sharti (void)close(snmp_client.fd); 1032122394Sharti snmp_client.fd = -1; 1033122394Sharti (void)remove(snmp_client.local_path); 1034122394Sharti return (-1); 1035122394Sharti } 1036122394Sharti return (0); 1037122394Sharti} 1038122394Sharti 1039122394Sharti/* 1040122394Sharti * SNMP_OPEN 1041122394Sharti */ 1042122394Shartiint 1043122394Shartisnmp_open(const char *host, const char *port, const char *readcomm, 1044122394Sharti const char *writecomm) 1045122394Sharti{ 1046122394Sharti struct timeval tout; 1047122394Sharti 1048122394Sharti /* still open ? */ 1049122394Sharti if (snmp_client.fd != -1) { 1050122394Sharti errno = EBUSY; 1051146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1052122394Sharti return (-1); 1053122394Sharti } 1054122394Sharti 1055122394Sharti /* copy community strings */ 1056122394Sharti if (readcomm != NULL) 1057122394Sharti strlcpy(snmp_client.read_community, readcomm, 1058122394Sharti sizeof(snmp_client.read_community)); 1059122394Sharti if (writecomm != NULL) 1060122394Sharti strlcpy(snmp_client.write_community, writecomm, 1061122394Sharti sizeof(snmp_client.write_community)); 1062122394Sharti 1063124861Sharti switch (snmp_client.trans) { 1064124861Sharti 1065124861Sharti case SNMP_TRANS_UDP: 1066122394Sharti if (open_client_udp(host, port)) 1067122394Sharti return (-1); 1068124861Sharti break; 1069124861Sharti 1070124861Sharti case SNMP_TRANS_LOC_DGRAM: 1071124861Sharti case SNMP_TRANS_LOC_STREAM: 1072122394Sharti if (open_client_local(host)) 1073122394Sharti return (-1); 1074124861Sharti break; 1075124861Sharti 1076124861Sharti default: 1077146525Sharti seterr(&snmp_client, "bad transport mapping"); 1078124861Sharti return (-1); 1079122394Sharti } 1080122394Sharti tout.tv_sec = 0; 1081122394Sharti tout.tv_usec = 0; 1082122394Sharti if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1083122394Sharti &tout, sizeof(struct timeval)) == -1) { 1084146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1085122394Sharti (void)close(snmp_client.fd); 1086122394Sharti snmp_client.fd = -1; 1087124861Sharti if (snmp_client.local_path[0] != '\0') 1088122394Sharti (void)remove(snmp_client.local_path); 1089122394Sharti return (-1); 1090122394Sharti } 1091122394Sharti 1092122394Sharti /* initialize list */ 1093122394Sharti LIST_INIT(&sent_pdus); 1094122394Sharti 1095122394Sharti return (0); 1096122394Sharti} 1097122394Sharti 1098122394Sharti 1099122394Sharti/* 1100122394Sharti * SNMP_CLOSE 1101122394Sharti * 1102122394Sharti * closes connection to snmp server 1103122394Sharti * - function cannot fail 1104122394Sharti * - clears connection 1105122394Sharti * - clears list of sent pdus 1106122394Sharti * 1107122394Sharti * input: 1108122394Sharti * void 1109122394Sharti * return: 1110122394Sharti * void 1111122394Sharti */ 1112122394Shartivoid 1113122394Shartisnmp_close(void) 1114122394Sharti{ 1115122394Sharti struct sent_pdu *p1; 1116122394Sharti 1117122394Sharti if (snmp_client.fd != -1) { 1118122394Sharti (void)close(snmp_client.fd); 1119122394Sharti snmp_client.fd = -1; 1120124861Sharti if (snmp_client.local_path[0] != '\0') 1121122394Sharti (void)remove(snmp_client.local_path); 1122122394Sharti } 1123122394Sharti while(!LIST_EMPTY(&sent_pdus)){ 1124122394Sharti p1 = LIST_FIRST(&sent_pdus); 1125122394Sharti if (p1->timeout_id != NULL) 1126122394Sharti snmp_client.timeout_stop(p1->timeout_id); 1127122394Sharti LIST_REMOVE(p1, entries); 1128122394Sharti free(p1); 1129122394Sharti } 1130122394Sharti free(snmp_client.chost); 1131122394Sharti free(snmp_client.cport); 1132122394Sharti} 1133122394Sharti 1134122394Sharti/* 1135122394Sharti * initialize a snmp_pdu structure 1136122394Sharti */ 1137122394Shartivoid 1138122394Shartisnmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1139122394Sharti{ 1140216294Ssyrinx memset(pdu, 0, sizeof(struct snmp_pdu)); 1141216294Ssyrinx 1142122394Sharti if (op == SNMP_PDU_SET) 1143122394Sharti strlcpy(pdu->community, snmp_client.write_community, 1144122394Sharti sizeof(pdu->community)); 1145122394Sharti else 1146122394Sharti strlcpy(pdu->community, snmp_client.read_community, 1147122394Sharti sizeof(pdu->community)); 1148122394Sharti 1149122394Sharti pdu->type = op; 1150122394Sharti pdu->version = snmp_client.version; 1151122394Sharti pdu->error_status = 0; 1152122394Sharti pdu->error_index = 0; 1153122394Sharti pdu->nbindings = 0; 1154216294Ssyrinx 1155216294Ssyrinx if (snmp_client.version != SNMP_V3) 1156216294Ssyrinx return; 1157216294Ssyrinx 1158216294Ssyrinx pdu->identifier = ++snmp_client.identifier; 1159216294Ssyrinx pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; 1160216294Ssyrinx pdu->flags = 0; 1161216294Ssyrinx pdu->security_model = snmp_client.security_model; 1162216294Ssyrinx 1163216594Ssyrinx if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1164216594Ssyrinx memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1165216594Ssyrinx memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1166216594Ssyrinx snmp_pdu_init_secparams(pdu); 1167216594Ssyrinx } else 1168216294Ssyrinx seterr(&snmp_client, "unknown security model"); 1169216294Ssyrinx 1170216294Ssyrinx if (snmp_client.clen > 0) { 1171216294Ssyrinx memcpy(pdu->context_engine, snmp_client.cengine, 1172216294Ssyrinx snmp_client.clen); 1173216294Ssyrinx pdu->context_engine_len = snmp_client.clen; 1174216294Ssyrinx } else { 1175216294Ssyrinx memcpy(pdu->context_engine, snmp_client.engine.engine_id, 1176216294Ssyrinx snmp_client.engine.engine_len); 1177216294Ssyrinx pdu->context_engine_len = snmp_client.engine.engine_len; 1178216294Ssyrinx } 1179216294Ssyrinx 1180216294Ssyrinx strlcpy(pdu->context_name, snmp_client.cname, 1181216294Ssyrinx sizeof(pdu->context_name)); 1182122394Sharti} 1183122394Sharti 1184122394Sharti/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1185122394Sharti/* added 10/04/02 by kek: check for MAX_BINDINGS */ 1186122394Shartiint 1187122394Shartisnmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1188122394Sharti{ 1189122394Sharti va_list ap; 1190122394Sharti const struct asn_oid *oid; 1191122394Sharti u_int ret; 1192122394Sharti 1193122394Sharti va_start(ap, pdu); 1194122394Sharti 1195122394Sharti ret = pdu->nbindings; 1196122394Sharti while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1197122394Sharti if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1198122394Sharti va_end(ap); 1199122394Sharti return (-1); 1200122394Sharti } 1201122394Sharti pdu->bindings[pdu->nbindings].var = *oid; 1202122394Sharti pdu->bindings[pdu->nbindings].syntax = 1203122394Sharti va_arg(ap, enum snmp_syntax); 1204122394Sharti pdu->nbindings++; 1205122394Sharti } 1206122394Sharti va_end(ap); 1207122394Sharti return (ret); 1208122394Sharti} 1209122394Sharti 1210122394Sharti 1211122394Shartistatic int32_t 1212122394Shartisnmp_next_reqid(struct snmp_client * c) 1213122394Sharti{ 1214122394Sharti int32_t i; 1215122394Sharti 1216122394Sharti i = c->next_reqid; 1217122394Sharti if (c->next_reqid >= c->max_reqid) 1218122394Sharti c->next_reqid = c->min_reqid; 1219122394Sharti else 1220122394Sharti c->next_reqid++; 1221122394Sharti return (i); 1222122394Sharti} 1223122394Sharti 1224122394Sharti/* 1225122394Sharti * Send request and return request id. 1226122394Sharti */ 1227122394Shartistatic int32_t 1228122394Shartisnmp_send_packet(struct snmp_pdu * pdu) 1229122394Sharti{ 1230122394Sharti u_char *buf; 1231122394Sharti struct asn_buf b; 1232122394Sharti ssize_t ret; 1233122394Sharti 1234122394Sharti if ((buf = malloc(snmp_client.txbuflen)) == NULL) { 1235146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1236122394Sharti return (-1); 1237122394Sharti } 1238122394Sharti 1239122394Sharti pdu->request_id = snmp_next_reqid(&snmp_client); 1240122394Sharti 1241122394Sharti b.asn_ptr = buf; 1242122394Sharti b.asn_len = snmp_client.txbuflen; 1243122394Sharti if (snmp_pdu_encode(pdu, &b)) { 1244146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1245122394Sharti free(buf); 1246122394Sharti return (-1); 1247122394Sharti } 1248122394Sharti 1249122394Sharti if (snmp_client.dump_pdus) 1250122394Sharti snmp_pdu_dump(pdu); 1251122394Sharti 1252122394Sharti if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1253146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1254122394Sharti free(buf); 1255122394Sharti return (-1); 1256122394Sharti } 1257122394Sharti free(buf); 1258122394Sharti 1259122394Sharti return pdu->request_id; 1260122394Sharti} 1261122394Sharti 1262122394Sharti/* 1263122394Sharti * to be called when a snmp request timed out 1264122394Sharti */ 1265122394Shartistatic void 1266122394Shartisnmp_timeout(void * listentry_ptr) 1267122394Sharti{ 1268122394Sharti struct sent_pdu *listentry = listentry_ptr; 1269122394Sharti 1270122394Sharti#if 0 1271122394Sharti warnx("snmp request %i timed out, attempt (%i/%i)", 1272122394Sharti listentry->reqid, listentry->retrycount, snmp_client.retries); 1273122394Sharti#endif 1274122394Sharti 1275122394Sharti listentry->retrycount++; 1276122394Sharti if (listentry->retrycount > snmp_client.retries) { 1277122394Sharti /* there is no answer at all */ 1278122394Sharti LIST_REMOVE(listentry, entries); 1279122394Sharti listentry->callback(listentry->pdu, NULL, listentry->arg); 1280122394Sharti free(listentry); 1281122394Sharti } else { 1282122394Sharti /* try again */ 1283122394Sharti /* new request with new request ID */ 1284122394Sharti listentry->reqid = snmp_send_packet(listentry->pdu); 1285122394Sharti listentry->timeout_id = 1286122394Sharti snmp_client.timeout_start(&snmp_client.timeout, 1287122394Sharti snmp_timeout, listentry); 1288122394Sharti } 1289122394Sharti} 1290122394Sharti 1291122394Shartiint32_t 1292122394Shartisnmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1293122394Sharti{ 1294122394Sharti struct sent_pdu *listentry; 1295122394Sharti int32_t id; 1296122394Sharti 1297122394Sharti if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1298146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1299122394Sharti return (-1); 1300122394Sharti } 1301122394Sharti 1302122394Sharti /* here we really send */ 1303122394Sharti if ((id = snmp_send_packet(pdu)) == -1) { 1304122394Sharti free(listentry); 1305122394Sharti return (-1); 1306122394Sharti } 1307122394Sharti 1308122394Sharti /* add entry to list of sent PDUs */ 1309122394Sharti listentry->pdu = pdu; 1310122394Sharti if (gettimeofday(&listentry->time, NULL) == -1) 1311122394Sharti warn("gettimeofday() failed"); 1312122394Sharti 1313122394Sharti listentry->reqid = pdu->request_id; 1314122394Sharti listentry->callback = func; 1315122394Sharti listentry->arg = arg; 1316122394Sharti listentry->retrycount=1; 1317122394Sharti listentry->timeout_id = 1318122394Sharti snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1319122394Sharti listentry); 1320122394Sharti 1321122394Sharti LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1322122394Sharti 1323122394Sharti return (id); 1324122394Sharti} 1325122394Sharti 1326122394Sharti/* 1327122394Sharti * Receive an SNMP packet. 1328122394Sharti * 1329122394Sharti * tv controls how we wait for a packet: if tv is a NULL pointer, 1330122394Sharti * the receive blocks forever, if tv points to a structure with all 1331122394Sharti * members 0 the socket is polled, in all other cases tv specifies the 1332122394Sharti * maximum time to wait for a packet. 1333122394Sharti * 1334122394Sharti * Return: 1335122394Sharti * -1 on errors 1336122394Sharti * 0 on timeout 1337122394Sharti * +1 if packet received 1338122394Sharti */ 1339122394Shartistatic int 1340122394Shartisnmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1341122394Sharti{ 1342122394Sharti int dopoll, setpoll; 1343122394Sharti int flags; 1344122394Sharti int saved_errno; 1345122394Sharti u_char *buf; 1346122394Sharti int ret; 1347122394Sharti struct asn_buf abuf; 1348122394Sharti int32_t ip; 1349133211Sharti#ifdef bsdi 1350133211Sharti int optlen; 1351133211Sharti#else 1352122394Sharti socklen_t optlen; 1353133211Sharti#endif 1354122394Sharti 1355122394Sharti if ((buf = malloc(snmp_client.rxbuflen)) == NULL) { 1356146525Sharti seterr(&snmp_client, "%s", strerror(errno)); 1357122394Sharti return (-1); 1358122394Sharti } 1359122394Sharti dopoll = setpoll = 0; 1360122394Sharti flags = 0; 1361122394Sharti if (tv != NULL) { 1362122394Sharti /* poll or timeout */ 1363122394Sharti if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1364122394Sharti /* wait with timeout */ 1365122394Sharti if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1366122394Sharti tv, sizeof(*tv)) == -1) { 1367146525Sharti seterr(&snmp_client, "setsockopt: %s", 1368146525Sharti strerror(errno)); 1369122394Sharti free(buf); 1370122394Sharti return (-1); 1371122394Sharti } 1372122394Sharti optlen = sizeof(*tv); 1373122394Sharti if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1374122394Sharti tv, &optlen) == -1) { 1375146525Sharti seterr(&snmp_client, "getsockopt: %s", 1376146525Sharti strerror(errno)); 1377122394Sharti free(buf); 1378122394Sharti return (-1); 1379122394Sharti } 1380122394Sharti /* at this point tv_sec and tv_usec may appear 1381122394Sharti * as 0. This happens for timeouts lesser than 1382122394Sharti * the clock granularity. The kernel rounds these to 1383122394Sharti * 0 and this would result in a blocking receive. 1384122394Sharti * Instead of an else we check tv_sec and tv_usec 1385122394Sharti * again below and if this rounding happens, 1386122394Sharti * switch to a polling receive. */ 1387122394Sharti } 1388122394Sharti if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1389122394Sharti /* poll */ 1390122394Sharti dopoll = 1; 1391122394Sharti if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1392146525Sharti seterr(&snmp_client, "fcntl: %s", 1393146525Sharti strerror(errno)); 1394122394Sharti free(buf); 1395122394Sharti return (-1); 1396122394Sharti } 1397122394Sharti if (!(flags & O_NONBLOCK)) { 1398122394Sharti setpoll = 1; 1399122394Sharti flags |= O_NONBLOCK; 1400122394Sharti if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1401146525Sharti seterr(&snmp_client, "fcntl: %s", 1402146525Sharti strerror(errno)); 1403122394Sharti free(buf); 1404122394Sharti return (-1); 1405122394Sharti } 1406122394Sharti } 1407122394Sharti } 1408122394Sharti } 1409122394Sharti ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1410122394Sharti saved_errno = errno; 1411122394Sharti if (tv != NULL) { 1412122394Sharti if (dopoll) { 1413122394Sharti if (setpoll) { 1414122394Sharti flags &= ~O_NONBLOCK; 1415122394Sharti (void)fcntl(snmp_client.fd, F_SETFL, flags); 1416122394Sharti } 1417122394Sharti } else { 1418122394Sharti tv->tv_sec = 0; 1419122394Sharti tv->tv_usec = 0; 1420122394Sharti (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1421122394Sharti tv, sizeof(*tv)); 1422122394Sharti } 1423122394Sharti } 1424122394Sharti if (ret == -1) { 1425122394Sharti free(buf); 1426122394Sharti if (errno == EAGAIN || errno == EWOULDBLOCK) 1427122394Sharti return (0); 1428146525Sharti seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1429122394Sharti return (-1); 1430122394Sharti } 1431133211Sharti if (ret == 0) { 1432133211Sharti /* this happens when we have a streaming socket and the 1433133211Sharti * remote side has closed it */ 1434133211Sharti free(buf); 1435146525Sharti seterr(&snmp_client, "recv: socket closed by peer"); 1436133211Sharti errno = EPIPE; 1437133211Sharti return (-1); 1438133211Sharti } 1439122394Sharti 1440122394Sharti abuf.asn_ptr = buf; 1441122394Sharti abuf.asn_len = ret; 1442122394Sharti 1443216294Ssyrinx memset(pdu, 0, sizeof(*pdu)); 1444216594Ssyrinx if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1445216594Ssyrinx memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1446216594Ssyrinx memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1447216594Ssyrinx snmp_pdu_init_secparams(pdu); 1448216594Ssyrinx } 1449216294Ssyrinx 1450122394Sharti if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1451146525Sharti seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1452122394Sharti free(buf); 1453122394Sharti return (-1); 1454122394Sharti } 1455216294Ssyrinx 1456122394Sharti free(buf); 1457122394Sharti if (snmp_client.dump_pdus) 1458122394Sharti snmp_pdu_dump(pdu); 1459122394Sharti 1460216294Ssyrinx snmp_client.engine.engine_time = pdu->engine.engine_time; 1461216294Ssyrinx snmp_client.engine.engine_boots = pdu->engine.engine_boots; 1462216294Ssyrinx 1463122394Sharti return (+1); 1464122394Sharti} 1465122394Sharti 1466122394Shartistatic int 1467122394Shartisnmp_deliver_packet(struct snmp_pdu * resp) 1468122394Sharti{ 1469122394Sharti struct sent_pdu *listentry; 1470122394Sharti 1471122394Sharti if (resp->type != SNMP_PDU_RESPONSE) { 1472122394Sharti warn("ignoring snmp pdu %u", resp->type); 1473122394Sharti return (-1); 1474122394Sharti } 1475122394Sharti 1476122394Sharti LIST_FOREACH(listentry, &sent_pdus, entries) 1477122394Sharti if (listentry->reqid == resp->request_id) 1478122394Sharti break; 1479122394Sharti if (listentry == NULL) 1480122394Sharti return (-1); 1481122394Sharti 1482122394Sharti LIST_REMOVE(listentry, entries); 1483122394Sharti listentry->callback(listentry->pdu, resp, listentry->arg); 1484122394Sharti 1485122394Sharti snmp_client.timeout_stop(listentry->timeout_id); 1486122394Sharti 1487122394Sharti free(listentry); 1488122394Sharti return (0); 1489122394Sharti} 1490122394Sharti 1491122394Shartiint 1492122394Shartisnmp_receive(int blocking) 1493122394Sharti{ 1494122394Sharti int ret; 1495122394Sharti 1496122394Sharti struct timeval tv; 1497122394Sharti struct snmp_pdu * resp; 1498122394Sharti 1499122394Sharti memset(&tv, 0, sizeof(tv)); 1500122394Sharti 1501122394Sharti resp = malloc(sizeof(struct snmp_pdu)); 1502122394Sharti if (resp == NULL) { 1503146525Sharti seterr(&snmp_client, "no memory for returning PDU"); 1504122394Sharti return (-1) ; 1505122394Sharti } 1506122394Sharti 1507122394Sharti if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1508122394Sharti free(resp); 1509122394Sharti return (ret); 1510122394Sharti } 1511122394Sharti ret = snmp_deliver_packet(resp); 1512122394Sharti snmp_pdu_free(resp); 1513122394Sharti free(resp); 1514122394Sharti return (ret); 1515122394Sharti} 1516122394Sharti 1517122394Sharti 1518122394Sharti/* 1519122394Sharti * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1520122394Sharti * unexpected error happened. +1 response is ok and is within the table 0 1521122394Sharti * response is ok, but is behind the table or error is NOSUCHNAME. The req 1522122394Sharti * should point to a template PDU which contains the base OIDs and the 1523122394Sharti * syntaxes. This is really only useful to sweep non-sparse tables. 1524122394Sharti */ 1525122394Shartistatic int 1526122394Shartiok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1527122394Sharti{ 1528122394Sharti u_int i; 1529122394Sharti 1530122394Sharti if (resp->version != req->version) { 1531122394Sharti warnx("SNMP GETNEXT: response has wrong version"); 1532122394Sharti return (-1); 1533122394Sharti } 1534122394Sharti 1535122394Sharti if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1536122394Sharti return (0); 1537122394Sharti 1538122394Sharti if (resp->error_status != SNMP_ERR_NOERROR) { 1539122394Sharti warnx("SNMP GETNEXT: error %d", resp->error_status); 1540122394Sharti return (-1); 1541122394Sharti } 1542122394Sharti if (resp->nbindings != req->nbindings) { 1543122394Sharti warnx("SNMP GETNEXT: bad number of bindings in response"); 1544122394Sharti return (-1); 1545122394Sharti } 1546122394Sharti for (i = 0; i < req->nbindings; i++) { 1547122394Sharti if (!asn_is_suboid(&req->bindings[i].var, 1548122394Sharti &resp->bindings[i].var)) { 1549122394Sharti if (i != 0) 1550122394Sharti warnx("SNMP GETNEXT: inconsistent table " 1551122394Sharti "response"); 1552122394Sharti return (0); 1553122394Sharti } 1554122394Sharti if (resp->version != SNMP_V1 && 1555122394Sharti resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1556122394Sharti return (0); 1557122394Sharti 1558122394Sharti if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1559122394Sharti warnx("SNMP GETNEXT: bad syntax in response"); 1560122394Sharti return (0); 1561122394Sharti } 1562122394Sharti } 1563122394Sharti return (1); 1564122394Sharti} 1565122394Sharti 1566122394Sharti/* 1567122394Sharti * Check a GET response. Here we have three possible outcomes: -1 an 1568122394Sharti * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1569122394Sharti * point to a template PDU which contains the OIDs and the syntaxes. This 1570122394Sharti * is only useful for SNMPv1 or single object GETS. 1571122394Sharti */ 1572122394Shartistatic int 1573122394Shartiok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1574122394Sharti{ 1575122394Sharti u_int i; 1576122394Sharti 1577122394Sharti if (resp->version != req->version) { 1578122394Sharti warnx("SNMP GET: response has wrong version"); 1579122394Sharti return (-1); 1580122394Sharti } 1581122394Sharti 1582122394Sharti if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1583122394Sharti return (0); 1584122394Sharti 1585122394Sharti if (resp->error_status != SNMP_ERR_NOERROR) { 1586122394Sharti warnx("SNMP GET: error %d", resp->error_status); 1587122394Sharti return (-1); 1588122394Sharti } 1589122394Sharti 1590122394Sharti if (resp->nbindings != req->nbindings) { 1591122394Sharti warnx("SNMP GET: bad number of bindings in response"); 1592122394Sharti return (-1); 1593122394Sharti } 1594122394Sharti for (i = 0; i < req->nbindings; i++) { 1595122394Sharti if (asn_compare_oid(&req->bindings[i].var, 1596122394Sharti &resp->bindings[i].var) != 0) { 1597122394Sharti warnx("SNMP GET: bad OID in response"); 1598122394Sharti return (-1); 1599122394Sharti } 1600122394Sharti if (snmp_client.version != SNMP_V1 && 1601122394Sharti (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1602122394Sharti resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1603122394Sharti return (0); 1604122394Sharti if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1605122394Sharti warnx("SNMP GET: bad syntax in response"); 1606122394Sharti return (-1); 1607122394Sharti } 1608122394Sharti } 1609122394Sharti return (1); 1610122394Sharti} 1611122394Sharti 1612122394Sharti/* 1613150920Sharti * Check the response to a SET PDU. We check: - the error status must be 0 - 1614122394Sharti * the number of bindings must be equal in response and request - the 1615122394Sharti * syntaxes must be the same in response and request - the OIDs must be the 1616122394Sharti * same in response and request 1617122394Sharti */ 1618122394Shartistatic int 1619122394Shartiok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1620122394Sharti{ 1621122394Sharti u_int i; 1622122394Sharti 1623122394Sharti if (resp->version != req->version) { 1624122394Sharti warnx("SNMP SET: response has wrong version"); 1625122394Sharti return (-1); 1626122394Sharti } 1627122394Sharti 1628122394Sharti if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1629122394Sharti warnx("SNMP SET: error %d", resp->error_status); 1630122394Sharti return (0); 1631122394Sharti } 1632122394Sharti if (resp->error_status != SNMP_ERR_NOERROR) { 1633122394Sharti warnx("SNMP SET: error %d", resp->error_status); 1634122394Sharti return (-1); 1635122394Sharti } 1636122394Sharti 1637122394Sharti if (resp->nbindings != req->nbindings) { 1638122394Sharti warnx("SNMP SET: bad number of bindings in response"); 1639122394Sharti return (-1); 1640122394Sharti } 1641122394Sharti for (i = 0; i < req->nbindings; i++) { 1642122394Sharti if (asn_compare_oid(&req->bindings[i].var, 1643122394Sharti &resp->bindings[i].var) != 0) { 1644122394Sharti warnx("SNMP SET: wrong OID in response to SET"); 1645122394Sharti return (-1); 1646122394Sharti } 1647122394Sharti if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1648122394Sharti warnx("SNMP SET: bad syntax in response"); 1649122394Sharti return (-1); 1650122394Sharti } 1651122394Sharti } 1652122394Sharti return (1); 1653122394Sharti} 1654122394Sharti 1655122394Sharti/* 1656122394Sharti * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1657122394Sharti * 0=nosuchname or similar, -1=failure, -2=no response at all 1658122394Sharti */ 1659122394Shartiint 1660122394Shartisnmp_pdu_check(const struct snmp_pdu *req, 1661122394Sharti const struct snmp_pdu *resp) 1662122394Sharti{ 1663122394Sharti if (resp == NULL) 1664122394Sharti return (-2); 1665122394Sharti 1666122394Sharti switch (req->type) { 1667122394Sharti 1668122394Sharti case SNMP_PDU_GET: 1669122394Sharti return (ok_get(req, resp)); 1670122394Sharti 1671122394Sharti case SNMP_PDU_SET: 1672122394Sharti return (ok_set(req, resp)); 1673122394Sharti 1674122394Sharti case SNMP_PDU_GETNEXT: 1675122394Sharti return (ok_getnext(req, resp)); 1676122394Sharti 1677122394Sharti } 1678122394Sharti errx(1, "%s: bad pdu type %i", __func__, req->type); 1679122394Sharti} 1680122394Sharti 1681122394Shartiint 1682122394Shartisnmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1683122394Sharti{ 1684122394Sharti u_int i; 1685122394Sharti int32_t reqid; 1686122394Sharti int ret; 1687122394Sharti struct timeval tv = snmp_client.timeout; 1688122394Sharti struct timeval end; 1689146525Sharti struct snmp_pdu pdu; 1690122394Sharti 1691146525Sharti /* 1692146525Sharti * Make a copy of the request and replace the syntaxes by NULL 1693146525Sharti * if this is a GET,GETNEXT or GETBULK. 1694146525Sharti */ 1695146525Sharti pdu = *req; 1696146525Sharti if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1697146525Sharti pdu.type == SNMP_PDU_GETBULK) { 1698146525Sharti for (i = 0; i < pdu.nbindings; i++) 1699146525Sharti pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1700146525Sharti } 1701146525Sharti 1702122394Sharti for (i = 0; i <= snmp_client.retries; i++) { 1703122394Sharti (void)gettimeofday(&end, NULL); 1704122394Sharti timeradd(&end, &snmp_client.timeout, &end); 1705146525Sharti if ((reqid = snmp_send_packet(&pdu)) == -1) 1706122394Sharti return (-1); 1707122394Sharti for (;;) { 1708122394Sharti (void)gettimeofday(&tv, NULL); 1709122394Sharti if (timercmp(&end, &tv, <=)) 1710122394Sharti break; 1711122394Sharti timersub(&end, &tv, &tv); 1712122394Sharti if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1713122394Sharti /* timeout */ 1714122394Sharti break; 1715122394Sharti 1716122394Sharti if (ret > 0) { 1717122394Sharti if (reqid == resp->request_id) 1718122394Sharti return (0); 1719122394Sharti /* not for us */ 1720122394Sharti (void)snmp_deliver_packet(resp); 1721122394Sharti } 1722133211Sharti if (ret < 0 && errno == EPIPE) 1723133211Sharti /* stream closed */ 1724133211Sharti return (-1); 1725122394Sharti } 1726122394Sharti } 1727133211Sharti errno = ETIMEDOUT; 1728146525Sharti seterr(&snmp_client, "retry count exceeded"); 1729122394Sharti return (-1); 1730122394Sharti} 1731122394Sharti 1732122394Shartiint 1733216294Ssyrinxsnmp_discover_engine(char *passwd) 1734216294Ssyrinx{ 1735216294Ssyrinx char cname[SNMP_ADM_STR32_SIZ]; 1736216294Ssyrinx enum snmp_authentication cap; 1737216294Ssyrinx enum snmp_privacy cpp; 1738216294Ssyrinx struct snmp_pdu req, resp; 1739216294Ssyrinx 1740216294Ssyrinx if (snmp_client.version != SNMP_V3) 1741216294Ssyrinx seterr(&snmp_client, "wrong version"); 1742216294Ssyrinx 1743216294Ssyrinx strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); 1744216294Ssyrinx cap = snmp_client.user.auth_proto; 1745216294Ssyrinx cpp = snmp_client.user.priv_proto; 1746216294Ssyrinx 1747216294Ssyrinx snmp_client.engine.engine_len = 0; 1748216294Ssyrinx snmp_client.engine.engine_boots = 0; 1749216294Ssyrinx snmp_client.engine.engine_time = 0; 1750216294Ssyrinx snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; 1751216294Ssyrinx snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; 1752216294Ssyrinx memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); 1753216294Ssyrinx 1754216294Ssyrinx snmp_pdu_create(&req, SNMP_PDU_GET); 1755216294Ssyrinx 1756216294Ssyrinx if (snmp_dialog(&req, &resp) == -1) 1757216294Ssyrinx return (-1); 1758216294Ssyrinx 1759216294Ssyrinx if (resp.version != req.version) { 1760216294Ssyrinx seterr(&snmp_client, "wrong version"); 1761216294Ssyrinx return (-1); 1762216294Ssyrinx } 1763216294Ssyrinx 1764216294Ssyrinx if (resp.error_status != SNMP_ERR_NOERROR) { 1765216294Ssyrinx seterr(&snmp_client, "Error %d in responce", resp.error_status); 1766216294Ssyrinx return (-1); 1767216294Ssyrinx } 1768216294Ssyrinx 1769216294Ssyrinx snmp_client.engine.engine_len = resp.engine.engine_len; 1770216294Ssyrinx snmp_client.engine.max_msg_size = resp.engine.max_msg_size; 1771216294Ssyrinx memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, 1772216294Ssyrinx resp.engine.engine_len); 1773216294Ssyrinx 1774216294Ssyrinx strlcpy(snmp_client.user.sec_name, cname, 1775216294Ssyrinx sizeof(snmp_client.user.sec_name)); 1776216294Ssyrinx snmp_client.user.auth_proto = cap; 1777216294Ssyrinx snmp_client.user.priv_proto = cpp; 1778216294Ssyrinx 1779216294Ssyrinx if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) 1780216294Ssyrinx return (0); 1781216294Ssyrinx 1782216294Ssyrinx if (passwd == NULL || 1783216294Ssyrinx snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || 1784216294Ssyrinx snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, 1785216294Ssyrinx snmp_client.engine.engine_len) != SNMP_CODE_OK) 1786216294Ssyrinx return (-1); 1787216294Ssyrinx 1788216294Ssyrinx if (resp.engine.engine_boots != 0) 1789216294Ssyrinx snmp_client.engine.engine_boots = resp.engine.engine_boots; 1790216294Ssyrinx 1791216294Ssyrinx if (resp.engine.engine_time != 0) { 1792216294Ssyrinx snmp_client.engine.engine_time = resp.engine.engine_time; 1793216294Ssyrinx return (0); 1794216294Ssyrinx } 1795216294Ssyrinx 1796216294Ssyrinx snmp_pdu_create(&req, SNMP_PDU_GET); 1797216294Ssyrinx req.engine.engine_boots = 0; 1798216294Ssyrinx req.engine.engine_time = 0; 1799216294Ssyrinx 1800216294Ssyrinx if (snmp_dialog(&req, &resp) == -1) 1801216294Ssyrinx return (-1); 1802216294Ssyrinx 1803216294Ssyrinx if (resp.version != req.version) { 1804216294Ssyrinx seterr(&snmp_client, "wrong version"); 1805216294Ssyrinx return (-1); 1806216294Ssyrinx } 1807216294Ssyrinx 1808216294Ssyrinx if (resp.error_status != SNMP_ERR_NOERROR) { 1809216294Ssyrinx seterr(&snmp_client, "Error %d in responce", resp.error_status); 1810216294Ssyrinx return (-1); 1811216294Ssyrinx } 1812216294Ssyrinx 1813216294Ssyrinx snmp_client.engine.engine_boots = resp.engine.engine_boots; 1814216294Ssyrinx snmp_client.engine.engine_time = resp.engine.engine_time; 1815216294Ssyrinx 1816216294Ssyrinx return (0); 1817216294Ssyrinx} 1818216294Ssyrinx 1819216294Ssyrinxint 1820122394Shartisnmp_client_set_host(struct snmp_client *cl, const char *h) 1821122394Sharti{ 1822122394Sharti char *np; 1823122394Sharti 1824122394Sharti if (h == NULL) { 1825122394Sharti if (cl->chost != NULL) 1826122394Sharti free(cl->chost); 1827122394Sharti cl->chost = NULL; 1828122394Sharti } else { 1829122394Sharti if ((np = malloc(strlen(h) + 1)) == NULL) 1830122394Sharti return (-1); 1831122394Sharti strcpy(np, h); 1832122394Sharti if (cl->chost != NULL) 1833122394Sharti free(cl->chost); 1834122394Sharti cl->chost = np; 1835122394Sharti } 1836122394Sharti return (0); 1837122394Sharti} 1838122394Sharti 1839122394Shartiint 1840122394Shartisnmp_client_set_port(struct snmp_client *cl, const char *p) 1841122394Sharti{ 1842122394Sharti char *np; 1843122394Sharti 1844122394Sharti if (p == NULL) { 1845122394Sharti if (cl->cport != NULL) 1846122394Sharti free(cl->cport); 1847122394Sharti cl->cport = NULL; 1848122394Sharti } else { 1849122394Sharti if ((np = malloc(strlen(p) + 1)) == NULL) 1850122394Sharti return (-1); 1851122394Sharti strcpy(np, p); 1852122394Sharti if (cl->cport != NULL) 1853122394Sharti free(cl->cport); 1854122394Sharti cl->cport = np; 1855122394Sharti } 1856122394Sharti return (0); 1857122394Sharti} 1858146525Sharti 1859146525Sharti/* 1860146525Sharti * parse a server specification 1861146525Sharti * 1862146525Sharti * [trans::][community@][server][:port] 1863146525Sharti */ 1864146525Shartiint 1865146525Shartisnmp_parse_server(struct snmp_client *sc, const char *str) 1866146525Sharti{ 1867146525Sharti const char *p, *s = str; 1868146525Sharti 1869146525Sharti /* look for a double colon */ 1870146525Sharti for (p = s; *p != '\0'; p++) { 1871146525Sharti if (*p == '\\' && p[1] != '\0') { 1872146525Sharti p++; 1873146525Sharti continue; 1874146525Sharti } 1875146525Sharti if (*p == ':' && p[1] == ':') 1876146525Sharti break; 1877146525Sharti } 1878146525Sharti if (*p != '\0') { 1879146525Sharti if (p > s) { 1880146525Sharti if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1881146525Sharti sc->trans = SNMP_TRANS_UDP; 1882146525Sharti else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1883146525Sharti sc->trans = SNMP_TRANS_LOC_STREAM; 1884146525Sharti else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1885146525Sharti sc->trans = SNMP_TRANS_LOC_DGRAM; 1886146525Sharti else { 1887146525Sharti seterr(sc, "unknown SNMP transport '%.*s'", 1888146525Sharti (int)(p - s), s); 1889146525Sharti return (-1); 1890146525Sharti } 1891146525Sharti } 1892146525Sharti s = p + 2; 1893146525Sharti } 1894146525Sharti 1895146525Sharti /* look for a @ */ 1896146525Sharti for (p = s; *p != '\0'; p++) { 1897146525Sharti if (*p == '\\' && p[1] != '\0') { 1898146525Sharti p++; 1899146525Sharti continue; 1900146525Sharti } 1901146525Sharti if (*p == '@') 1902146525Sharti break; 1903146525Sharti } 1904146525Sharti 1905146525Sharti if (*p != '\0') { 1906146525Sharti if (p - s > SNMP_COMMUNITY_MAXLEN) { 1907146525Sharti seterr(sc, "community string too long"); 1908146525Sharti return (-1); 1909146525Sharti } 1910146525Sharti strncpy(sc->read_community, s, p - s); 1911146525Sharti sc->read_community[p - s] = '\0'; 1912146525Sharti strncpy(sc->write_community, s, p - s); 1913146525Sharti sc->write_community[p - s] = '\0'; 1914146525Sharti s = p + 1; 1915146525Sharti } 1916146525Sharti 1917146525Sharti /* look for a colon */ 1918146525Sharti for (p = s; *p != '\0'; p++) { 1919146525Sharti if (*p == '\\' && p[1] != '\0') { 1920146525Sharti p++; 1921146525Sharti continue; 1922146525Sharti } 1923146525Sharti if (*p == ':') 1924146525Sharti break; 1925146525Sharti } 1926146525Sharti 1927146525Sharti if (*p == ':') { 1928146525Sharti if (p > s) { 1929146525Sharti /* host:port */ 1930146525Sharti free(sc->chost); 1931146525Sharti if ((sc->chost = malloc(p - s + 1)) == NULL) { 1932146525Sharti seterr(sc, "%s", strerror(errno)); 1933146525Sharti return (-1); 1934146525Sharti } 1935146525Sharti strncpy(sc->chost, s, p - s); 1936146525Sharti sc->chost[p - s] = '\0'; 1937146525Sharti } 1938146525Sharti /* port */ 1939146525Sharti free(sc->cport); 1940146525Sharti if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) { 1941146525Sharti seterr(sc, "%s", strerror(errno)); 1942146525Sharti return (-1); 1943146525Sharti } 1944146525Sharti strcpy(sc->cport, p + 1); 1945146525Sharti 1946146525Sharti } else if (p > s) { 1947146525Sharti /* host */ 1948146525Sharti free(sc->chost); 1949146525Sharti if ((sc->chost = malloc(strlen(s) + 1)) == NULL) { 1950146525Sharti seterr(sc, "%s", strerror(errno)); 1951146525Sharti return (-1); 1952146525Sharti } 1953146525Sharti strcpy(sc->chost, s); 1954146525Sharti } 1955146525Sharti return (0); 1956146525Sharti} 1957