133965Sjdp/* 278828Sobrien * Copyright (c) 2004-2005 378828Sobrien * Hartmut Brandt. 4213274Srpaulo * All rights reserved. 5213274Srpaulo * Copyright (c) 2001-2003 633965Sjdp * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 778828Sobrien * All rights reserved. 878828Sobrien * 978828Sobrien * Author: Harti Brandt <harti@freebsd.org> 1078828Sobrien * Kendy Kutzner 1178828Sobrien * 1278828Sobrien * Redistribution and use in source and binary forms, with or without 1378828Sobrien * modification, are permitted provided that the following conditions 1478828Sobrien * are met: 1578828Sobrien * 1. Redistributions of source code must retain the above copyright 1678828Sobrien * notice, this list of conditions and the following disclaimer. 1778828Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1878828Sobrien * notice, this list of conditions and the following disclaimer in the 19213274Srpaulo * documentation and/or other materials provided with the distribution. 2078828Sobrien * 2133965Sjdp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2233965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2333965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2433965Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 2533965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2633965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2733965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2833965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2933965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3033965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3133965Sjdp * SUCH DAMAGE. 3233965Sjdp * 3333965Sjdp * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 3433965Sjdp * 3533965Sjdp * Support functions for SNMP clients. 3633965Sjdp */ 3733965Sjdp#include <sys/types.h> 3833965Sjdp#include <sys/time.h> 3933965Sjdp#include <sys/queue.h> 4033965Sjdp#include <sys/socket.h> 4133965Sjdp#include <sys/un.h> 4233965Sjdp#include <stdio.h> 4333965Sjdp#include <stdlib.h> 4433965Sjdp#include <stddef.h> 4533965Sjdp#include <stdarg.h> 4633965Sjdp#include <string.h> 4733965Sjdp#include <errno.h> 4833965Sjdp#include <unistd.h> 4933965Sjdp#include <fcntl.h> 5033965Sjdp#include <netdb.h> 5133965Sjdp#ifdef HAVE_STDINT_H 5233965Sjdp#include <stdint.h> 5333965Sjdp#elif defined(HAVE_INTTYPES_H) 5433965Sjdp#include <inttypes.h> 5533965Sjdp#endif 5633965Sjdp#include <limits.h> 5733965Sjdp#ifdef HAVE_ERR_H 5833965Sjdp#include <err.h> 5933965Sjdp#endif 6033965Sjdp 61213274Srpaulo#include "support.h" 62213274Srpaulo#include "asn1.h" 6333965Sjdp#include "snmp.h" 6433965Sjdp#include "snmpclient.h" 6533965Sjdp#include "snmppriv.h" 6633965Sjdp 67213274Srpaulo/* global context */ 68213274Srpaulostruct snmp_client snmp_client; 69213274Srpaulo 70213274Srpaulo/* List of all outstanding requests */ 71213274Srpaulostruct sent_pdu { 72213274Srpaulo int reqid; 73213274Srpaulo struct snmp_pdu *pdu; 7489857Sobrien struct timeval time; 7533965Sjdp u_int retrycount; 7633965Sjdp snmp_send_cb_f callback; 7733965Sjdp void *arg; 7833965Sjdp void *timeout_id; 7933965Sjdp LIST_ENTRY(sent_pdu) entries; 8033965Sjdp}; 8133965SjdpLIST_HEAD(sent_pdu_list, sent_pdu); 8277298Sobrien 8333965Sjdpstatic struct sent_pdu_list sent_pdus; 8433965Sjdp 8533965Sjdp/* 8633965Sjdp * Prototype table entry. All C-structure produced by the table function must 8789857Sobrien * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 8889857Sobrien * are compatible with each other in the sense implied by ANSI-C. 8989857Sobrien */ 9089857Sobrienstruct entry { 9189857Sobrien TAILQ_ENTRY(entry) link; 9289857Sobrien uint64_t found; 9389857Sobrien}; 9489857SobrienTAILQ_HEAD(table, entry); 9589857Sobrien 96213274Srpaulo/* 97213274Srpaulo * working list entry. This list is used to hold the Index part of the 9833965Sjdp * table row's. The entry list and the work list parallel each other. 9933965Sjdp */ 10033965Sjdpstruct work { 10133965Sjdp TAILQ_ENTRY(work) link; 10233965Sjdp struct asn_oid index; 10333965Sjdp}; 10433965SjdpTAILQ_HEAD(worklist, work); 10533965Sjdp 10633965Sjdp/* 10733965Sjdp * Table working data 10833965Sjdp */ 109213274Srpaulostruct tabwork { 11033965Sjdp const struct snmp_table *descr; 11189857Sobrien struct table *table; 11233965Sjdp struct worklist worklist; 11333965Sjdp uint32_t last_change; 11433965Sjdp int first; 11533965Sjdp u_int iter; 11633965Sjdp snmp_table_cb_f callback; 117213274Srpaulo void *arg; 118213274Srpaulo struct snmp_pdu pdu; 119213274Srpaulo}; 120213274Srpaulo 121213274Srpaulo/* 122213274Srpaulo * Set the error string 123213274Srpaulo */ 124213274Srpaulostatic void 125213274Srpauloseterr(struct snmp_client *sc, const char *fmt, ...) 126213274Srpaulo{ 127213274Srpaulo va_list ap; 128213274Srpaulo 129213274Srpaulo va_start(ap, fmt); 130213274Srpaulo vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 131213274Srpaulo va_end(ap); 132213274Srpaulo} 13333965Sjdp 13433965Sjdp/* 13560484Sobrien * Free the entire table and work list. If table is NULL only the worklist 13633965Sjdp * is freed. 13733965Sjdp */ 13833965Sjdpstatic void 13960484Sobrientable_free(struct tabwork *work, int all) 14060484Sobrien{ 14160484Sobrien struct work *w; 14260484Sobrien struct entry *e; 14333965Sjdp const struct snmp_table_entry *d; 14433965Sjdp u_int i; 14533965Sjdp 14677298Sobrien while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 14777298Sobrien TAILQ_REMOVE(&work->worklist, w, link); 14877298Sobrien free(w); 14933965Sjdp } 15033965Sjdp 15133965Sjdp if (all == 0) 152213274Srpaulo return; 153213274Srpaulo 154213274Srpaulo while ((e = TAILQ_FIRST(work->table)) != NULL) { 155213274Srpaulo for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 156213274Srpaulo i++) { 157213274Srpaulo d = &work->descr->entries[i]; 158213274Srpaulo if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 159213274Srpaulo (e->found & ((uint64_t)1 << i))) 160213274Srpaulo free(*(void **)(void *) 161213274Srpaulo ((u_char *)e + d->offset)); 162213274Srpaulo } 163213274Srpaulo TAILQ_REMOVE(work->table, e, link); 164213274Srpaulo free(e); 165213274Srpaulo } 166213274Srpaulo} 167213274Srpaulo 168213274Srpaulo/* 169213274Srpaulo * Find the correct table entry for the given variable. If non exists, 170213274Srpaulo * create one. 171213274Srpaulo */ 172213274Srpaulostatic struct entry * 173213274Srpaulotable_find(struct tabwork *work, const struct asn_oid *var) 17433965Sjdp{ 17533965Sjdp struct entry *e, *e1; 17633965Sjdp struct work *w, *w1; 17733965Sjdp u_int i, p, j; 17833965Sjdp size_t len; 17933965Sjdp u_char *ptr; 18033965Sjdp struct asn_oid oid; 18133965Sjdp 18233965Sjdp /* get index */ 18333965Sjdp asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 18433965Sjdp 18533965Sjdp e = TAILQ_FIRST(work->table); 18633965Sjdp w = TAILQ_FIRST(&work->worklist); 18733965Sjdp while (e != NULL) { 18833965Sjdp if (asn_compare_oid(&w->index, &oid) == 0) 18933965Sjdp return (e); 19033965Sjdp e = TAILQ_NEXT(e, link); 19133965Sjdp w = TAILQ_NEXT(w, link); 19233965Sjdp } 19333965Sjdp 19433965Sjdp /* Not found create new one */ 19533965Sjdp if ((e = malloc(work->descr->entry_size)) == NULL) { 19633965Sjdp seterr(&snmp_client, "no memory for table entry"); 19733965Sjdp return (NULL); 19833965Sjdp } 199213274Srpaulo if ((w = malloc(sizeof(*w))) == NULL) { 20033965Sjdp seterr(&snmp_client, "no memory for table entry"); 20133965Sjdp free(e); 20233965Sjdp return (NULL); 20333965Sjdp } 20433965Sjdp w->index = oid; 20533965Sjdp memset(e, 0, work->descr->entry_size); 20633965Sjdp 20733965Sjdp /* decode index */ 20833965Sjdp p = work->descr->table.len + 2; 20933965Sjdp for (i = 0; i < work->descr->index_size; i++) { 21033965Sjdp switch (work->descr->entries[i].syntax) { 21133965Sjdp 21233965Sjdp case SNMP_SYNTAX_INTEGER: 21333965Sjdp if (var->len < p + 1) { 21433965Sjdp seterr(&snmp_client, "bad index: need integer"); 21533965Sjdp goto err; 21633965Sjdp } 21733965Sjdp if (var->subs[p] > INT32_MAX) { 21833965Sjdp seterr(&snmp_client, 21933965Sjdp "bad index: integer too large"); 22033965Sjdp goto err; 22133965Sjdp } 22233965Sjdp *(int32_t *)(void *)((u_char *)e + 22333965Sjdp work->descr->entries[i].offset) = var->subs[p++]; 22433965Sjdp break; 22533965Sjdp 22689857Sobrien case SNMP_SYNTAX_OCTETSTRING: 22733965Sjdp if (var->len < p + 1) { 22833965Sjdp seterr(&snmp_client, 22933965Sjdp "bad index: need string length"); 23033965Sjdp goto err; 23133965Sjdp } 23233965Sjdp len = var->subs[p++]; 23333965Sjdp if (var->len < p + len) { 23433965Sjdp seterr(&snmp_client, 23533965Sjdp "bad index: string too short"); 23633965Sjdp goto err; 23789857Sobrien } 23889857Sobrien if ((ptr = malloc(len + 1)) == NULL) { 23933965Sjdp seterr(&snmp_client, 24033965Sjdp "no memory for index string"); 24133965Sjdp goto err; 24233965Sjdp } 24333965Sjdp for (j = 0; j < len; j++) { 24433965Sjdp if (var->subs[p] > UCHAR_MAX) { 24533965Sjdp seterr(&snmp_client, 24633965Sjdp "bad index: char too large"); 24733965Sjdp free(ptr); 24833965Sjdp goto err; 24933965Sjdp } 25033965Sjdp ptr[j] = var->subs[p++]; 25133965Sjdp } 25233965Sjdp ptr[j] = '\0'; 25333965Sjdp *(u_char **)(void *)((u_char *)e + 25433965Sjdp work->descr->entries[i].offset) = ptr; 25533965Sjdp *(size_t *)(void *)((u_char *)e + 25633965Sjdp work->descr->entries[i].offset + sizeof(u_char *)) 25733965Sjdp = len; 25833965Sjdp break; 25933965Sjdp 26033965Sjdp case SNMP_SYNTAX_OID: 26133965Sjdp if (var->len < p + 1) { 26233965Sjdp seterr(&snmp_client, 26333965Sjdp "bad index: need oid length"); 26433965Sjdp goto err; 26533965Sjdp } 26633965Sjdp oid.len = var->subs[p++]; 26733965Sjdp if (var->len < p + oid.len) { 26833965Sjdp seterr(&snmp_client, 26933965Sjdp "bad index: oid too short"); 27033965Sjdp goto err; 27133965Sjdp } 27233965Sjdp for (j = 0; j < oid.len; j++) 27333965Sjdp oid.subs[j] = var->subs[p++]; 27433965Sjdp *(struct asn_oid *)(void *)((u_char *)e + 27533965Sjdp work->descr->entries[i].offset) = oid; 27633965Sjdp break; 27733965Sjdp 27833965Sjdp case SNMP_SYNTAX_IPADDRESS: 27933965Sjdp if (var->len < p + 4) { 28033965Sjdp seterr(&snmp_client, 28133965Sjdp "bad index: need ip-address"); 28233965Sjdp goto err; 28333965Sjdp } 28433965Sjdp for (j = 0; j < 4; j++) { 28533965Sjdp if (var->subs[p] > 0xff) { 28633965Sjdp seterr(&snmp_client, 287130561Sobrien "bad index: ipaddress too large"); 28838889Sjdp goto err; 28977298Sobrien } 29060484Sobrien ((u_char *)e + 29160484Sobrien work->descr->entries[i].offset)[j] = 29260484Sobrien var->subs[p++]; 29360484Sobrien } 29460484Sobrien break; 29533965Sjdp 29633965Sjdp case SNMP_SYNTAX_GAUGE: 29733965Sjdp if (var->len < p + 1) { 29833965Sjdp seterr(&snmp_client, 29933965Sjdp "bad index: need unsigned"); 30033965Sjdp goto err; 30133965Sjdp } 30233965Sjdp if (var->subs[p] > UINT32_MAX) { 30333965Sjdp seterr(&snmp_client, 30433965Sjdp "bad index: unsigned too large"); 30533965Sjdp goto err; 30633965Sjdp } 30733965Sjdp *(uint32_t *)(void *)((u_char *)e + 30833965Sjdp work->descr->entries[i].offset) = var->subs[p++]; 30933965Sjdp break; 31033965Sjdp 31133965Sjdp case SNMP_SYNTAX_COUNTER: 31233965Sjdp case SNMP_SYNTAX_TIMETICKS: 31333965Sjdp case SNMP_SYNTAX_COUNTER64: 31433965Sjdp case SNMP_SYNTAX_NULL: 31533965Sjdp case SNMP_SYNTAX_NOSUCHOBJECT: 31633965Sjdp case SNMP_SYNTAX_NOSUCHINSTANCE: 31733965Sjdp case SNMP_SYNTAX_ENDOFMIBVIEW: 31833965Sjdp abort(); 31933965Sjdp } 320213274Srpaulo e->found |= (uint64_t)1 << i; 32133965Sjdp } 322213274Srpaulo 323213274Srpaulo /* link into the correct place */ 324213274Srpaulo e1 = TAILQ_FIRST(work->table); 325213274Srpaulo w1 = TAILQ_FIRST(&work->worklist); 326213274Srpaulo while (e1 != NULL) { 32733965Sjdp if (asn_compare_oid(&w1->index, &w->index) > 0) 32833965Sjdp break; 32933965Sjdp e1 = TAILQ_NEXT(e1, link); 33033965Sjdp w1 = TAILQ_NEXT(w1, link); 33133965Sjdp } 33233965Sjdp if (e1 == NULL) { 33333965Sjdp TAILQ_INSERT_TAIL(work->table, e, link); 33433965Sjdp TAILQ_INSERT_TAIL(&work->worklist, w, link); 33533965Sjdp } else { 33633965Sjdp TAILQ_INSERT_BEFORE(e1, e, link); 33733965Sjdp TAILQ_INSERT_BEFORE(w1, w, link); 33833965Sjdp } 33933965Sjdp 34033965Sjdp return (e); 34133965Sjdp 34233965Sjdp err: 34333965Sjdp /* 34438889Sjdp * Error happend. Free all octet string index parts and the entry 34560484Sobrien * itself. 34660484Sobrien */ 34760484Sobrien for (i = 0; i < work->descr->index_size; i++) { 34860484Sobrien if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 34960484Sobrien (e->found & ((uint64_t)1 << i))) 35038889Sjdp free(*(void **)(void *)((u_char *)e + 351213274Srpaulo work->descr->entries[i].offset)); 352213274Srpaulo } 353213274Srpaulo free(e); 354213274Srpaulo free(w); 35533965Sjdp return (NULL); 35633965Sjdp} 35733965Sjdp 35833965Sjdp/* 35933965Sjdp * Assign the value 36033965Sjdp */ 36133965Sjdpstatic int 36233965Sjdptable_value(const struct snmp_table *descr, struct entry *e, 36333965Sjdp const struct snmp_value *b) 36433965Sjdp{ 36533965Sjdp u_int i; 36633965Sjdp u_char *ptr; 36733965Sjdp 36833965Sjdp for (i = descr->index_size; 36933965Sjdp descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 37033965Sjdp if (descr->entries[i].subid == 37133965Sjdp b->var.subs[descr->table.len + 1]) 37233965Sjdp break; 37333965Sjdp if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 37433965Sjdp return (0); 37533965Sjdp 37633965Sjdp /* check syntax */ 37733965Sjdp if (b->syntax != descr->entries[i].syntax) { 37877298Sobrien seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 37933965Sjdp descr->entries[i].syntax); 38033965Sjdp return (-1); 38189857Sobrien } 38233965Sjdp 38333965Sjdp switch (b->syntax) { 38433965Sjdp 38533965Sjdp case SNMP_SYNTAX_INTEGER: 38633965Sjdp *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 38733965Sjdp b->v.integer; 38833965Sjdp break; 38933965Sjdp 39033965Sjdp case SNMP_SYNTAX_OCTETSTRING: 39133965Sjdp if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 39233965Sjdp seterr(&snmp_client, "no memory for string"); 39333965Sjdp return (-1); 39433965Sjdp } 39533965Sjdp memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 39633965Sjdp ptr[b->v.octetstring.len] = '\0'; 39733965Sjdp *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 39833965Sjdp ptr; 39933965Sjdp *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 40089857Sobrien sizeof(u_char *)) = b->v.octetstring.len; 40189857Sobrien break; 40289857Sobrien 40389857Sobrien case SNMP_SYNTAX_OID: 40433965Sjdp *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 40533965Sjdp b->v.oid; 40633965Sjdp break; 40733965Sjdp 40833965Sjdp case SNMP_SYNTAX_IPADDRESS: 40933965Sjdp memcpy((u_char *)e + descr->entries[i].offset, 41033965Sjdp b->v.ipaddress, 4); 41189857Sobrien break; 41289857Sobrien 41389857Sobrien case SNMP_SYNTAX_COUNTER: 41433965Sjdp case SNMP_SYNTAX_GAUGE: 41533965Sjdp case SNMP_SYNTAX_TIMETICKS: 41633965Sjdp *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 41733965Sjdp b->v.uint32; 41833965Sjdp break; 41989857Sobrien 42089857Sobrien case SNMP_SYNTAX_COUNTER64: 42133965Sjdp *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 42233965Sjdp b->v.counter64; 42333965Sjdp break; 42433965Sjdp 42533965Sjdp case SNMP_SYNTAX_NULL: 42633965Sjdp case SNMP_SYNTAX_NOSUCHOBJECT: 42733965Sjdp case SNMP_SYNTAX_NOSUCHINSTANCE: 42833965Sjdp case SNMP_SYNTAX_ENDOFMIBVIEW: 42933965Sjdp abort(); 43033965Sjdp } 43133965Sjdp e->found |= (uint64_t)1 << i; 43233965Sjdp 43333965Sjdp return (0); 43433965Sjdp} 435213274Srpaulo 43633965Sjdp/* 43733965Sjdp * Initialize the first PDU to send 438213274Srpaulo */ 439213274Srpaulostatic void 44033965Sjdptable_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 44133965Sjdp{ 44233965Sjdp if (snmp_client.version == SNMP_V1) 443213274Srpaulo snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 44433965Sjdp else { 44533965Sjdp snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 44633965Sjdp pdu->error_index = 10; 44733965Sjdp } 44833965Sjdp if (descr->last_change.len != 0) { 44933965Sjdp pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 45033965Sjdp pdu->bindings[pdu->nbindings].var = descr->last_change; 45133965Sjdp pdu->nbindings++; 45233965Sjdp if (pdu->version != SNMP_V1) 45333965Sjdp pdu->error_status++; 45433965Sjdp } 45533965Sjdp pdu->bindings[pdu->nbindings].var = descr->table; 45689857Sobrien pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 45733965Sjdp pdu->nbindings++; 45833965Sjdp} 45933965Sjdp 46033965Sjdp/* 46133965Sjdp * Return code: 46233965Sjdp * 0 - End Of Table 46333965Sjdp * -1 - Error 46489857Sobrien * -2 - Last change changed - again 46589857Sobrien * +1 - ok, continue 46633965Sjdp */ 46733965Sjdpstatic int 46833965Sjdptable_check_response(struct tabwork *work, const struct snmp_pdu *resp) 46933965Sjdp{ 47033965Sjdp const struct snmp_value *b; 47133965Sjdp struct entry *e; 47233965Sjdp 47333965Sjdp if (resp->error_status != SNMP_ERR_NOERROR) { 47433965Sjdp if (snmp_client.version == SNMP_V1 && 47533965Sjdp resp->error_status == SNMP_ERR_NOSUCHNAME && 47633965Sjdp resp->error_index == 47733965Sjdp (work->descr->last_change.len == 0) ? 1 : 2) 47833965Sjdp /* EOT */ 47933965Sjdp return (0); 48033965Sjdp /* Error */ 48133965Sjdp seterr(&snmp_client, "error fetching table: status=%d index=%d", 48233965Sjdp resp->error_status, resp->error_index); 48333965Sjdp return (-1); 48489857Sobrien } 48589857Sobrien 48633965Sjdp for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 48733965Sjdp if (work->descr->last_change.len != 0 && b == resp->bindings) { 48833965Sjdp if (!asn_is_suboid(&work->descr->last_change, &b->var) || 48933965Sjdp b->var.len != work->descr->last_change.len + 1 || 49033965Sjdp b->var.subs[work->descr->last_change.len] != 0) { 49133965Sjdp seterr(&snmp_client, 49260484Sobrien "last_change: bad response"); 49333965Sjdp return (-1); 49438889Sjdp } 49538889Sjdp if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 49638889Sjdp seterr(&snmp_client, 49738889Sjdp "last_change: bad syntax %u", b->syntax); 49838889Sjdp return (-1); 49938889Sjdp } 50038889Sjdp if (work->first) { 50138889Sjdp work->last_change = b->v.uint32; 50238889Sjdp work->first = 0; 50338889Sjdp 50433965Sjdp } else if (work->last_change != b->v.uint32) { 50533965Sjdp if (++work->iter >= work->descr->max_iter) { 50633965Sjdp seterr(&snmp_client, 50733965Sjdp "max iteration count exceeded"); 50833965Sjdp return (-1); 50933965Sjdp } 51033965Sjdp table_free(work, 1); 51133965Sjdp return (-2); 51233965Sjdp } 51333965Sjdp 51433965Sjdp continue; 51533965Sjdp } 51633965Sjdp if (!asn_is_suboid(&work->descr->table, &b->var) || 51733965Sjdp b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 51833965Sjdp return (0); 51933965Sjdp 52033965Sjdp if ((e = table_find(work, &b->var)) == NULL) 52133965Sjdp return (-1); 52233965Sjdp if (table_value(work->descr, e, b)) 52333965Sjdp return (-1); 52433965Sjdp } 52533965Sjdp return (+1); 52633965Sjdp} 52733965Sjdp 52833965Sjdp/* 52933965Sjdp * Check table consistency 53089857Sobrien */ 53133965Sjdpstatic int 53233965Sjdptable_check_cons(struct tabwork *work) 53333965Sjdp{ 53433965Sjdp struct entry *e; 53533965Sjdp 53633965Sjdp TAILQ_FOREACH(e, work->table, link) 53733965Sjdp if ((e->found & work->descr->req_mask) != 53833965Sjdp work->descr->req_mask) { 53933965Sjdp if (work->descr->last_change.len == 0) { 54033965Sjdp if (++work->iter >= work->descr->max_iter) { 54133965Sjdp seterr(&snmp_client, 54233965Sjdp "max iteration count exceeded"); 54333965Sjdp return (-1); 54433965Sjdp } 54533965Sjdp return (-2); 54633965Sjdp } 54733965Sjdp seterr(&snmp_client, "inconsistency detected %llx %llx", 54833965Sjdp e->found, work->descr->req_mask); 54933965Sjdp return (-1); 55033965Sjdp } 55133965Sjdp return (0); 55233965Sjdp} 55333965Sjdp 55433965Sjdp/* 55533965Sjdp * Fetch a table. Returns 0 if ok, -1 on errors. 55633965Sjdp * This is the synchronous variant. 55733965Sjdp */ 55833965Sjdpint 55933965Sjdpsnmp_table_fetch(const struct snmp_table *descr, void *list) 56033965Sjdp{ 56133965Sjdp struct snmp_pdu resp; 56233965Sjdp struct tabwork work; 56333965Sjdp int ret; 56433965Sjdp 56533965Sjdp work.descr = descr; 56633965Sjdp work.table = (struct table *)list; 56733965Sjdp work.iter = 0; 56833965Sjdp TAILQ_INIT(work.table); 56933965Sjdp TAILQ_INIT(&work.worklist); 57033965Sjdp work.callback = NULL; 57133965Sjdp work.arg = NULL; 57233965Sjdp 57333965Sjdp again: 57433965Sjdp /* 57533965Sjdp * We come to this label when the code detects that the table 57633965Sjdp * has changed while fetching it. 57733965Sjdp */ 57833965Sjdp work.first = 1; 57933965Sjdp work.last_change = 0; 58033965Sjdp table_init_pdu(descr, &work.pdu); 58189857Sobrien 58233965Sjdp for (;;) { 58333965Sjdp if (snmp_dialog(&work.pdu, &resp)) { 58433965Sjdp table_free(&work, 1); 58533965Sjdp return (-1); 58633965Sjdp } 58733965Sjdp if ((ret = table_check_response(&work, &resp)) == 0) { 58833965Sjdp snmp_pdu_free(&resp); 58933965Sjdp break; 59033965Sjdp } 59133965Sjdp if (ret == -1) { 59233965Sjdp snmp_pdu_free(&resp); 59333965Sjdp table_free(&work, 1); 59433965Sjdp return (-1); 59533965Sjdp } 59633965Sjdp if (ret == -2) { 59733965Sjdp snmp_pdu_free(&resp); 59833965Sjdp goto again; 59933965Sjdp } 60033965Sjdp 60133965Sjdp work.pdu.bindings[work.pdu.nbindings - 1].var = 60233965Sjdp resp.bindings[resp.nbindings - 1].var; 60333965Sjdp 60433965Sjdp snmp_pdu_free(&resp); 60533965Sjdp } 60633965Sjdp 60733965Sjdp if ((ret = table_check_cons(&work)) == -1) { 60833965Sjdp table_free(&work, 1); 60933965Sjdp return (-1); 61033965Sjdp } 61133965Sjdp if (ret == -2) { 61233965Sjdp table_free(&work, 1); 61333965Sjdp goto again; 61433965Sjdp } 61533965Sjdp /* 61633965Sjdp * Free index list 61733965Sjdp */ 61833965Sjdp table_free(&work, 0); 61933965Sjdp return (0); 62033965Sjdp} 62133965Sjdp 62233965Sjdp/* 62333965Sjdp * Callback for table 62433965Sjdp */ 62533965Sjdpstatic void 62633965Sjdptable_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 62733965Sjdp{ 62833965Sjdp struct tabwork *work = arg; 62933965Sjdp int ret; 63033965Sjdp 63133965Sjdp if (resp == NULL) { 63233965Sjdp /* timeout */ 63333965Sjdp seterr(&snmp_client, "no response to fetch table request"); 63433965Sjdp table_free(work, 1); 63533965Sjdp work->callback(work->table, work->arg, -1); 63633965Sjdp free(work); 63733965Sjdp return; 63833965Sjdp } 63933965Sjdp 64033965Sjdp if ((ret = table_check_response(work, resp)) == 0) { 64133965Sjdp /* EOT */ 64233965Sjdp snmp_pdu_free(resp); 64333965Sjdp 64433965Sjdp if ((ret = table_check_cons(work)) == -1) { 64533965Sjdp /* error happend */ 64633965Sjdp table_free(work, 1); 64733965Sjdp work->callback(work->table, work->arg, -1); 64833965Sjdp free(work); 64933965Sjdp return; 65033965Sjdp } 65133965Sjdp if (ret == -2) { 65233965Sjdp /* restart */ 65333965Sjdp again: 65433965Sjdp table_free(work, 1); 65533965Sjdp work->first = 1; 65633965Sjdp work->last_change = 0; 65733965Sjdp table_init_pdu(work->descr, &work->pdu); 65833965Sjdp if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 65933965Sjdp work->callback(work->table, work->arg, -1); 66033965Sjdp free(work); 661213274Srpaulo return; 662213274Srpaulo } 663213274Srpaulo return; 664213274Srpaulo } 665213274Srpaulo /* 666213274Srpaulo * Free index list 667213274Srpaulo */ 668213274Srpaulo table_free(work, 0); 669213274Srpaulo work->callback(work->table, work->arg, 0); 670213274Srpaulo free(work); 671213274Srpaulo return; 672213274Srpaulo } 673213274Srpaulo 674213274Srpaulo if (ret == -1) { 675213274Srpaulo /* error */ 676213274Srpaulo snmp_pdu_free(resp); 677213274Srpaulo table_free(work, 1); 678213274Srpaulo work->callback(work->table, work->arg, -1); 679213274Srpaulo free(work); 68060484Sobrien return; 681130561Sobrien } 68260484Sobrien 68360484Sobrien if (ret == -2) { 684213274Srpaulo /* again */ 68560484Sobrien snmp_pdu_free(resp); 68660484Sobrien goto again; 68760484Sobrien } 68860484Sobrien 68960484Sobrien /* next part */ 69060484Sobrien 691130561Sobrien work->pdu.bindings[work->pdu.nbindings - 1].var = 69260484Sobrien resp->bindings[resp->nbindings - 1].var; 69360484Sobrien 69460484Sobrien snmp_pdu_free(resp); 69560484Sobrien 69660484Sobrien if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 69760484Sobrien table_free(work, 1); 698130561Sobrien work->callback(work->table, work->arg, -1); 699130561Sobrien free(work); 70033965Sjdp return; 70160484Sobrien } 70260484Sobrien} 70360484Sobrien 70460484Sobrienint 70560484Sobriensnmp_table_fetch_async(const struct snmp_table *descr, void *list, 70660484Sobrien snmp_table_cb_f func, void *arg) 70733965Sjdp{ 70833965Sjdp struct tabwork *work; 70933965Sjdp 71033965Sjdp if ((work = malloc(sizeof(*work))) == NULL) { 71133965Sjdp seterr(&snmp_client, "%s", strerror(errno)); 71233965Sjdp return (-1); 71333965Sjdp } 71433965Sjdp 71533965Sjdp work->descr = descr; 71633965Sjdp work->table = (struct table *)list; 71733965Sjdp work->iter = 0; 71833965Sjdp TAILQ_INIT(work->table); 71933965Sjdp TAILQ_INIT(&work->worklist); 72033965Sjdp 72133965Sjdp work->callback = func; 72233965Sjdp work->arg = arg; 72333965Sjdp 72433965Sjdp /* 72589857Sobrien * Start by sending the first PDU 72633965Sjdp */ 72733965Sjdp work->first = 1; 72833965Sjdp work->last_change = 0; 72933965Sjdp table_init_pdu(descr, &work->pdu); 73033965Sjdp 73133965Sjdp if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) 73233965Sjdp return (-1); 73333965Sjdp return (0); 73433965Sjdp} 73533965Sjdp 73633965Sjdp/* 73789857Sobrien * Append an index to an oid 73833965Sjdp */ 73933965Sjdpint 74033965Sjdpsnmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 74133965Sjdp{ 74233965Sjdp va_list va; 74333965Sjdp int size; 74433965Sjdp char *nextptr; 74533965Sjdp const u_char *str; 74633965Sjdp size_t len; 74733965Sjdp struct in_addr ina; 74833965Sjdp int ret; 74933965Sjdp 75033965Sjdp va_start(va, fmt); 75133965Sjdp 75233965Sjdp size = 0; 75333965Sjdp 75433965Sjdp ret = 0; 75533965Sjdp while (*fmt != '\0') { 75633965Sjdp switch (*fmt++) { 75733965Sjdp case 'i': 75833965Sjdp /* just an integer more */ 75933965Sjdp if (oid->len + 1 > ASN_MAXOIDLEN) { 76033965Sjdp warnx("%s: OID too long for integer", __func__); 76133965Sjdp ret = -1; 76233965Sjdp break; 76333965Sjdp } 76433965Sjdp oid->subs[oid->len++] = va_arg(va, asn_subid_t); 76589857Sobrien break; 76660484Sobrien 76733965Sjdp case 'a': 76833965Sjdp /* append an IP address */ 76933965Sjdp if (oid->len + 4 > ASN_MAXOIDLEN) { 77033965Sjdp warnx("%s: OID too long for ip-addr", __func__); 77133965Sjdp ret = -1; 77233965Sjdp break; 77333965Sjdp } 77433965Sjdp ina = va_arg(va, struct in_addr); 77533965Sjdp ina.s_addr = ntohl(ina.s_addr); 77633965Sjdp oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 77733965Sjdp oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 77833965Sjdp oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 77933965Sjdp oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 78033965Sjdp break; 78133965Sjdp 78233965Sjdp case 's': 78333965Sjdp /* append a null-terminated string, 78433965Sjdp * length is computed */ 78533965Sjdp str = (const u_char *)va_arg(va, const char *); 78633965Sjdp len = strlen((const char *)str); 78733965Sjdp if (oid->len + len + 1 > ASN_MAXOIDLEN) { 78833965Sjdp warnx("%s: OID too long for string", __func__); 78933965Sjdp ret = -1; 79033965Sjdp break; 791213274Srpaulo } 792213274Srpaulo oid->subs[oid->len++] = len; 793213274Srpaulo while (len--) 794213274Srpaulo oid->subs[oid->len++] = *str++; 795213274Srpaulo break; 79633965Sjdp 79733965Sjdp case '(': 79833965Sjdp /* the integer value between ( and ) is stored 79933965Sjdp * in size */ 80033965Sjdp size = strtol(fmt, &nextptr, 10); 80133965Sjdp if (*nextptr != ')') 80233965Sjdp abort(); 80333965Sjdp fmt = ++nextptr; 80433965Sjdp break; 80533965Sjdp 80633965Sjdp case 'b': 80733965Sjdp /* append `size` characters */ 80833965Sjdp str = (const u_char *)va_arg(va, const char *); 80933965Sjdp if (oid->len + size > ASN_MAXOIDLEN) { 81033965Sjdp warnx("%s: OID too long for string", __func__); 81133965Sjdp ret = -1; 81233965Sjdp break; 81333965Sjdp } 81433965Sjdp while (size--) 81533965Sjdp oid->subs[oid->len++] = *str++; 81633965Sjdp break; 81733965Sjdp 81833965Sjdp case 'c': 81933965Sjdp /* get size and the octets from the arguments */ 82033965Sjdp size = va_arg(va, size_t); 82133965Sjdp str = va_arg(va, const u_char *); 82233965Sjdp if (oid->len + size + 1 > ASN_MAXOIDLEN) { 82333965Sjdp warnx("%s: OID too long for string", __func__); 82433965Sjdp ret = -1; 82533965Sjdp break; 826 } 827 oid->subs[oid->len++] = size; 828 while (size--) 829 oid->subs[oid->len++] = *str++; 830 break; 831 832 default: 833 abort(); 834 } 835 } 836 va_end(va); 837 return (ret); 838} 839 840/* 841 * Initialize a client structure 842 */ 843void 844snmp_client_init(struct snmp_client *c) 845{ 846 memset(c, 0, sizeof(*c)); 847 848 c->version = SNMP_V2c; 849 c->trans = SNMP_TRANS_UDP; 850 c->chost = NULL; 851 c->cport = NULL; 852 853 strcpy(c->read_community, "public"); 854 strcpy(c->write_community, "private"); 855 856 c->security_model = SNMP_SECMODEL_USM; 857 strcpy(c->cname, ""); 858 859 c->timeout.tv_sec = 3; 860 c->timeout.tv_usec = 0; 861 c->retries = 3; 862 c->dump_pdus = 0; 863 c->txbuflen = c->rxbuflen = 10000; 864 865 c->fd = -1; 866 867 c->max_reqid = INT32_MAX; 868 c->min_reqid = 0; 869 c->next_reqid = 0; 870 871 c->engine.max_msg_size = 1500; /* XXX */ 872} 873 874 875/* 876 * Open UDP client socket 877 */ 878static int 879open_client_udp(const char *host, const char *port) 880{ 881 int error; 882 char *ptr; 883 struct addrinfo hints, *res0, *res; 884 885 /* copy host- and portname */ 886 if (snmp_client.chost == NULL) { 887 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 888 == NULL) { 889 seterr(&snmp_client, "%s", strerror(errno)); 890 return (-1); 891 } 892 strcpy(snmp_client.chost, DEFAULT_HOST); 893 } 894 if (host != NULL) { 895 if ((ptr = malloc(1 + strlen(host))) == NULL) { 896 seterr(&snmp_client, "%s", strerror(errno)); 897 return (-1); 898 } 899 free(snmp_client.chost); 900 snmp_client.chost = ptr; 901 strcpy(snmp_client.chost, host); 902 } 903 if (snmp_client.cport == NULL) { 904 if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 905 == NULL) { 906 seterr(&snmp_client, "%s", strerror(errno)); 907 return (-1); 908 } 909 strcpy(snmp_client.cport, DEFAULT_PORT); 910 } 911 if (port != NULL) { 912 if ((ptr = malloc(1 + strlen(port))) == NULL) { 913 seterr(&snmp_client, "%s", strerror(errno)); 914 return (-1); 915 } 916 free(snmp_client.cport); 917 snmp_client.cport = ptr; 918 strcpy(snmp_client.cport, port); 919 } 920 921 /* open connection */ 922 memset(&hints, 0, sizeof(hints)); 923 hints.ai_flags = AI_CANONNAME; 924 hints.ai_family = AF_INET; 925 hints.ai_socktype = SOCK_DGRAM; 926 hints.ai_protocol = 0; 927 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 928 if (error != 0) { 929 seterr(&snmp_client, "%s: %s", snmp_client.chost, 930 gai_strerror(error)); 931 return (-1); 932 } 933 res = res0; 934 for (;;) { 935 if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 936 res->ai_protocol)) == -1) { 937 if ((res = res->ai_next) == NULL) { 938 seterr(&snmp_client, "%s", strerror(errno)); 939 freeaddrinfo(res0); 940 return (-1); 941 } 942 } else if (connect(snmp_client.fd, res->ai_addr, 943 res->ai_addrlen) == -1) { 944 if ((res = res->ai_next) == NULL) { 945 seterr(&snmp_client, "%s", strerror(errno)); 946 freeaddrinfo(res0); 947 return (-1); 948 } 949 } else 950 break; 951 } 952 freeaddrinfo(res0); 953 return (0); 954} 955 956static void 957remove_local(void) 958{ 959 (void)remove(snmp_client.local_path); 960} 961 962/* 963 * Open local socket 964 */ 965static int 966open_client_local(const char *path) 967{ 968 struct sockaddr_un sa; 969 char *ptr; 970 int stype; 971 972 if (snmp_client.chost == NULL) { 973 if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 974 == NULL) { 975 seterr(&snmp_client, "%s", strerror(errno)); 976 return (-1); 977 } 978 strcpy(snmp_client.chost, DEFAULT_LOCAL); 979 } 980 if (path != NULL) { 981 if ((ptr = malloc(1 + strlen(path))) == NULL) { 982 seterr(&snmp_client, "%s", strerror(errno)); 983 return (-1); 984 } 985 free(snmp_client.chost); 986 snmp_client.chost = ptr; 987 strcpy(snmp_client.chost, path); 988 } 989 990 if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 991 stype = SOCK_DGRAM; 992 else 993 stype = SOCK_STREAM; 994 995 if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 996 seterr(&snmp_client, "%s", strerror(errno)); 997 return (-1); 998 } 999 1000 snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 1001 "%s", SNMP_LOCAL_PATH); 1002 1003 if (mktemp(snmp_client.local_path) == NULL) { 1004 seterr(&snmp_client, "%s", strerror(errno)); 1005 (void)close(snmp_client.fd); 1006 snmp_client.fd = -1; 1007 return (-1); 1008 } 1009 1010 sa.sun_family = AF_LOCAL; 1011 sa.sun_len = sizeof(sa); 1012 strcpy(sa.sun_path, snmp_client.local_path); 1013 1014 if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 1015 seterr(&snmp_client, "%s", strerror(errno)); 1016 (void)close(snmp_client.fd); 1017 snmp_client.fd = -1; 1018 (void)remove(snmp_client.local_path); 1019 return (-1); 1020 } 1021 atexit(remove_local); 1022 1023 sa.sun_family = AF_LOCAL; 1024 sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1025 strlen(snmp_client.chost); 1026 strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1027 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1028 1029 if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 1030 seterr(&snmp_client, "%s", strerror(errno)); 1031 (void)close(snmp_client.fd); 1032 snmp_client.fd = -1; 1033 (void)remove(snmp_client.local_path); 1034 return (-1); 1035 } 1036 return (0); 1037} 1038 1039/* 1040 * SNMP_OPEN 1041 */ 1042int 1043snmp_open(const char *host, const char *port, const char *readcomm, 1044 const char *writecomm) 1045{ 1046 struct timeval tout; 1047 1048 /* still open ? */ 1049 if (snmp_client.fd != -1) { 1050 errno = EBUSY; 1051 seterr(&snmp_client, "%s", strerror(errno)); 1052 return (-1); 1053 } 1054 1055 /* copy community strings */ 1056 if (readcomm != NULL) 1057 strlcpy(snmp_client.read_community, readcomm, 1058 sizeof(snmp_client.read_community)); 1059 if (writecomm != NULL) 1060 strlcpy(snmp_client.write_community, writecomm, 1061 sizeof(snmp_client.write_community)); 1062 1063 switch (snmp_client.trans) { 1064 1065 case SNMP_TRANS_UDP: 1066 if (open_client_udp(host, port)) 1067 return (-1); 1068 break; 1069 1070 case SNMP_TRANS_LOC_DGRAM: 1071 case SNMP_TRANS_LOC_STREAM: 1072 if (open_client_local(host)) 1073 return (-1); 1074 break; 1075 1076 default: 1077 seterr(&snmp_client, "bad transport mapping"); 1078 return (-1); 1079 } 1080 tout.tv_sec = 0; 1081 tout.tv_usec = 0; 1082 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1083 &tout, sizeof(struct timeval)) == -1) { 1084 seterr(&snmp_client, "%s", strerror(errno)); 1085 (void)close(snmp_client.fd); 1086 snmp_client.fd = -1; 1087 if (snmp_client.local_path[0] != '\0') 1088 (void)remove(snmp_client.local_path); 1089 return (-1); 1090 } 1091 1092 /* initialize list */ 1093 LIST_INIT(&sent_pdus); 1094 1095 return (0); 1096} 1097 1098 1099/* 1100 * SNMP_CLOSE 1101 * 1102 * closes connection to snmp server 1103 * - function cannot fail 1104 * - clears connection 1105 * - clears list of sent pdus 1106 * 1107 * input: 1108 * void 1109 * return: 1110 * void 1111 */ 1112void 1113snmp_close(void) 1114{ 1115 struct sent_pdu *p1; 1116 1117 if (snmp_client.fd != -1) { 1118 (void)close(snmp_client.fd); 1119 snmp_client.fd = -1; 1120 if (snmp_client.local_path[0] != '\0') 1121 (void)remove(snmp_client.local_path); 1122 } 1123 while(!LIST_EMPTY(&sent_pdus)){ 1124 p1 = LIST_FIRST(&sent_pdus); 1125 if (p1->timeout_id != NULL) 1126 snmp_client.timeout_stop(p1->timeout_id); 1127 LIST_REMOVE(p1, entries); 1128 free(p1); 1129 } 1130 free(snmp_client.chost); 1131 free(snmp_client.cport); 1132} 1133 1134/* 1135 * initialize a snmp_pdu structure 1136 */ 1137void 1138snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1139{ 1140 memset(pdu, 0, sizeof(struct snmp_pdu)); 1141 1142 if (op == SNMP_PDU_SET) 1143 strlcpy(pdu->community, snmp_client.write_community, 1144 sizeof(pdu->community)); 1145 else 1146 strlcpy(pdu->community, snmp_client.read_community, 1147 sizeof(pdu->community)); 1148 1149 pdu->type = op; 1150 pdu->version = snmp_client.version; 1151 pdu->error_status = 0; 1152 pdu->error_index = 0; 1153 pdu->nbindings = 0; 1154 1155 if (snmp_client.version != SNMP_V3) 1156 return; 1157 1158 pdu->identifier = ++snmp_client.identifier; 1159 pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; 1160 pdu->flags = 0; 1161 pdu->security_model = snmp_client.security_model; 1162 1163 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1164 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1165 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1166 snmp_pdu_init_secparams(pdu); 1167 } else 1168 seterr(&snmp_client, "unknown security model"); 1169 1170 if (snmp_client.clen > 0) { 1171 memcpy(pdu->context_engine, snmp_client.cengine, 1172 snmp_client.clen); 1173 pdu->context_engine_len = snmp_client.clen; 1174 } else { 1175 memcpy(pdu->context_engine, snmp_client.engine.engine_id, 1176 snmp_client.engine.engine_len); 1177 pdu->context_engine_len = snmp_client.engine.engine_len; 1178 } 1179 1180 strlcpy(pdu->context_name, snmp_client.cname, 1181 sizeof(pdu->context_name)); 1182} 1183 1184/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1185/* added 10/04/02 by kek: check for MAX_BINDINGS */ 1186int 1187snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1188{ 1189 va_list ap; 1190 const struct asn_oid *oid; 1191 u_int ret; 1192 1193 va_start(ap, pdu); 1194 1195 ret = pdu->nbindings; 1196 while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1197 if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1198 va_end(ap); 1199 return (-1); 1200 } 1201 pdu->bindings[pdu->nbindings].var = *oid; 1202 pdu->bindings[pdu->nbindings].syntax = 1203 va_arg(ap, enum snmp_syntax); 1204 pdu->nbindings++; 1205 } 1206 va_end(ap); 1207 return (ret); 1208} 1209 1210 1211static int32_t 1212snmp_next_reqid(struct snmp_client * c) 1213{ 1214 int32_t i; 1215 1216 i = c->next_reqid; 1217 if (c->next_reqid >= c->max_reqid) 1218 c->next_reqid = c->min_reqid; 1219 else 1220 c->next_reqid++; 1221 return (i); 1222} 1223 1224/* 1225 * Send request and return request id. 1226 */ 1227static int32_t 1228snmp_send_packet(struct snmp_pdu * pdu) 1229{ 1230 u_char *buf; 1231 struct asn_buf b; 1232 ssize_t ret; 1233 1234 if ((buf = malloc(snmp_client.txbuflen)) == NULL) { 1235 seterr(&snmp_client, "%s", strerror(errno)); 1236 return (-1); 1237 } 1238 1239 pdu->request_id = snmp_next_reqid(&snmp_client); 1240 1241 b.asn_ptr = buf; 1242 b.asn_len = snmp_client.txbuflen; 1243 if (snmp_pdu_encode(pdu, &b)) { 1244 seterr(&snmp_client, "%s", strerror(errno)); 1245 free(buf); 1246 return (-1); 1247 } 1248 1249 if (snmp_client.dump_pdus) 1250 snmp_pdu_dump(pdu); 1251 1252 if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 1253 seterr(&snmp_client, "%s", strerror(errno)); 1254 free(buf); 1255 return (-1); 1256 } 1257 free(buf); 1258 1259 return pdu->request_id; 1260} 1261 1262/* 1263 * to be called when a snmp request timed out 1264 */ 1265static void 1266snmp_timeout(void * listentry_ptr) 1267{ 1268 struct sent_pdu *listentry = listentry_ptr; 1269 1270#if 0 1271 warnx("snmp request %i timed out, attempt (%i/%i)", 1272 listentry->reqid, listentry->retrycount, snmp_client.retries); 1273#endif 1274 1275 listentry->retrycount++; 1276 if (listentry->retrycount > snmp_client.retries) { 1277 /* there is no answer at all */ 1278 LIST_REMOVE(listentry, entries); 1279 listentry->callback(listentry->pdu, NULL, listentry->arg); 1280 free(listentry); 1281 } else { 1282 /* try again */ 1283 /* new request with new request ID */ 1284 listentry->reqid = snmp_send_packet(listentry->pdu); 1285 listentry->timeout_id = 1286 snmp_client.timeout_start(&snmp_client.timeout, 1287 snmp_timeout, listentry); 1288 } 1289} 1290 1291int32_t 1292snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1293{ 1294 struct sent_pdu *listentry; 1295 int32_t id; 1296 1297 if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 1298 seterr(&snmp_client, "%s", strerror(errno)); 1299 return (-1); 1300 } 1301 1302 /* here we really send */ 1303 if ((id = snmp_send_packet(pdu)) == -1) { 1304 free(listentry); 1305 return (-1); 1306 } 1307 1308 /* add entry to list of sent PDUs */ 1309 listentry->pdu = pdu; 1310 if (gettimeofday(&listentry->time, NULL) == -1) 1311 warn("gettimeofday() failed"); 1312 1313 listentry->reqid = pdu->request_id; 1314 listentry->callback = func; 1315 listentry->arg = arg; 1316 listentry->retrycount=1; 1317 listentry->timeout_id = 1318 snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1319 listentry); 1320 1321 LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1322 1323 return (id); 1324} 1325 1326/* 1327 * Receive an SNMP packet. 1328 * 1329 * tv controls how we wait for a packet: if tv is a NULL pointer, 1330 * the receive blocks forever, if tv points to a structure with all 1331 * members 0 the socket is polled, in all other cases tv specifies the 1332 * maximum time to wait for a packet. 1333 * 1334 * Return: 1335 * -1 on errors 1336 * 0 on timeout 1337 * +1 if packet received 1338 */ 1339static int 1340snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1341{ 1342 int dopoll, setpoll; 1343 int flags; 1344 int saved_errno; 1345 u_char *buf; 1346 int ret; 1347 struct asn_buf abuf; 1348 int32_t ip; 1349#ifdef bsdi 1350 int optlen; 1351#else 1352 socklen_t optlen; 1353#endif 1354 1355 if ((buf = malloc(snmp_client.rxbuflen)) == NULL) { 1356 seterr(&snmp_client, "%s", strerror(errno)); 1357 return (-1); 1358 } 1359 dopoll = setpoll = 0; 1360 flags = 0; 1361 if (tv != NULL) { 1362 /* poll or timeout */ 1363 if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1364 /* wait with timeout */ 1365 if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1366 tv, sizeof(*tv)) == -1) { 1367 seterr(&snmp_client, "setsockopt: %s", 1368 strerror(errno)); 1369 free(buf); 1370 return (-1); 1371 } 1372 optlen = sizeof(*tv); 1373 if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1374 tv, &optlen) == -1) { 1375 seterr(&snmp_client, "getsockopt: %s", 1376 strerror(errno)); 1377 free(buf); 1378 return (-1); 1379 } 1380 /* at this point tv_sec and tv_usec may appear 1381 * as 0. This happens for timeouts lesser than 1382 * the clock granularity. The kernel rounds these to 1383 * 0 and this would result in a blocking receive. 1384 * Instead of an else we check tv_sec and tv_usec 1385 * again below and if this rounding happens, 1386 * switch to a polling receive. */ 1387 } 1388 if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1389 /* poll */ 1390 dopoll = 1; 1391 if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 1392 seterr(&snmp_client, "fcntl: %s", 1393 strerror(errno)); 1394 free(buf); 1395 return (-1); 1396 } 1397 if (!(flags & O_NONBLOCK)) { 1398 setpoll = 1; 1399 flags |= O_NONBLOCK; 1400 if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 1401 seterr(&snmp_client, "fcntl: %s", 1402 strerror(errno)); 1403 free(buf); 1404 return (-1); 1405 } 1406 } 1407 } 1408 } 1409 ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1410 saved_errno = errno; 1411 if (tv != NULL) { 1412 if (dopoll) { 1413 if (setpoll) { 1414 flags &= ~O_NONBLOCK; 1415 (void)fcntl(snmp_client.fd, F_SETFL, flags); 1416 } 1417 } else { 1418 tv->tv_sec = 0; 1419 tv->tv_usec = 0; 1420 (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1421 tv, sizeof(*tv)); 1422 } 1423 } 1424 if (ret == -1) { 1425 free(buf); 1426 if (errno == EAGAIN || errno == EWOULDBLOCK) 1427 return (0); 1428 seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1429 return (-1); 1430 } 1431 if (ret == 0) { 1432 /* this happens when we have a streaming socket and the 1433 * remote side has closed it */ 1434 free(buf); 1435 seterr(&snmp_client, "recv: socket closed by peer"); 1436 errno = EPIPE; 1437 return (-1); 1438 } 1439 1440 abuf.asn_ptr = buf; 1441 abuf.asn_len = ret; 1442 1443 memset(pdu, 0, sizeof(*pdu)); 1444 if (snmp_client.security_model == SNMP_SECMODEL_USM) { 1445 memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 1446 memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 1447 snmp_pdu_init_secparams(pdu); 1448 } 1449 1450 if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 1451 seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1452 free(buf); 1453 return (-1); 1454 } 1455 1456 free(buf); 1457 if (snmp_client.dump_pdus) 1458 snmp_pdu_dump(pdu); 1459 1460 snmp_client.engine.engine_time = pdu->engine.engine_time; 1461 snmp_client.engine.engine_boots = pdu->engine.engine_boots; 1462 1463 return (+1); 1464} 1465 1466static int 1467snmp_deliver_packet(struct snmp_pdu * resp) 1468{ 1469 struct sent_pdu *listentry; 1470 1471 if (resp->type != SNMP_PDU_RESPONSE) { 1472 warn("ignoring snmp pdu %u", resp->type); 1473 return (-1); 1474 } 1475 1476 LIST_FOREACH(listentry, &sent_pdus, entries) 1477 if (listentry->reqid == resp->request_id) 1478 break; 1479 if (listentry == NULL) 1480 return (-1); 1481 1482 LIST_REMOVE(listentry, entries); 1483 listentry->callback(listentry->pdu, resp, listentry->arg); 1484 1485 snmp_client.timeout_stop(listentry->timeout_id); 1486 1487 free(listentry); 1488 return (0); 1489} 1490 1491int 1492snmp_receive(int blocking) 1493{ 1494 int ret; 1495 1496 struct timeval tv; 1497 struct snmp_pdu * resp; 1498 1499 memset(&tv, 0, sizeof(tv)); 1500 1501 resp = malloc(sizeof(struct snmp_pdu)); 1502 if (resp == NULL) { 1503 seterr(&snmp_client, "no memory for returning PDU"); 1504 return (-1) ; 1505 } 1506 1507 if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1508 free(resp); 1509 return (ret); 1510 } 1511 ret = snmp_deliver_packet(resp); 1512 snmp_pdu_free(resp); 1513 free(resp); 1514 return (ret); 1515} 1516 1517 1518/* 1519 * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1520 * unexpected error happened. +1 response is ok and is within the table 0 1521 * response is ok, but is behind the table or error is NOSUCHNAME. The req 1522 * should point to a template PDU which contains the base OIDs and the 1523 * syntaxes. This is really only useful to sweep non-sparse tables. 1524 */ 1525static int 1526ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1527{ 1528 u_int i; 1529 1530 if (resp->version != req->version) { 1531 warnx("SNMP GETNEXT: response has wrong version"); 1532 return (-1); 1533 } 1534 1535 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1536 return (0); 1537 1538 if (resp->error_status != SNMP_ERR_NOERROR) { 1539 warnx("SNMP GETNEXT: error %d", resp->error_status); 1540 return (-1); 1541 } 1542 if (resp->nbindings != req->nbindings) { 1543 warnx("SNMP GETNEXT: bad number of bindings in response"); 1544 return (-1); 1545 } 1546 for (i = 0; i < req->nbindings; i++) { 1547 if (!asn_is_suboid(&req->bindings[i].var, 1548 &resp->bindings[i].var)) { 1549 if (i != 0) 1550 warnx("SNMP GETNEXT: inconsistent table " 1551 "response"); 1552 return (0); 1553 } 1554 if (resp->version != SNMP_V1 && 1555 resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1556 return (0); 1557 1558 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1559 warnx("SNMP GETNEXT: bad syntax in response"); 1560 return (0); 1561 } 1562 } 1563 return (1); 1564} 1565 1566/* 1567 * Check a GET response. Here we have three possible outcomes: -1 an 1568 * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1569 * point to a template PDU which contains the OIDs and the syntaxes. This 1570 * is only useful for SNMPv1 or single object GETS. 1571 */ 1572static int 1573ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1574{ 1575 u_int i; 1576 1577 if (resp->version != req->version) { 1578 warnx("SNMP GET: response has wrong version"); 1579 return (-1); 1580 } 1581 1582 if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1583 return (0); 1584 1585 if (resp->error_status != SNMP_ERR_NOERROR) { 1586 warnx("SNMP GET: error %d", resp->error_status); 1587 return (-1); 1588 } 1589 1590 if (resp->nbindings != req->nbindings) { 1591 warnx("SNMP GET: bad number of bindings in response"); 1592 return (-1); 1593 } 1594 for (i = 0; i < req->nbindings; i++) { 1595 if (asn_compare_oid(&req->bindings[i].var, 1596 &resp->bindings[i].var) != 0) { 1597 warnx("SNMP GET: bad OID in response"); 1598 return (-1); 1599 } 1600 if (snmp_client.version != SNMP_V1 && 1601 (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1602 resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1603 return (0); 1604 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1605 warnx("SNMP GET: bad syntax in response"); 1606 return (-1); 1607 } 1608 } 1609 return (1); 1610} 1611 1612/* 1613 * Check the response to a SET PDU. We check: - the error status must be 0 - 1614 * the number of bindings must be equal in response and request - the 1615 * syntaxes must be the same in response and request - the OIDs must be the 1616 * same in response and request 1617 */ 1618static int 1619ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1620{ 1621 u_int i; 1622 1623 if (resp->version != req->version) { 1624 warnx("SNMP SET: response has wrong version"); 1625 return (-1); 1626 } 1627 1628 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1629 warnx("SNMP SET: error %d", resp->error_status); 1630 return (0); 1631 } 1632 if (resp->error_status != SNMP_ERR_NOERROR) { 1633 warnx("SNMP SET: error %d", resp->error_status); 1634 return (-1); 1635 } 1636 1637 if (resp->nbindings != req->nbindings) { 1638 warnx("SNMP SET: bad number of bindings in response"); 1639 return (-1); 1640 } 1641 for (i = 0; i < req->nbindings; i++) { 1642 if (asn_compare_oid(&req->bindings[i].var, 1643 &resp->bindings[i].var) != 0) { 1644 warnx("SNMP SET: wrong OID in response to SET"); 1645 return (-1); 1646 } 1647 if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1648 warnx("SNMP SET: bad syntax in response"); 1649 return (-1); 1650 } 1651 } 1652 return (1); 1653} 1654 1655/* 1656 * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1657 * 0=nosuchname or similar, -1=failure, -2=no response at all 1658 */ 1659int 1660snmp_pdu_check(const struct snmp_pdu *req, 1661 const struct snmp_pdu *resp) 1662{ 1663 if (resp == NULL) 1664 return (-2); 1665 1666 switch (req->type) { 1667 1668 case SNMP_PDU_GET: 1669 return (ok_get(req, resp)); 1670 1671 case SNMP_PDU_SET: 1672 return (ok_set(req, resp)); 1673 1674 case SNMP_PDU_GETNEXT: 1675 return (ok_getnext(req, resp)); 1676 1677 } 1678 errx(1, "%s: bad pdu type %i", __func__, req->type); 1679} 1680 1681int 1682snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1683{ 1684 u_int i; 1685 int32_t reqid; 1686 int ret; 1687 struct timeval tv = snmp_client.timeout; 1688 struct timeval end; 1689 struct snmp_pdu pdu; 1690 1691 /* 1692 * Make a copy of the request and replace the syntaxes by NULL 1693 * if this is a GET,GETNEXT or GETBULK. 1694 */ 1695 pdu = *req; 1696 if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 1697 pdu.type == SNMP_PDU_GETBULK) { 1698 for (i = 0; i < pdu.nbindings; i++) 1699 pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 1700 } 1701 1702 for (i = 0; i <= snmp_client.retries; i++) { 1703 (void)gettimeofday(&end, NULL); 1704 timeradd(&end, &snmp_client.timeout, &end); 1705 if ((reqid = snmp_send_packet(&pdu)) == -1) 1706 return (-1); 1707 for (;;) { 1708 (void)gettimeofday(&tv, NULL); 1709 if (timercmp(&end, &tv, <=)) 1710 break; 1711 timersub(&end, &tv, &tv); 1712 if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1713 /* timeout */ 1714 break; 1715 1716 if (ret > 0) { 1717 if (reqid == resp->request_id) 1718 return (0); 1719 /* not for us */ 1720 (void)snmp_deliver_packet(resp); 1721 } 1722 if (ret < 0 && errno == EPIPE) 1723 /* stream closed */ 1724 return (-1); 1725 } 1726 } 1727 errno = ETIMEDOUT; 1728 seterr(&snmp_client, "retry count exceeded"); 1729 return (-1); 1730} 1731 1732int 1733snmp_discover_engine(char *passwd) 1734{ 1735 char cname[SNMP_ADM_STR32_SIZ]; 1736 enum snmp_authentication cap; 1737 enum snmp_privacy cpp; 1738 struct snmp_pdu req, resp; 1739 1740 if (snmp_client.version != SNMP_V3) 1741 seterr(&snmp_client, "wrong version"); 1742 1743 strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); 1744 cap = snmp_client.user.auth_proto; 1745 cpp = snmp_client.user.priv_proto; 1746 1747 snmp_client.engine.engine_len = 0; 1748 snmp_client.engine.engine_boots = 0; 1749 snmp_client.engine.engine_time = 0; 1750 snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; 1751 snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; 1752 memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); 1753 1754 snmp_pdu_create(&req, SNMP_PDU_GET); 1755 1756 if (snmp_dialog(&req, &resp) == -1) 1757 return (-1); 1758 1759 if (resp.version != req.version) { 1760 seterr(&snmp_client, "wrong version"); 1761 return (-1); 1762 } 1763 1764 if (resp.error_status != SNMP_ERR_NOERROR) { 1765 seterr(&snmp_client, "Error %d in responce", resp.error_status); 1766 return (-1); 1767 } 1768 1769 snmp_client.engine.engine_len = resp.engine.engine_len; 1770 snmp_client.engine.max_msg_size = resp.engine.max_msg_size; 1771 memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, 1772 resp.engine.engine_len); 1773 1774 strlcpy(snmp_client.user.sec_name, cname, 1775 sizeof(snmp_client.user.sec_name)); 1776 snmp_client.user.auth_proto = cap; 1777 snmp_client.user.priv_proto = cpp; 1778 1779 if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) 1780 return (0); 1781 1782 if (passwd == NULL || 1783 snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || 1784 snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, 1785 snmp_client.engine.engine_len) != SNMP_CODE_OK) 1786 return (-1); 1787 1788 if (resp.engine.engine_boots != 0) 1789 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1790 1791 if (resp.engine.engine_time != 0) { 1792 snmp_client.engine.engine_time = resp.engine.engine_time; 1793 return (0); 1794 } 1795 1796 snmp_pdu_create(&req, SNMP_PDU_GET); 1797 req.engine.engine_boots = 0; 1798 req.engine.engine_time = 0; 1799 1800 if (snmp_dialog(&req, &resp) == -1) 1801 return (-1); 1802 1803 if (resp.version != req.version) { 1804 seterr(&snmp_client, "wrong version"); 1805 return (-1); 1806 } 1807 1808 if (resp.error_status != SNMP_ERR_NOERROR) { 1809 seterr(&snmp_client, "Error %d in responce", resp.error_status); 1810 return (-1); 1811 } 1812 1813 snmp_client.engine.engine_boots = resp.engine.engine_boots; 1814 snmp_client.engine.engine_time = resp.engine.engine_time; 1815 1816 return (0); 1817} 1818 1819int 1820snmp_client_set_host(struct snmp_client *cl, const char *h) 1821{ 1822 char *np; 1823 1824 if (h == NULL) { 1825 if (cl->chost != NULL) 1826 free(cl->chost); 1827 cl->chost = NULL; 1828 } else { 1829 if ((np = malloc(strlen(h) + 1)) == NULL) 1830 return (-1); 1831 strcpy(np, h); 1832 if (cl->chost != NULL) 1833 free(cl->chost); 1834 cl->chost = np; 1835 } 1836 return (0); 1837} 1838 1839int 1840snmp_client_set_port(struct snmp_client *cl, const char *p) 1841{ 1842 char *np; 1843 1844 if (p == NULL) { 1845 if (cl->cport != NULL) 1846 free(cl->cport); 1847 cl->cport = NULL; 1848 } else { 1849 if ((np = malloc(strlen(p) + 1)) == NULL) 1850 return (-1); 1851 strcpy(np, p); 1852 if (cl->cport != NULL) 1853 free(cl->cport); 1854 cl->cport = np; 1855 } 1856 return (0); 1857} 1858 1859/* 1860 * parse a server specification 1861 * 1862 * [trans::][community@][server][:port] 1863 */ 1864int 1865snmp_parse_server(struct snmp_client *sc, const char *str) 1866{ 1867 const char *p, *s = str; 1868 1869 /* look for a double colon */ 1870 for (p = s; *p != '\0'; p++) { 1871 if (*p == '\\' && p[1] != '\0') { 1872 p++; 1873 continue; 1874 } 1875 if (*p == ':' && p[1] == ':') 1876 break; 1877 } 1878 if (*p != '\0') { 1879 if (p > s) { 1880 if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1881 sc->trans = SNMP_TRANS_UDP; 1882 else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1883 sc->trans = SNMP_TRANS_LOC_STREAM; 1884 else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1885 sc->trans = SNMP_TRANS_LOC_DGRAM; 1886 else { 1887 seterr(sc, "unknown SNMP transport '%.*s'", 1888 (int)(p - s), s); 1889 return (-1); 1890 } 1891 } 1892 s = p + 2; 1893 } 1894 1895 /* look for a @ */ 1896 for (p = s; *p != '\0'; p++) { 1897 if (*p == '\\' && p[1] != '\0') { 1898 p++; 1899 continue; 1900 } 1901 if (*p == '@') 1902 break; 1903 } 1904 1905 if (*p != '\0') { 1906 if (p - s > SNMP_COMMUNITY_MAXLEN) { 1907 seterr(sc, "community string too long"); 1908 return (-1); 1909 } 1910 strncpy(sc->read_community, s, p - s); 1911 sc->read_community[p - s] = '\0'; 1912 strncpy(sc->write_community, s, p - s); 1913 sc->write_community[p - s] = '\0'; 1914 s = p + 1; 1915 } 1916 1917 /* look for a colon */ 1918 for (p = s; *p != '\0'; p++) { 1919 if (*p == '\\' && p[1] != '\0') { 1920 p++; 1921 continue; 1922 } 1923 if (*p == ':') 1924 break; 1925 } 1926 1927 if (*p == ':') { 1928 if (p > s) { 1929 /* host:port */ 1930 free(sc->chost); 1931 if ((sc->chost = malloc(p - s + 1)) == NULL) { 1932 seterr(sc, "%s", strerror(errno)); 1933 return (-1); 1934 } 1935 strncpy(sc->chost, s, p - s); 1936 sc->chost[p - s] = '\0'; 1937 } 1938 /* port */ 1939 free(sc->cport); 1940 if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) { 1941 seterr(sc, "%s", strerror(errno)); 1942 return (-1); 1943 } 1944 strcpy(sc->cport, p + 1); 1945 1946 } else if (p > s) { 1947 /* host */ 1948 free(sc->chost); 1949 if ((sc->chost = malloc(strlen(s) + 1)) == NULL) { 1950 seterr(sc, "%s", strerror(errno)); 1951 return (-1); 1952 } 1953 strcpy(sc->chost, s); 1954 } 1955 return (0); 1956} 1957