1f06ca4afSHartmut Brandt /* 20bf56da3SHartmut Brandt * Copyright (c) 2004-2005,2018-2019 369292cedSHartmut Brandt * Hartmut Brandt. 469292cedSHartmut Brandt * All rights reserved. 5f06ca4afSHartmut Brandt * Copyright (c) 2001-2003 6f06ca4afSHartmut Brandt * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 7f06ca4afSHartmut Brandt * All rights reserved. 8f06ca4afSHartmut Brandt * 9f06ca4afSHartmut Brandt * Author: Harti Brandt <harti@freebsd.org> 10f06ca4afSHartmut Brandt * Kendy Kutzner 11f06ca4afSHartmut Brandt * 12896052c1SHartmut Brandt * Redistribution and use in source and binary forms, with or without 13896052c1SHartmut Brandt * modification, are permitted provided that the following conditions 14896052c1SHartmut Brandt * are met: 15896052c1SHartmut Brandt * 1. Redistributions of source code must retain the above copyright 16896052c1SHartmut Brandt * notice, this list of conditions and the following disclaimer. 17f06ca4afSHartmut Brandt * 2. Redistributions in binary form must reproduce the above copyright 18f06ca4afSHartmut Brandt * notice, this list of conditions and the following disclaimer in the 19f06ca4afSHartmut Brandt * documentation and/or other materials provided with the distribution. 20f06ca4afSHartmut Brandt * 21896052c1SHartmut Brandt * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22896052c1SHartmut Brandt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23896052c1SHartmut Brandt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24896052c1SHartmut Brandt * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25896052c1SHartmut Brandt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26896052c1SHartmut Brandt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27896052c1SHartmut Brandt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28896052c1SHartmut Brandt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29896052c1SHartmut Brandt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30896052c1SHartmut Brandt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31896052c1SHartmut Brandt * SUCH DAMAGE. 32f06ca4afSHartmut Brandt * 33748b5b1eSHartmut Brandt * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 34f06ca4afSHartmut Brandt * 35f06ca4afSHartmut Brandt * Support functions for SNMP clients. 36f06ca4afSHartmut Brandt */ 3704d17814SAndrey V. Elsukov #include <sys/param.h> 38896052c1SHartmut Brandt #include <sys/time.h> 39f06ca4afSHartmut Brandt #include <sys/queue.h> 40f06ca4afSHartmut Brandt #include <sys/socket.h> 41f06ca4afSHartmut Brandt #include <sys/un.h> 4204d17814SAndrey V. Elsukov #include <net/if.h> 4304d17814SAndrey V. Elsukov #include <ctype.h> 44f06ca4afSHartmut Brandt #include <stdio.h> 45f06ca4afSHartmut Brandt #include <stdlib.h> 46f06ca4afSHartmut Brandt #include <stddef.h> 47f06ca4afSHartmut Brandt #include <stdarg.h> 48f06ca4afSHartmut Brandt #include <string.h> 49f06ca4afSHartmut Brandt #include <errno.h> 50f06ca4afSHartmut Brandt #include <unistd.h> 51f06ca4afSHartmut Brandt #include <fcntl.h> 52f06ca4afSHartmut Brandt #include <netdb.h> 53165c5d31SHartmut Brandt #ifdef HAVE_STDINT_H 54896052c1SHartmut Brandt #include <stdint.h> 55165c5d31SHartmut Brandt #elif defined(HAVE_INTTYPES_H) 56165c5d31SHartmut Brandt #include <inttypes.h> 57165c5d31SHartmut Brandt #endif 58f06ca4afSHartmut Brandt #include <limits.h> 59896052c1SHartmut Brandt #ifdef HAVE_ERR_H 60896052c1SHartmut Brandt #include <err.h> 61896052c1SHartmut Brandt #endif 62f06ca4afSHartmut Brandt 6304d17814SAndrey V. Elsukov #include <arpa/inet.h> 6404d17814SAndrey V. Elsukov 65896052c1SHartmut Brandt #include "support.h" 66f06ca4afSHartmut Brandt #include "asn1.h" 67f06ca4afSHartmut Brandt #include "snmp.h" 68f06ca4afSHartmut Brandt #include "snmpclient.h" 69f06ca4afSHartmut Brandt #include "snmppriv.h" 70f06ca4afSHartmut Brandt 7104d17814SAndrey V. Elsukov #define DEBUG_PARSE 0 7204d17814SAndrey V. Elsukov 73f06ca4afSHartmut Brandt /* global context */ 74f5312007SEdward Tomasz Napierala struct snmp_client snmp_client; 75f06ca4afSHartmut Brandt 76f06ca4afSHartmut Brandt /* List of all outstanding requests */ 77f06ca4afSHartmut Brandt struct sent_pdu { 78f06ca4afSHartmut Brandt int reqid; 79f06ca4afSHartmut Brandt struct snmp_pdu *pdu; 80f06ca4afSHartmut Brandt struct timeval time; 81f06ca4afSHartmut Brandt u_int retrycount; 82f06ca4afSHartmut Brandt snmp_send_cb_f callback; 83f06ca4afSHartmut Brandt void *arg; 84f06ca4afSHartmut Brandt void *timeout_id; 85f06ca4afSHartmut Brandt LIST_ENTRY(sent_pdu) entries; 86f06ca4afSHartmut Brandt }; 87f06ca4afSHartmut Brandt LIST_HEAD(sent_pdu_list, sent_pdu); 88f06ca4afSHartmut Brandt 89f5312007SEdward Tomasz Napierala static struct sent_pdu_list sent_pdus; 90f06ca4afSHartmut Brandt 91f06ca4afSHartmut Brandt /* 92f06ca4afSHartmut Brandt * Prototype table entry. All C-structure produced by the table function must 93f06ca4afSHartmut Brandt * start with these two fields. This relies on the fact, that all TAILQ_ENTRY 94f06ca4afSHartmut Brandt * are compatible with each other in the sense implied by ANSI-C. 95f06ca4afSHartmut Brandt */ 96f06ca4afSHartmut Brandt struct entry { 97f06ca4afSHartmut Brandt TAILQ_ENTRY(entry) link; 98896052c1SHartmut Brandt uint64_t found; 99f06ca4afSHartmut Brandt }; 100f06ca4afSHartmut Brandt TAILQ_HEAD(table, entry); 101f06ca4afSHartmut Brandt 102f06ca4afSHartmut Brandt /* 103f06ca4afSHartmut Brandt * working list entry. This list is used to hold the Index part of the 104f06ca4afSHartmut Brandt * table row's. The entry list and the work list parallel each other. 105f06ca4afSHartmut Brandt */ 106f06ca4afSHartmut Brandt struct work { 107f06ca4afSHartmut Brandt TAILQ_ENTRY(work) link; 108f06ca4afSHartmut Brandt struct asn_oid index; 109f06ca4afSHartmut Brandt }; 110f06ca4afSHartmut Brandt TAILQ_HEAD(worklist, work); 111f06ca4afSHartmut Brandt 112f06ca4afSHartmut Brandt /* 113f06ca4afSHartmut Brandt * Table working data 114f06ca4afSHartmut Brandt */ 115f06ca4afSHartmut Brandt struct tabwork { 116f06ca4afSHartmut Brandt const struct snmp_table *descr; 117f06ca4afSHartmut Brandt struct table *table; 118f06ca4afSHartmut Brandt struct worklist worklist; 119896052c1SHartmut Brandt uint32_t last_change; 120f06ca4afSHartmut Brandt int first; 121f06ca4afSHartmut Brandt u_int iter; 122f06ca4afSHartmut Brandt snmp_table_cb_f callback; 123f06ca4afSHartmut Brandt void *arg; 124f06ca4afSHartmut Brandt struct snmp_pdu pdu; 125f06ca4afSHartmut Brandt }; 126f06ca4afSHartmut Brandt 127f06ca4afSHartmut Brandt /* 128f06ca4afSHartmut Brandt * Set the error string 129f06ca4afSHartmut Brandt */ 130f06ca4afSHartmut Brandt static void 13169292cedSHartmut Brandt seterr(struct snmp_client *sc, const char *fmt, ...) 132f06ca4afSHartmut Brandt { 133f06ca4afSHartmut Brandt va_list ap; 134f06ca4afSHartmut Brandt 135f06ca4afSHartmut Brandt va_start(ap, fmt); 13669292cedSHartmut Brandt vsnprintf(sc->error, sizeof(sc->error), fmt, ap); 137f06ca4afSHartmut Brandt va_end(ap); 138f06ca4afSHartmut Brandt } 139f06ca4afSHartmut Brandt 140f06ca4afSHartmut Brandt /* 141f06ca4afSHartmut Brandt * Free the entire table and work list. If table is NULL only the worklist 142f06ca4afSHartmut Brandt * is freed. 143f06ca4afSHartmut Brandt */ 144f06ca4afSHartmut Brandt static void 145f06ca4afSHartmut Brandt table_free(struct tabwork *work, int all) 146f06ca4afSHartmut Brandt { 147f06ca4afSHartmut Brandt struct work *w; 148f06ca4afSHartmut Brandt struct entry *e; 149f06ca4afSHartmut Brandt const struct snmp_table_entry *d; 150f06ca4afSHartmut Brandt u_int i; 151f06ca4afSHartmut Brandt 152f06ca4afSHartmut Brandt while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { 153f06ca4afSHartmut Brandt TAILQ_REMOVE(&work->worklist, w, link); 154f06ca4afSHartmut Brandt free(w); 155f06ca4afSHartmut Brandt } 156f06ca4afSHartmut Brandt 157f06ca4afSHartmut Brandt if (all == 0) 158f06ca4afSHartmut Brandt return; 159f06ca4afSHartmut Brandt 160f06ca4afSHartmut Brandt while ((e = TAILQ_FIRST(work->table)) != NULL) { 161f06ca4afSHartmut Brandt for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; 162f06ca4afSHartmut Brandt i++) { 163f06ca4afSHartmut Brandt d = &work->descr->entries[i]; 164f06ca4afSHartmut Brandt if (d->syntax == SNMP_SYNTAX_OCTETSTRING && 165896052c1SHartmut Brandt (e->found & ((uint64_t)1 << i))) 166f06ca4afSHartmut Brandt free(*(void **)(void *) 167f06ca4afSHartmut Brandt ((u_char *)e + d->offset)); 168f06ca4afSHartmut Brandt } 169f06ca4afSHartmut Brandt TAILQ_REMOVE(work->table, e, link); 170f06ca4afSHartmut Brandt free(e); 171f06ca4afSHartmut Brandt } 172f06ca4afSHartmut Brandt } 173f06ca4afSHartmut Brandt 174f06ca4afSHartmut Brandt /* 175f06ca4afSHartmut Brandt * Find the correct table entry for the given variable. If non exists, 176f06ca4afSHartmut Brandt * create one. 177f06ca4afSHartmut Brandt */ 178f06ca4afSHartmut Brandt static struct entry * 179f06ca4afSHartmut Brandt table_find(struct tabwork *work, const struct asn_oid *var) 180f06ca4afSHartmut Brandt { 181f06ca4afSHartmut Brandt struct entry *e, *e1; 182f06ca4afSHartmut Brandt struct work *w, *w1; 183f06ca4afSHartmut Brandt u_int i, p, j; 184f06ca4afSHartmut Brandt size_t len; 185f06ca4afSHartmut Brandt u_char *ptr; 186f06ca4afSHartmut Brandt struct asn_oid oid; 187f06ca4afSHartmut Brandt 188f06ca4afSHartmut Brandt /* get index */ 189f06ca4afSHartmut Brandt asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); 190f06ca4afSHartmut Brandt 191f06ca4afSHartmut Brandt e = TAILQ_FIRST(work->table); 192f06ca4afSHartmut Brandt w = TAILQ_FIRST(&work->worklist); 193f06ca4afSHartmut Brandt while (e != NULL) { 194f06ca4afSHartmut Brandt if (asn_compare_oid(&w->index, &oid) == 0) 195f06ca4afSHartmut Brandt return (e); 196f06ca4afSHartmut Brandt e = TAILQ_NEXT(e, link); 197f06ca4afSHartmut Brandt w = TAILQ_NEXT(w, link); 198f06ca4afSHartmut Brandt } 199f06ca4afSHartmut Brandt 200f06ca4afSHartmut Brandt /* Not found create new one */ 201f06ca4afSHartmut Brandt if ((e = malloc(work->descr->entry_size)) == NULL) { 20269292cedSHartmut Brandt seterr(&snmp_client, "no memory for table entry"); 203f06ca4afSHartmut Brandt return (NULL); 204f06ca4afSHartmut Brandt } 205f06ca4afSHartmut Brandt if ((w = malloc(sizeof(*w))) == NULL) { 20669292cedSHartmut Brandt seterr(&snmp_client, "no memory for table entry"); 207f06ca4afSHartmut Brandt free(e); 208f06ca4afSHartmut Brandt return (NULL); 209f06ca4afSHartmut Brandt } 210f06ca4afSHartmut Brandt w->index = oid; 211f06ca4afSHartmut Brandt memset(e, 0, work->descr->entry_size); 212f06ca4afSHartmut Brandt 213f06ca4afSHartmut Brandt /* decode index */ 214f06ca4afSHartmut Brandt p = work->descr->table.len + 2; 215f06ca4afSHartmut Brandt for (i = 0; i < work->descr->index_size; i++) { 216f06ca4afSHartmut Brandt switch (work->descr->entries[i].syntax) { 217f06ca4afSHartmut Brandt 218f06ca4afSHartmut Brandt case SNMP_SYNTAX_INTEGER: 219f06ca4afSHartmut Brandt if (var->len < p + 1) { 22069292cedSHartmut Brandt seterr(&snmp_client, "bad index: need integer"); 221f06ca4afSHartmut Brandt goto err; 222f06ca4afSHartmut Brandt } 223f06ca4afSHartmut Brandt if (var->subs[p] > INT32_MAX) { 22469292cedSHartmut Brandt seterr(&snmp_client, 22569292cedSHartmut Brandt "bad index: integer too large"); 226f06ca4afSHartmut Brandt goto err; 227f06ca4afSHartmut Brandt } 228f06ca4afSHartmut Brandt *(int32_t *)(void *)((u_char *)e + 229f06ca4afSHartmut Brandt work->descr->entries[i].offset) = var->subs[p++]; 230f06ca4afSHartmut Brandt break; 231f06ca4afSHartmut Brandt 232f06ca4afSHartmut Brandt case SNMP_SYNTAX_OCTETSTRING: 233f06ca4afSHartmut Brandt if (var->len < p + 1) { 23469292cedSHartmut Brandt seterr(&snmp_client, 23569292cedSHartmut Brandt "bad index: need string length"); 236f06ca4afSHartmut Brandt goto err; 237f06ca4afSHartmut Brandt } 238f06ca4afSHartmut Brandt len = var->subs[p++]; 239f06ca4afSHartmut Brandt if (var->len < p + len) { 24069292cedSHartmut Brandt seterr(&snmp_client, 24169292cedSHartmut Brandt "bad index: string too short"); 242f06ca4afSHartmut Brandt goto err; 243f06ca4afSHartmut Brandt } 244f06ca4afSHartmut Brandt if ((ptr = malloc(len + 1)) == NULL) { 24569292cedSHartmut Brandt seterr(&snmp_client, 24669292cedSHartmut Brandt "no memory for index string"); 247f06ca4afSHartmut Brandt goto err; 248f06ca4afSHartmut Brandt } 249f06ca4afSHartmut Brandt for (j = 0; j < len; j++) { 250f06ca4afSHartmut Brandt if (var->subs[p] > UCHAR_MAX) { 25169292cedSHartmut Brandt seterr(&snmp_client, 25269292cedSHartmut Brandt "bad index: char too large"); 253f06ca4afSHartmut Brandt free(ptr); 254f06ca4afSHartmut Brandt goto err; 255f06ca4afSHartmut Brandt } 256f06ca4afSHartmut Brandt ptr[j] = var->subs[p++]; 257f06ca4afSHartmut Brandt } 258f06ca4afSHartmut Brandt ptr[j] = '\0'; 259f06ca4afSHartmut Brandt *(u_char **)(void *)((u_char *)e + 260f06ca4afSHartmut Brandt work->descr->entries[i].offset) = ptr; 261f06ca4afSHartmut Brandt *(size_t *)(void *)((u_char *)e + 262f06ca4afSHartmut Brandt work->descr->entries[i].offset + sizeof(u_char *)) 263f06ca4afSHartmut Brandt = len; 264f06ca4afSHartmut Brandt break; 265f06ca4afSHartmut Brandt 266f06ca4afSHartmut Brandt case SNMP_SYNTAX_OID: 267f06ca4afSHartmut Brandt if (var->len < p + 1) { 26869292cedSHartmut Brandt seterr(&snmp_client, 26969292cedSHartmut Brandt "bad index: need oid length"); 270f06ca4afSHartmut Brandt goto err; 271f06ca4afSHartmut Brandt } 272f06ca4afSHartmut Brandt oid.len = var->subs[p++]; 273f06ca4afSHartmut Brandt if (var->len < p + oid.len) { 27469292cedSHartmut Brandt seterr(&snmp_client, 27569292cedSHartmut Brandt "bad index: oid too short"); 276f06ca4afSHartmut Brandt goto err; 277f06ca4afSHartmut Brandt } 278f06ca4afSHartmut Brandt for (j = 0; j < oid.len; j++) 279f06ca4afSHartmut Brandt oid.subs[j] = var->subs[p++]; 280f06ca4afSHartmut Brandt *(struct asn_oid *)(void *)((u_char *)e + 281f06ca4afSHartmut Brandt work->descr->entries[i].offset) = oid; 282f06ca4afSHartmut Brandt break; 283f06ca4afSHartmut Brandt 284f06ca4afSHartmut Brandt case SNMP_SYNTAX_IPADDRESS: 285f06ca4afSHartmut Brandt if (var->len < p + 4) { 28669292cedSHartmut Brandt seterr(&snmp_client, 28769292cedSHartmut Brandt "bad index: need ip-address"); 288f06ca4afSHartmut Brandt goto err; 289f06ca4afSHartmut Brandt } 290f06ca4afSHartmut Brandt for (j = 0; j < 4; j++) { 291f06ca4afSHartmut Brandt if (var->subs[p] > 0xff) { 29269292cedSHartmut Brandt seterr(&snmp_client, 29369292cedSHartmut Brandt "bad index: ipaddress too large"); 294f06ca4afSHartmut Brandt goto err; 295f06ca4afSHartmut Brandt } 296f06ca4afSHartmut Brandt ((u_char *)e + 297f06ca4afSHartmut Brandt work->descr->entries[i].offset)[j] = 298f06ca4afSHartmut Brandt var->subs[p++]; 299f06ca4afSHartmut Brandt } 300f06ca4afSHartmut Brandt break; 301f06ca4afSHartmut Brandt 302f06ca4afSHartmut Brandt case SNMP_SYNTAX_GAUGE: 303f06ca4afSHartmut Brandt if (var->len < p + 1) { 30469292cedSHartmut Brandt seterr(&snmp_client, 30569292cedSHartmut Brandt "bad index: need unsigned"); 306f06ca4afSHartmut Brandt goto err; 307f06ca4afSHartmut Brandt } 308f06ca4afSHartmut Brandt if (var->subs[p] > UINT32_MAX) { 30969292cedSHartmut Brandt seterr(&snmp_client, 31069292cedSHartmut Brandt "bad index: unsigned too large"); 311f06ca4afSHartmut Brandt goto err; 312f06ca4afSHartmut Brandt } 313896052c1SHartmut Brandt *(uint32_t *)(void *)((u_char *)e + 314f06ca4afSHartmut Brandt work->descr->entries[i].offset) = var->subs[p++]; 315f06ca4afSHartmut Brandt break; 316f06ca4afSHartmut Brandt 317f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER: 318f06ca4afSHartmut Brandt case SNMP_SYNTAX_TIMETICKS: 319f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER64: 320f06ca4afSHartmut Brandt case SNMP_SYNTAX_NULL: 321f06ca4afSHartmut Brandt case SNMP_SYNTAX_NOSUCHOBJECT: 322f06ca4afSHartmut Brandt case SNMP_SYNTAX_NOSUCHINSTANCE: 323f06ca4afSHartmut Brandt case SNMP_SYNTAX_ENDOFMIBVIEW: 324f06ca4afSHartmut Brandt abort(); 325f06ca4afSHartmut Brandt } 326896052c1SHartmut Brandt e->found |= (uint64_t)1 << i; 327f06ca4afSHartmut Brandt } 328f06ca4afSHartmut Brandt 329f06ca4afSHartmut Brandt /* link into the correct place */ 330f06ca4afSHartmut Brandt e1 = TAILQ_FIRST(work->table); 331f06ca4afSHartmut Brandt w1 = TAILQ_FIRST(&work->worklist); 332f06ca4afSHartmut Brandt while (e1 != NULL) { 333f06ca4afSHartmut Brandt if (asn_compare_oid(&w1->index, &w->index) > 0) 334f06ca4afSHartmut Brandt break; 335f06ca4afSHartmut Brandt e1 = TAILQ_NEXT(e1, link); 336f06ca4afSHartmut Brandt w1 = TAILQ_NEXT(w1, link); 337f06ca4afSHartmut Brandt } 338f06ca4afSHartmut Brandt if (e1 == NULL) { 339f06ca4afSHartmut Brandt TAILQ_INSERT_TAIL(work->table, e, link); 340f06ca4afSHartmut Brandt TAILQ_INSERT_TAIL(&work->worklist, w, link); 341f06ca4afSHartmut Brandt } else { 342f06ca4afSHartmut Brandt TAILQ_INSERT_BEFORE(e1, e, link); 343f06ca4afSHartmut Brandt TAILQ_INSERT_BEFORE(w1, w, link); 344f06ca4afSHartmut Brandt } 345f06ca4afSHartmut Brandt 346f06ca4afSHartmut Brandt return (e); 347f06ca4afSHartmut Brandt 348f06ca4afSHartmut Brandt err: 349f06ca4afSHartmut Brandt /* 350f06ca4afSHartmut Brandt * Error happend. Free all octet string index parts and the entry 351f06ca4afSHartmut Brandt * itself. 352f06ca4afSHartmut Brandt */ 353f06ca4afSHartmut Brandt for (i = 0; i < work->descr->index_size; i++) { 354f06ca4afSHartmut Brandt if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && 355896052c1SHartmut Brandt (e->found & ((uint64_t)1 << i))) 356f06ca4afSHartmut Brandt free(*(void **)(void *)((u_char *)e + 357f06ca4afSHartmut Brandt work->descr->entries[i].offset)); 358f06ca4afSHartmut Brandt } 359f06ca4afSHartmut Brandt free(e); 360f06ca4afSHartmut Brandt free(w); 361f06ca4afSHartmut Brandt return (NULL); 362f06ca4afSHartmut Brandt } 363f06ca4afSHartmut Brandt 364f06ca4afSHartmut Brandt /* 365f06ca4afSHartmut Brandt * Assign the value 366f06ca4afSHartmut Brandt */ 367f06ca4afSHartmut Brandt static int 368f06ca4afSHartmut Brandt table_value(const struct snmp_table *descr, struct entry *e, 369f06ca4afSHartmut Brandt const struct snmp_value *b) 370f06ca4afSHartmut Brandt { 371f06ca4afSHartmut Brandt u_int i; 372f06ca4afSHartmut Brandt u_char *ptr; 373f06ca4afSHartmut Brandt 374f06ca4afSHartmut Brandt for (i = descr->index_size; 375f06ca4afSHartmut Brandt descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) 376f06ca4afSHartmut Brandt if (descr->entries[i].subid == 377f06ca4afSHartmut Brandt b->var.subs[descr->table.len + 1]) 378f06ca4afSHartmut Brandt break; 379f06ca4afSHartmut Brandt if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) 380f06ca4afSHartmut Brandt return (0); 381f06ca4afSHartmut Brandt 382f06ca4afSHartmut Brandt /* check syntax */ 383f06ca4afSHartmut Brandt if (b->syntax != descr->entries[i].syntax) { 38469292cedSHartmut Brandt seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, 385f06ca4afSHartmut Brandt descr->entries[i].syntax); 386f06ca4afSHartmut Brandt return (-1); 387f06ca4afSHartmut Brandt } 388f06ca4afSHartmut Brandt 389f06ca4afSHartmut Brandt switch (b->syntax) { 390f06ca4afSHartmut Brandt 391f06ca4afSHartmut Brandt case SNMP_SYNTAX_INTEGER: 392f06ca4afSHartmut Brandt *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 393f06ca4afSHartmut Brandt b->v.integer; 394f06ca4afSHartmut Brandt break; 395f06ca4afSHartmut Brandt 396f06ca4afSHartmut Brandt case SNMP_SYNTAX_OCTETSTRING: 397f06ca4afSHartmut Brandt if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { 39869292cedSHartmut Brandt seterr(&snmp_client, "no memory for string"); 399f06ca4afSHartmut Brandt return (-1); 400f06ca4afSHartmut Brandt } 401f06ca4afSHartmut Brandt memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); 402f06ca4afSHartmut Brandt ptr[b->v.octetstring.len] = '\0'; 403f06ca4afSHartmut Brandt *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = 404f06ca4afSHartmut Brandt ptr; 405f06ca4afSHartmut Brandt *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + 406f06ca4afSHartmut Brandt sizeof(u_char *)) = b->v.octetstring.len; 407f06ca4afSHartmut Brandt break; 408f06ca4afSHartmut Brandt 409f06ca4afSHartmut Brandt case SNMP_SYNTAX_OID: 410f06ca4afSHartmut Brandt *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = 411f06ca4afSHartmut Brandt b->v.oid; 412f06ca4afSHartmut Brandt break; 413f06ca4afSHartmut Brandt 414f06ca4afSHartmut Brandt case SNMP_SYNTAX_IPADDRESS: 415f06ca4afSHartmut Brandt memcpy((u_char *)e + descr->entries[i].offset, 416f06ca4afSHartmut Brandt b->v.ipaddress, 4); 417f06ca4afSHartmut Brandt break; 418f06ca4afSHartmut Brandt 419f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER: 420f06ca4afSHartmut Brandt case SNMP_SYNTAX_GAUGE: 421f06ca4afSHartmut Brandt case SNMP_SYNTAX_TIMETICKS: 422896052c1SHartmut Brandt *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = 423f06ca4afSHartmut Brandt b->v.uint32; 424f06ca4afSHartmut Brandt break; 425f06ca4afSHartmut Brandt 426f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER64: 427896052c1SHartmut Brandt *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = 428f06ca4afSHartmut Brandt b->v.counter64; 429f06ca4afSHartmut Brandt break; 430f06ca4afSHartmut Brandt 431f06ca4afSHartmut Brandt case SNMP_SYNTAX_NULL: 432f06ca4afSHartmut Brandt case SNMP_SYNTAX_NOSUCHOBJECT: 433f06ca4afSHartmut Brandt case SNMP_SYNTAX_NOSUCHINSTANCE: 434f06ca4afSHartmut Brandt case SNMP_SYNTAX_ENDOFMIBVIEW: 435f06ca4afSHartmut Brandt abort(); 436f06ca4afSHartmut Brandt } 437896052c1SHartmut Brandt e->found |= (uint64_t)1 << i; 438f06ca4afSHartmut Brandt 439f06ca4afSHartmut Brandt return (0); 440f06ca4afSHartmut Brandt } 441f06ca4afSHartmut Brandt 442f06ca4afSHartmut Brandt /* 443f06ca4afSHartmut Brandt * Initialize the first PDU to send 444f06ca4afSHartmut Brandt */ 445f06ca4afSHartmut Brandt static void 446f06ca4afSHartmut Brandt table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) 447f06ca4afSHartmut Brandt { 448f06ca4afSHartmut Brandt if (snmp_client.version == SNMP_V1) 449f06ca4afSHartmut Brandt snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); 450f06ca4afSHartmut Brandt else { 451f06ca4afSHartmut Brandt snmp_pdu_create(pdu, SNMP_PDU_GETBULK); 452f06ca4afSHartmut Brandt pdu->error_index = 10; 453f06ca4afSHartmut Brandt } 454f06ca4afSHartmut Brandt if (descr->last_change.len != 0) { 455f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 456f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].var = descr->last_change; 457f06ca4afSHartmut Brandt pdu->nbindings++; 458f06ca4afSHartmut Brandt if (pdu->version != SNMP_V1) 459f06ca4afSHartmut Brandt pdu->error_status++; 460f06ca4afSHartmut Brandt } 461f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].var = descr->table; 462f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; 463f06ca4afSHartmut Brandt pdu->nbindings++; 464f06ca4afSHartmut Brandt } 465f06ca4afSHartmut Brandt 466f06ca4afSHartmut Brandt /* 467f06ca4afSHartmut Brandt * Return code: 468f06ca4afSHartmut Brandt * 0 - End Of Table 469f06ca4afSHartmut Brandt * -1 - Error 470f06ca4afSHartmut Brandt * -2 - Last change changed - again 471f06ca4afSHartmut Brandt * +1 - ok, continue 472f06ca4afSHartmut Brandt */ 473f06ca4afSHartmut Brandt static int 474f06ca4afSHartmut Brandt table_check_response(struct tabwork *work, const struct snmp_pdu *resp) 475f06ca4afSHartmut Brandt { 476f06ca4afSHartmut Brandt const struct snmp_value *b; 477f06ca4afSHartmut Brandt struct entry *e; 478f06ca4afSHartmut Brandt 479f06ca4afSHartmut Brandt if (resp->error_status != SNMP_ERR_NOERROR) { 480f06ca4afSHartmut Brandt if (snmp_client.version == SNMP_V1 && 481f06ca4afSHartmut Brandt resp->error_status == SNMP_ERR_NOSUCHNAME && 482f06ca4afSHartmut Brandt resp->error_index == 48338effe88SJustin Hibbits ((work->descr->last_change.len == 0) ? 1 : 2)) 484f06ca4afSHartmut Brandt /* EOT */ 485f06ca4afSHartmut Brandt return (0); 486f06ca4afSHartmut Brandt /* Error */ 48769292cedSHartmut Brandt seterr(&snmp_client, "error fetching table: status=%d index=%d", 488f06ca4afSHartmut Brandt resp->error_status, resp->error_index); 489f06ca4afSHartmut Brandt return (-1); 490f06ca4afSHartmut Brandt } 491f06ca4afSHartmut Brandt 492f06ca4afSHartmut Brandt for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { 493f06ca4afSHartmut Brandt if (work->descr->last_change.len != 0 && b == resp->bindings) { 494f06ca4afSHartmut Brandt if (!asn_is_suboid(&work->descr->last_change, &b->var) || 495f06ca4afSHartmut Brandt b->var.len != work->descr->last_change.len + 1 || 496f06ca4afSHartmut Brandt b->var.subs[work->descr->last_change.len] != 0) { 49769292cedSHartmut Brandt seterr(&snmp_client, 49869292cedSHartmut Brandt "last_change: bad response"); 499f06ca4afSHartmut Brandt return (-1); 500f06ca4afSHartmut Brandt } 501f06ca4afSHartmut Brandt if (b->syntax != SNMP_SYNTAX_TIMETICKS) { 50269292cedSHartmut Brandt seterr(&snmp_client, 50369292cedSHartmut Brandt "last_change: bad syntax %u", b->syntax); 504f06ca4afSHartmut Brandt return (-1); 505f06ca4afSHartmut Brandt } 506f06ca4afSHartmut Brandt if (work->first) { 507f06ca4afSHartmut Brandt work->last_change = b->v.uint32; 508f06ca4afSHartmut Brandt work->first = 0; 509f06ca4afSHartmut Brandt 510f06ca4afSHartmut Brandt } else if (work->last_change != b->v.uint32) { 511f06ca4afSHartmut Brandt if (++work->iter >= work->descr->max_iter) { 51269292cedSHartmut Brandt seterr(&snmp_client, 51369292cedSHartmut Brandt "max iteration count exceeded"); 514f06ca4afSHartmut Brandt return (-1); 515f06ca4afSHartmut Brandt } 516f06ca4afSHartmut Brandt table_free(work, 1); 517f06ca4afSHartmut Brandt return (-2); 518f06ca4afSHartmut Brandt } 519f06ca4afSHartmut Brandt 520f06ca4afSHartmut Brandt continue; 521f06ca4afSHartmut Brandt } 522f06ca4afSHartmut Brandt if (!asn_is_suboid(&work->descr->table, &b->var) || 523f06ca4afSHartmut Brandt b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 524f06ca4afSHartmut Brandt return (0); 525f06ca4afSHartmut Brandt 526f06ca4afSHartmut Brandt if ((e = table_find(work, &b->var)) == NULL) 527f06ca4afSHartmut Brandt return (-1); 528f06ca4afSHartmut Brandt if (table_value(work->descr, e, b)) 529f06ca4afSHartmut Brandt return (-1); 530f06ca4afSHartmut Brandt } 531f06ca4afSHartmut Brandt return (+1); 532f06ca4afSHartmut Brandt } 533f06ca4afSHartmut Brandt 534f06ca4afSHartmut Brandt /* 535f06ca4afSHartmut Brandt * Check table consistency 536f06ca4afSHartmut Brandt */ 537f06ca4afSHartmut Brandt static int 538f06ca4afSHartmut Brandt table_check_cons(struct tabwork *work) 539f06ca4afSHartmut Brandt { 540f06ca4afSHartmut Brandt struct entry *e; 541f06ca4afSHartmut Brandt 542f06ca4afSHartmut Brandt TAILQ_FOREACH(e, work->table, link) 543f06ca4afSHartmut Brandt if ((e->found & work->descr->req_mask) != 544f06ca4afSHartmut Brandt work->descr->req_mask) { 545f06ca4afSHartmut Brandt if (work->descr->last_change.len == 0) { 546f06ca4afSHartmut Brandt if (++work->iter >= work->descr->max_iter) { 54769292cedSHartmut Brandt seterr(&snmp_client, 54869292cedSHartmut Brandt "max iteration count exceeded"); 549f06ca4afSHartmut Brandt return (-1); 550f06ca4afSHartmut Brandt } 551f06ca4afSHartmut Brandt return (-2); 552f06ca4afSHartmut Brandt } 55369292cedSHartmut Brandt seterr(&snmp_client, "inconsistency detected %llx %llx", 554f06ca4afSHartmut Brandt e->found, work->descr->req_mask); 555f06ca4afSHartmut Brandt return (-1); 556f06ca4afSHartmut Brandt } 557f06ca4afSHartmut Brandt return (0); 558f06ca4afSHartmut Brandt } 559f06ca4afSHartmut Brandt 560f06ca4afSHartmut Brandt /* 561f06ca4afSHartmut Brandt * Fetch a table. Returns 0 if ok, -1 on errors. 562165c5d31SHartmut Brandt * This is the synchronous variant. 563f06ca4afSHartmut Brandt */ 564f06ca4afSHartmut Brandt int 565f06ca4afSHartmut Brandt snmp_table_fetch(const struct snmp_table *descr, void *list) 566f06ca4afSHartmut Brandt { 567f06ca4afSHartmut Brandt struct snmp_pdu resp; 568f06ca4afSHartmut Brandt struct tabwork work; 569f06ca4afSHartmut Brandt int ret; 570f06ca4afSHartmut Brandt 571f06ca4afSHartmut Brandt work.descr = descr; 572f06ca4afSHartmut Brandt work.table = (struct table *)list; 573f06ca4afSHartmut Brandt work.iter = 0; 574f06ca4afSHartmut Brandt TAILQ_INIT(work.table); 575f06ca4afSHartmut Brandt TAILQ_INIT(&work.worklist); 576f06ca4afSHartmut Brandt work.callback = NULL; 577f06ca4afSHartmut Brandt work.arg = NULL; 578f06ca4afSHartmut Brandt 579f06ca4afSHartmut Brandt again: 580f06ca4afSHartmut Brandt /* 581f06ca4afSHartmut Brandt * We come to this label when the code detects that the table 582f06ca4afSHartmut Brandt * has changed while fetching it. 583f06ca4afSHartmut Brandt */ 584f06ca4afSHartmut Brandt work.first = 1; 585f06ca4afSHartmut Brandt work.last_change = 0; 586f06ca4afSHartmut Brandt table_init_pdu(descr, &work.pdu); 587f06ca4afSHartmut Brandt 588f06ca4afSHartmut Brandt for (;;) { 589f06ca4afSHartmut Brandt if (snmp_dialog(&work.pdu, &resp)) { 590f06ca4afSHartmut Brandt table_free(&work, 1); 591f06ca4afSHartmut Brandt return (-1); 592f06ca4afSHartmut Brandt } 593f06ca4afSHartmut Brandt if ((ret = table_check_response(&work, &resp)) == 0) { 594f06ca4afSHartmut Brandt snmp_pdu_free(&resp); 595f06ca4afSHartmut Brandt break; 596f06ca4afSHartmut Brandt } 597f06ca4afSHartmut Brandt if (ret == -1) { 598f06ca4afSHartmut Brandt snmp_pdu_free(&resp); 599f06ca4afSHartmut Brandt table_free(&work, 1); 600f06ca4afSHartmut Brandt return (-1); 601f06ca4afSHartmut Brandt } 602f06ca4afSHartmut Brandt if (ret == -2) { 603f06ca4afSHartmut Brandt snmp_pdu_free(&resp); 604f06ca4afSHartmut Brandt goto again; 605f06ca4afSHartmut Brandt } 606f06ca4afSHartmut Brandt 607f06ca4afSHartmut Brandt work.pdu.bindings[work.pdu.nbindings - 1].var = 608f06ca4afSHartmut Brandt resp.bindings[resp.nbindings - 1].var; 609f06ca4afSHartmut Brandt 610f06ca4afSHartmut Brandt snmp_pdu_free(&resp); 611f06ca4afSHartmut Brandt } 612f06ca4afSHartmut Brandt 613f06ca4afSHartmut Brandt if ((ret = table_check_cons(&work)) == -1) { 614f06ca4afSHartmut Brandt table_free(&work, 1); 615f06ca4afSHartmut Brandt return (-1); 616f06ca4afSHartmut Brandt } 617f06ca4afSHartmut Brandt if (ret == -2) { 618f06ca4afSHartmut Brandt table_free(&work, 1); 619f06ca4afSHartmut Brandt goto again; 620f06ca4afSHartmut Brandt } 621f06ca4afSHartmut Brandt /* 622f06ca4afSHartmut Brandt * Free index list 623f06ca4afSHartmut Brandt */ 624f06ca4afSHartmut Brandt table_free(&work, 0); 625f06ca4afSHartmut Brandt return (0); 626f06ca4afSHartmut Brandt } 627f06ca4afSHartmut Brandt 628f06ca4afSHartmut Brandt /* 629f06ca4afSHartmut Brandt * Callback for table 630f06ca4afSHartmut Brandt */ 631f06ca4afSHartmut Brandt static void 632f06ca4afSHartmut Brandt table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) 633f06ca4afSHartmut Brandt { 634f06ca4afSHartmut Brandt struct tabwork *work = arg; 635f06ca4afSHartmut Brandt int ret; 636f06ca4afSHartmut Brandt 637f06ca4afSHartmut Brandt if (resp == NULL) { 638f06ca4afSHartmut Brandt /* timeout */ 63969292cedSHartmut Brandt seterr(&snmp_client, "no response to fetch table request"); 640f06ca4afSHartmut Brandt table_free(work, 1); 641f06ca4afSHartmut Brandt work->callback(work->table, work->arg, -1); 642f06ca4afSHartmut Brandt free(work); 643f06ca4afSHartmut Brandt return; 644f06ca4afSHartmut Brandt } 645f06ca4afSHartmut Brandt 646f06ca4afSHartmut Brandt if ((ret = table_check_response(work, resp)) == 0) { 647f06ca4afSHartmut Brandt /* EOT */ 648f06ca4afSHartmut Brandt snmp_pdu_free(resp); 649f06ca4afSHartmut Brandt 650f06ca4afSHartmut Brandt if ((ret = table_check_cons(work)) == -1) { 651f06ca4afSHartmut Brandt /* error happend */ 652f06ca4afSHartmut Brandt table_free(work, 1); 653f06ca4afSHartmut Brandt work->callback(work->table, work->arg, -1); 654f06ca4afSHartmut Brandt free(work); 655f06ca4afSHartmut Brandt return; 656f06ca4afSHartmut Brandt } 657f06ca4afSHartmut Brandt if (ret == -2) { 658f06ca4afSHartmut Brandt /* restart */ 659f06ca4afSHartmut Brandt again: 660f06ca4afSHartmut Brandt table_free(work, 1); 661f06ca4afSHartmut Brandt work->first = 1; 662f06ca4afSHartmut Brandt work->last_change = 0; 663f06ca4afSHartmut Brandt table_init_pdu(work->descr, &work->pdu); 664f06ca4afSHartmut Brandt if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 665f06ca4afSHartmut Brandt work->callback(work->table, work->arg, -1); 666f06ca4afSHartmut Brandt free(work); 667f06ca4afSHartmut Brandt return; 668f06ca4afSHartmut Brandt } 669f06ca4afSHartmut Brandt return; 670f06ca4afSHartmut Brandt } 671f06ca4afSHartmut Brandt /* 672f06ca4afSHartmut Brandt * Free index list 673f06ca4afSHartmut Brandt */ 674f06ca4afSHartmut Brandt table_free(work, 0); 675f06ca4afSHartmut Brandt work->callback(work->table, work->arg, 0); 676f06ca4afSHartmut Brandt free(work); 677f06ca4afSHartmut Brandt return; 678f06ca4afSHartmut Brandt } 679f06ca4afSHartmut Brandt 680f06ca4afSHartmut Brandt if (ret == -1) { 681f06ca4afSHartmut Brandt /* error */ 682f06ca4afSHartmut Brandt snmp_pdu_free(resp); 683f06ca4afSHartmut Brandt table_free(work, 1); 684f06ca4afSHartmut Brandt work->callback(work->table, work->arg, -1); 685f06ca4afSHartmut Brandt free(work); 686f06ca4afSHartmut Brandt return; 687f06ca4afSHartmut Brandt } 688f06ca4afSHartmut Brandt 689f06ca4afSHartmut Brandt if (ret == -2) { 690f06ca4afSHartmut Brandt /* again */ 691f06ca4afSHartmut Brandt snmp_pdu_free(resp); 692f06ca4afSHartmut Brandt goto again; 693f06ca4afSHartmut Brandt } 694f06ca4afSHartmut Brandt 695f06ca4afSHartmut Brandt /* next part */ 696f06ca4afSHartmut Brandt 697f06ca4afSHartmut Brandt work->pdu.bindings[work->pdu.nbindings - 1].var = 698f06ca4afSHartmut Brandt resp->bindings[resp->nbindings - 1].var; 699f06ca4afSHartmut Brandt 700f06ca4afSHartmut Brandt snmp_pdu_free(resp); 701f06ca4afSHartmut Brandt 702f06ca4afSHartmut Brandt if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 703f06ca4afSHartmut Brandt table_free(work, 1); 704f06ca4afSHartmut Brandt work->callback(work->table, work->arg, -1); 705f06ca4afSHartmut Brandt free(work); 706f06ca4afSHartmut Brandt return; 707f06ca4afSHartmut Brandt } 708f06ca4afSHartmut Brandt } 709f06ca4afSHartmut Brandt 710f06ca4afSHartmut Brandt int 711f06ca4afSHartmut Brandt snmp_table_fetch_async(const struct snmp_table *descr, void *list, 712f06ca4afSHartmut Brandt snmp_table_cb_f func, void *arg) 713f06ca4afSHartmut Brandt { 714f06ca4afSHartmut Brandt struct tabwork *work; 715f06ca4afSHartmut Brandt 716f06ca4afSHartmut Brandt if ((work = malloc(sizeof(*work))) == NULL) { 71769292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 718f06ca4afSHartmut Brandt return (-1); 719f06ca4afSHartmut Brandt } 720f06ca4afSHartmut Brandt 721f06ca4afSHartmut Brandt work->descr = descr; 722f06ca4afSHartmut Brandt work->table = (struct table *)list; 723f06ca4afSHartmut Brandt work->iter = 0; 724f06ca4afSHartmut Brandt TAILQ_INIT(work->table); 725f06ca4afSHartmut Brandt TAILQ_INIT(&work->worklist); 726f06ca4afSHartmut Brandt 727f06ca4afSHartmut Brandt work->callback = func; 728f06ca4afSHartmut Brandt work->arg = arg; 729f06ca4afSHartmut Brandt 730f06ca4afSHartmut Brandt /* 731f06ca4afSHartmut Brandt * Start by sending the first PDU 732f06ca4afSHartmut Brandt */ 733f06ca4afSHartmut Brandt work->first = 1; 734f06ca4afSHartmut Brandt work->last_change = 0; 735f06ca4afSHartmut Brandt table_init_pdu(descr, &work->pdu); 736f06ca4afSHartmut Brandt 7373daec7aeSEnji Cooper if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { 7383daec7aeSEnji Cooper free(work); 7393daec7aeSEnji Cooper work = NULL; 740f06ca4afSHartmut Brandt return (-1); 7413daec7aeSEnji Cooper } 742f06ca4afSHartmut Brandt return (0); 743f06ca4afSHartmut Brandt } 744f06ca4afSHartmut Brandt 745f06ca4afSHartmut Brandt /* 746f06ca4afSHartmut Brandt * Append an index to an oid 747f06ca4afSHartmut Brandt */ 748f06ca4afSHartmut Brandt int 749f06ca4afSHartmut Brandt snmp_oid_append(struct asn_oid *oid, const char *fmt, ...) 750f06ca4afSHartmut Brandt { 751f06ca4afSHartmut Brandt va_list va; 752f06ca4afSHartmut Brandt int size; 753f06ca4afSHartmut Brandt char *nextptr; 754f06ca4afSHartmut Brandt const u_char *str; 755f06ca4afSHartmut Brandt size_t len; 756f06ca4afSHartmut Brandt struct in_addr ina; 757f06ca4afSHartmut Brandt int ret; 758f06ca4afSHartmut Brandt 759f06ca4afSHartmut Brandt va_start(va, fmt); 760f06ca4afSHartmut Brandt 761f06ca4afSHartmut Brandt size = 0; 762f06ca4afSHartmut Brandt 763f06ca4afSHartmut Brandt ret = 0; 764f06ca4afSHartmut Brandt while (*fmt != '\0') { 765f06ca4afSHartmut Brandt switch (*fmt++) { 766f06ca4afSHartmut Brandt case 'i': 767f06ca4afSHartmut Brandt /* just an integer more */ 768f06ca4afSHartmut Brandt if (oid->len + 1 > ASN_MAXOIDLEN) { 769f06ca4afSHartmut Brandt warnx("%s: OID too long for integer", __func__); 770f06ca4afSHartmut Brandt ret = -1; 771f06ca4afSHartmut Brandt break; 772f06ca4afSHartmut Brandt } 773f06ca4afSHartmut Brandt oid->subs[oid->len++] = va_arg(va, asn_subid_t); 774f06ca4afSHartmut Brandt break; 775f06ca4afSHartmut Brandt 776f06ca4afSHartmut Brandt case 'a': 777f06ca4afSHartmut Brandt /* append an IP address */ 778f06ca4afSHartmut Brandt if (oid->len + 4 > ASN_MAXOIDLEN) { 779f06ca4afSHartmut Brandt warnx("%s: OID too long for ip-addr", __func__); 780f06ca4afSHartmut Brandt ret = -1; 781f06ca4afSHartmut Brandt break; 782f06ca4afSHartmut Brandt } 783f06ca4afSHartmut Brandt ina = va_arg(va, struct in_addr); 784f06ca4afSHartmut Brandt ina.s_addr = ntohl(ina.s_addr); 785f06ca4afSHartmut Brandt oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; 786f06ca4afSHartmut Brandt oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; 787f06ca4afSHartmut Brandt oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; 788f06ca4afSHartmut Brandt oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; 789f06ca4afSHartmut Brandt break; 790f06ca4afSHartmut Brandt 791f06ca4afSHartmut Brandt case 's': 792f06ca4afSHartmut Brandt /* append a null-terminated string, 793f06ca4afSHartmut Brandt * length is computed */ 794f06ca4afSHartmut Brandt str = (const u_char *)va_arg(va, const char *); 795f06ca4afSHartmut Brandt len = strlen((const char *)str); 796f06ca4afSHartmut Brandt if (oid->len + len + 1 > ASN_MAXOIDLEN) { 797f06ca4afSHartmut Brandt warnx("%s: OID too long for string", __func__); 798f06ca4afSHartmut Brandt ret = -1; 799f06ca4afSHartmut Brandt break; 800f06ca4afSHartmut Brandt } 801f06ca4afSHartmut Brandt oid->subs[oid->len++] = len; 802f06ca4afSHartmut Brandt while (len--) 803f06ca4afSHartmut Brandt oid->subs[oid->len++] = *str++; 804f06ca4afSHartmut Brandt break; 805f06ca4afSHartmut Brandt 806f06ca4afSHartmut Brandt case '(': 807f06ca4afSHartmut Brandt /* the integer value between ( and ) is stored 808f06ca4afSHartmut Brandt * in size */ 809f06ca4afSHartmut Brandt size = strtol(fmt, &nextptr, 10); 810f06ca4afSHartmut Brandt if (*nextptr != ')') 811f06ca4afSHartmut Brandt abort(); 812f06ca4afSHartmut Brandt fmt = ++nextptr; 813f06ca4afSHartmut Brandt break; 814f06ca4afSHartmut Brandt 815f06ca4afSHartmut Brandt case 'b': 816f06ca4afSHartmut Brandt /* append `size` characters */ 817f06ca4afSHartmut Brandt str = (const u_char *)va_arg(va, const char *); 818f06ca4afSHartmut Brandt if (oid->len + size > ASN_MAXOIDLEN) { 819f06ca4afSHartmut Brandt warnx("%s: OID too long for string", __func__); 820f06ca4afSHartmut Brandt ret = -1; 821f06ca4afSHartmut Brandt break; 822f06ca4afSHartmut Brandt } 823f06ca4afSHartmut Brandt while (size--) 824f06ca4afSHartmut Brandt oid->subs[oid->len++] = *str++; 825f06ca4afSHartmut Brandt break; 826f06ca4afSHartmut Brandt 827f06ca4afSHartmut Brandt case 'c': 828f06ca4afSHartmut Brandt /* get size and the octets from the arguments */ 829f06ca4afSHartmut Brandt size = va_arg(va, size_t); 830f06ca4afSHartmut Brandt str = va_arg(va, const u_char *); 831f06ca4afSHartmut Brandt if (oid->len + size + 1 > ASN_MAXOIDLEN) { 832f06ca4afSHartmut Brandt warnx("%s: OID too long for string", __func__); 833f06ca4afSHartmut Brandt ret = -1; 834f06ca4afSHartmut Brandt break; 835f06ca4afSHartmut Brandt } 836f06ca4afSHartmut Brandt oid->subs[oid->len++] = size; 837f06ca4afSHartmut Brandt while (size--) 838f06ca4afSHartmut Brandt oid->subs[oid->len++] = *str++; 839f06ca4afSHartmut Brandt break; 840f06ca4afSHartmut Brandt 841f06ca4afSHartmut Brandt default: 842f06ca4afSHartmut Brandt abort(); 843f06ca4afSHartmut Brandt } 844f06ca4afSHartmut Brandt } 845f06ca4afSHartmut Brandt va_end(va); 846f06ca4afSHartmut Brandt return (ret); 847f06ca4afSHartmut Brandt } 848f06ca4afSHartmut Brandt 849f06ca4afSHartmut Brandt /* 850f06ca4afSHartmut Brandt * Initialize a client structure 851f06ca4afSHartmut Brandt */ 852f06ca4afSHartmut Brandt void 853f06ca4afSHartmut Brandt snmp_client_init(struct snmp_client *c) 854f06ca4afSHartmut Brandt { 855f06ca4afSHartmut Brandt memset(c, 0, sizeof(*c)); 856f06ca4afSHartmut Brandt 857f06ca4afSHartmut Brandt c->version = SNMP_V2c; 85870af00a1SHartmut Brandt c->trans = SNMP_TRANS_UDP; 859f06ca4afSHartmut Brandt c->chost = NULL; 860f06ca4afSHartmut Brandt c->cport = NULL; 861f06ca4afSHartmut Brandt 862f06ca4afSHartmut Brandt strcpy(c->read_community, "public"); 863f06ca4afSHartmut Brandt strcpy(c->write_community, "private"); 864f06ca4afSHartmut Brandt 865135f7de5SShteryana Shopova c->security_model = SNMP_SECMODEL_USM; 866135f7de5SShteryana Shopova strcpy(c->cname, ""); 867135f7de5SShteryana Shopova 868f06ca4afSHartmut Brandt c->timeout.tv_sec = 3; 869f06ca4afSHartmut Brandt c->timeout.tv_usec = 0; 870f06ca4afSHartmut Brandt c->retries = 3; 871f06ca4afSHartmut Brandt c->dump_pdus = 0; 872f06ca4afSHartmut Brandt c->txbuflen = c->rxbuflen = 10000; 873f06ca4afSHartmut Brandt 874f06ca4afSHartmut Brandt c->fd = -1; 875f06ca4afSHartmut Brandt 876f06ca4afSHartmut Brandt c->max_reqid = INT32_MAX; 877f06ca4afSHartmut Brandt c->min_reqid = 0; 878f06ca4afSHartmut Brandt c->next_reqid = 0; 879135f7de5SShteryana Shopova 880135f7de5SShteryana Shopova c->engine.max_msg_size = 1500; /* XXX */ 881f06ca4afSHartmut Brandt } 882f06ca4afSHartmut Brandt 883f06ca4afSHartmut Brandt 884f06ca4afSHartmut Brandt /* 885f06ca4afSHartmut Brandt * Open UDP client socket 886f06ca4afSHartmut Brandt */ 887f06ca4afSHartmut Brandt static int 888f06ca4afSHartmut Brandt open_client_udp(const char *host, const char *port) 889f06ca4afSHartmut Brandt { 890f06ca4afSHartmut Brandt int error; 891f06ca4afSHartmut Brandt char *ptr; 892f06ca4afSHartmut Brandt struct addrinfo hints, *res0, *res; 893f06ca4afSHartmut Brandt 894f06ca4afSHartmut Brandt /* copy host- and portname */ 895f06ca4afSHartmut Brandt if (snmp_client.chost == NULL) { 896f06ca4afSHartmut Brandt if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST))) 897f06ca4afSHartmut Brandt == NULL) { 89869292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 899f06ca4afSHartmut Brandt return (-1); 900f06ca4afSHartmut Brandt } 901f06ca4afSHartmut Brandt strcpy(snmp_client.chost, DEFAULT_HOST); 902f06ca4afSHartmut Brandt } 903f06ca4afSHartmut Brandt if (host != NULL) { 904f06ca4afSHartmut Brandt if ((ptr = malloc(1 + strlen(host))) == NULL) { 90569292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 906f06ca4afSHartmut Brandt return (-1); 907f06ca4afSHartmut Brandt } 908f06ca4afSHartmut Brandt free(snmp_client.chost); 909f06ca4afSHartmut Brandt snmp_client.chost = ptr; 910f06ca4afSHartmut Brandt strcpy(snmp_client.chost, host); 911f06ca4afSHartmut Brandt } 912f06ca4afSHartmut Brandt if (snmp_client.cport == NULL) { 913f06ca4afSHartmut Brandt if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT))) 914f06ca4afSHartmut Brandt == NULL) { 91569292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 916f06ca4afSHartmut Brandt return (-1); 917f06ca4afSHartmut Brandt } 918f06ca4afSHartmut Brandt strcpy(snmp_client.cport, DEFAULT_PORT); 919f06ca4afSHartmut Brandt } 920f06ca4afSHartmut Brandt if (port != NULL) { 921f06ca4afSHartmut Brandt if ((ptr = malloc(1 + strlen(port))) == NULL) { 92269292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 923f06ca4afSHartmut Brandt return (-1); 924f06ca4afSHartmut Brandt } 925f06ca4afSHartmut Brandt free(snmp_client.cport); 926f06ca4afSHartmut Brandt snmp_client.cport = ptr; 927f06ca4afSHartmut Brandt strcpy(snmp_client.cport, port); 928f06ca4afSHartmut Brandt } 929f06ca4afSHartmut Brandt 930f06ca4afSHartmut Brandt /* open connection */ 931f06ca4afSHartmut Brandt memset(&hints, 0, sizeof(hints)); 932f06ca4afSHartmut Brandt hints.ai_flags = AI_CANONNAME; 93304d17814SAndrey V. Elsukov hints.ai_family = snmp_client.trans == SNMP_TRANS_UDP ? AF_INET : 93404d17814SAndrey V. Elsukov AF_INET6; 935f06ca4afSHartmut Brandt hints.ai_socktype = SOCK_DGRAM; 936f06ca4afSHartmut Brandt hints.ai_protocol = 0; 937f06ca4afSHartmut Brandt error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 938f06ca4afSHartmut Brandt if (error != 0) { 93969292cedSHartmut Brandt seterr(&snmp_client, "%s: %s", snmp_client.chost, 94069292cedSHartmut Brandt gai_strerror(error)); 941f06ca4afSHartmut Brandt return (-1); 942f06ca4afSHartmut Brandt } 943f06ca4afSHartmut Brandt res = res0; 944f06ca4afSHartmut Brandt for (;;) { 945f06ca4afSHartmut Brandt if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype, 946f06ca4afSHartmut Brandt res->ai_protocol)) == -1) { 947f06ca4afSHartmut Brandt if ((res = res->ai_next) == NULL) { 94869292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 949f06ca4afSHartmut Brandt freeaddrinfo(res0); 950f06ca4afSHartmut Brandt return (-1); 951f06ca4afSHartmut Brandt } 952f06ca4afSHartmut Brandt } else if (connect(snmp_client.fd, res->ai_addr, 953f06ca4afSHartmut Brandt res->ai_addrlen) == -1) { 954f06ca4afSHartmut Brandt if ((res = res->ai_next) == NULL) { 95569292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 956f06ca4afSHartmut Brandt freeaddrinfo(res0); 9577583dc27SEnji Cooper (void)close(snmp_client.fd); 9587583dc27SEnji Cooper snmp_client.fd = -1; 959f06ca4afSHartmut Brandt return (-1); 960f06ca4afSHartmut Brandt } 961f06ca4afSHartmut Brandt } else 962f06ca4afSHartmut Brandt break; 963f06ca4afSHartmut Brandt } 964f06ca4afSHartmut Brandt freeaddrinfo(res0); 965f06ca4afSHartmut Brandt return (0); 966f06ca4afSHartmut Brandt } 967f06ca4afSHartmut Brandt 968f06ca4afSHartmut Brandt static void 969f06ca4afSHartmut Brandt remove_local(void) 970f06ca4afSHartmut Brandt { 971f06ca4afSHartmut Brandt (void)remove(snmp_client.local_path); 972f06ca4afSHartmut Brandt } 973f06ca4afSHartmut Brandt 974f06ca4afSHartmut Brandt /* 975f06ca4afSHartmut Brandt * Open local socket 976f06ca4afSHartmut Brandt */ 977f06ca4afSHartmut Brandt static int 978f06ca4afSHartmut Brandt open_client_local(const char *path) 979f06ca4afSHartmut Brandt { 980f06ca4afSHartmut Brandt struct sockaddr_un sa; 981f06ca4afSHartmut Brandt char *ptr; 98270af00a1SHartmut Brandt int stype; 983f06ca4afSHartmut Brandt 984f06ca4afSHartmut Brandt if (snmp_client.chost == NULL) { 985f06ca4afSHartmut Brandt if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) 986f06ca4afSHartmut Brandt == NULL) { 98769292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 988f06ca4afSHartmut Brandt return (-1); 989f06ca4afSHartmut Brandt } 990f06ca4afSHartmut Brandt strcpy(snmp_client.chost, DEFAULT_LOCAL); 991f06ca4afSHartmut Brandt } 992f06ca4afSHartmut Brandt if (path != NULL) { 993f06ca4afSHartmut Brandt if ((ptr = malloc(1 + strlen(path))) == NULL) { 99469292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 995f06ca4afSHartmut Brandt return (-1); 996f06ca4afSHartmut Brandt } 997f06ca4afSHartmut Brandt free(snmp_client.chost); 998f06ca4afSHartmut Brandt snmp_client.chost = ptr; 999f06ca4afSHartmut Brandt strcpy(snmp_client.chost, path); 1000f06ca4afSHartmut Brandt } 1001f06ca4afSHartmut Brandt 100270af00a1SHartmut Brandt if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) 100370af00a1SHartmut Brandt stype = SOCK_DGRAM; 100470af00a1SHartmut Brandt else 100570af00a1SHartmut Brandt stype = SOCK_STREAM; 100670af00a1SHartmut Brandt 100770af00a1SHartmut Brandt if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { 100869292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1009f06ca4afSHartmut Brandt return (-1); 1010f06ca4afSHartmut Brandt } 1011f06ca4afSHartmut Brandt 1012f06ca4afSHartmut Brandt snprintf(snmp_client.local_path, sizeof(snmp_client.local_path), 1013f06ca4afSHartmut Brandt "%s", SNMP_LOCAL_PATH); 1014f06ca4afSHartmut Brandt 101581e0e7b9STom Jones if (mkstemp(snmp_client.local_path) == -1) { 101669292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1017f06ca4afSHartmut Brandt (void)close(snmp_client.fd); 1018f06ca4afSHartmut Brandt snmp_client.fd = -1; 1019f06ca4afSHartmut Brandt return (-1); 1020f06ca4afSHartmut Brandt } 1021f06ca4afSHartmut Brandt 1022f06ca4afSHartmut Brandt sa.sun_family = AF_LOCAL; 1023f06ca4afSHartmut Brandt sa.sun_len = sizeof(sa); 1024f06ca4afSHartmut Brandt strcpy(sa.sun_path, snmp_client.local_path); 1025f06ca4afSHartmut Brandt 1026f06ca4afSHartmut Brandt if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 102769292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1028f06ca4afSHartmut Brandt (void)close(snmp_client.fd); 1029f06ca4afSHartmut Brandt snmp_client.fd = -1; 1030f06ca4afSHartmut Brandt (void)remove(snmp_client.local_path); 1031f06ca4afSHartmut Brandt return (-1); 1032f06ca4afSHartmut Brandt } 1033f06ca4afSHartmut Brandt atexit(remove_local); 1034f06ca4afSHartmut Brandt 1035f06ca4afSHartmut Brandt sa.sun_family = AF_LOCAL; 1036f06ca4afSHartmut Brandt sa.sun_len = offsetof(struct sockaddr_un, sun_path) + 1037f06ca4afSHartmut Brandt strlen(snmp_client.chost); 1038f06ca4afSHartmut Brandt strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1); 1039f06ca4afSHartmut Brandt sa.sun_path[sizeof(sa.sun_path) - 1] = '\0'; 1040f06ca4afSHartmut Brandt 1041f06ca4afSHartmut Brandt if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) { 104269292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1043f06ca4afSHartmut Brandt (void)close(snmp_client.fd); 1044f06ca4afSHartmut Brandt snmp_client.fd = -1; 1045f06ca4afSHartmut Brandt (void)remove(snmp_client.local_path); 1046f06ca4afSHartmut Brandt return (-1); 1047f06ca4afSHartmut Brandt } 1048f06ca4afSHartmut Brandt return (0); 1049f06ca4afSHartmut Brandt } 1050f06ca4afSHartmut Brandt 1051f06ca4afSHartmut Brandt /* 1052f06ca4afSHartmut Brandt * SNMP_OPEN 1053f06ca4afSHartmut Brandt */ 1054f06ca4afSHartmut Brandt int 1055f06ca4afSHartmut Brandt snmp_open(const char *host, const char *port, const char *readcomm, 1056f06ca4afSHartmut Brandt const char *writecomm) 1057f06ca4afSHartmut Brandt { 1058f06ca4afSHartmut Brandt struct timeval tout; 1059f06ca4afSHartmut Brandt 1060f06ca4afSHartmut Brandt /* still open ? */ 1061f06ca4afSHartmut Brandt if (snmp_client.fd != -1) { 1062f06ca4afSHartmut Brandt errno = EBUSY; 106369292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1064f06ca4afSHartmut Brandt return (-1); 1065f06ca4afSHartmut Brandt } 1066f06ca4afSHartmut Brandt 1067f06ca4afSHartmut Brandt /* copy community strings */ 1068f06ca4afSHartmut Brandt if (readcomm != NULL) 1069f06ca4afSHartmut Brandt strlcpy(snmp_client.read_community, readcomm, 1070f06ca4afSHartmut Brandt sizeof(snmp_client.read_community)); 1071f06ca4afSHartmut Brandt if (writecomm != NULL) 1072f06ca4afSHartmut Brandt strlcpy(snmp_client.write_community, writecomm, 1073f06ca4afSHartmut Brandt sizeof(snmp_client.write_community)); 1074f06ca4afSHartmut Brandt 107570af00a1SHartmut Brandt switch (snmp_client.trans) { 107670af00a1SHartmut Brandt 107770af00a1SHartmut Brandt case SNMP_TRANS_UDP: 107804d17814SAndrey V. Elsukov case SNMP_TRANS_UDP6: 1079295685c5SEnji Cooper if (open_client_udp(host, port) != 0) 1080f06ca4afSHartmut Brandt return (-1); 108170af00a1SHartmut Brandt break; 108270af00a1SHartmut Brandt 108370af00a1SHartmut Brandt case SNMP_TRANS_LOC_DGRAM: 108470af00a1SHartmut Brandt case SNMP_TRANS_LOC_STREAM: 1085295685c5SEnji Cooper if (open_client_local(host) != 0) 1086f06ca4afSHartmut Brandt return (-1); 108770af00a1SHartmut Brandt break; 108870af00a1SHartmut Brandt 108970af00a1SHartmut Brandt default: 109069292cedSHartmut Brandt seterr(&snmp_client, "bad transport mapping"); 109170af00a1SHartmut Brandt return (-1); 1092f06ca4afSHartmut Brandt } 1093f06ca4afSHartmut Brandt tout.tv_sec = 0; 1094f06ca4afSHartmut Brandt tout.tv_usec = 0; 1095f06ca4afSHartmut Brandt if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO, 1096f06ca4afSHartmut Brandt &tout, sizeof(struct timeval)) == -1) { 109769292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1098f06ca4afSHartmut Brandt (void)close(snmp_client.fd); 1099f06ca4afSHartmut Brandt snmp_client.fd = -1; 110070af00a1SHartmut Brandt if (snmp_client.local_path[0] != '\0') 1101f06ca4afSHartmut Brandt (void)remove(snmp_client.local_path); 1102f06ca4afSHartmut Brandt return (-1); 1103f06ca4afSHartmut Brandt } 1104f06ca4afSHartmut Brandt 1105f06ca4afSHartmut Brandt /* initialize list */ 1106f06ca4afSHartmut Brandt LIST_INIT(&sent_pdus); 1107f06ca4afSHartmut Brandt 1108f06ca4afSHartmut Brandt return (0); 1109f06ca4afSHartmut Brandt } 1110f06ca4afSHartmut Brandt 1111f06ca4afSHartmut Brandt 1112f06ca4afSHartmut Brandt /* 1113f06ca4afSHartmut Brandt * SNMP_CLOSE 1114f06ca4afSHartmut Brandt * 1115f06ca4afSHartmut Brandt * closes connection to snmp server 1116f06ca4afSHartmut Brandt * - function cannot fail 1117f06ca4afSHartmut Brandt * - clears connection 1118f06ca4afSHartmut Brandt * - clears list of sent pdus 1119f06ca4afSHartmut Brandt * 1120f06ca4afSHartmut Brandt * input: 1121f06ca4afSHartmut Brandt * void 1122f06ca4afSHartmut Brandt * return: 1123f06ca4afSHartmut Brandt * void 1124f06ca4afSHartmut Brandt */ 1125f06ca4afSHartmut Brandt void 1126f06ca4afSHartmut Brandt snmp_close(void) 1127f06ca4afSHartmut Brandt { 1128f06ca4afSHartmut Brandt struct sent_pdu *p1; 1129f06ca4afSHartmut Brandt 1130f06ca4afSHartmut Brandt if (snmp_client.fd != -1) { 1131f06ca4afSHartmut Brandt (void)close(snmp_client.fd); 1132f06ca4afSHartmut Brandt snmp_client.fd = -1; 113370af00a1SHartmut Brandt if (snmp_client.local_path[0] != '\0') 1134f06ca4afSHartmut Brandt (void)remove(snmp_client.local_path); 1135f06ca4afSHartmut Brandt } 1136f06ca4afSHartmut Brandt while(!LIST_EMPTY(&sent_pdus)){ 1137f06ca4afSHartmut Brandt p1 = LIST_FIRST(&sent_pdus); 1138f06ca4afSHartmut Brandt if (p1->timeout_id != NULL) 1139f06ca4afSHartmut Brandt snmp_client.timeout_stop(p1->timeout_id); 1140f06ca4afSHartmut Brandt LIST_REMOVE(p1, entries); 1141f06ca4afSHartmut Brandt free(p1); 1142f06ca4afSHartmut Brandt } 1143f06ca4afSHartmut Brandt free(snmp_client.chost); 1144f06ca4afSHartmut Brandt free(snmp_client.cport); 1145f06ca4afSHartmut Brandt } 1146f06ca4afSHartmut Brandt 1147f06ca4afSHartmut Brandt /* 1148f06ca4afSHartmut Brandt * initialize a snmp_pdu structure 1149f06ca4afSHartmut Brandt */ 1150f06ca4afSHartmut Brandt void 1151f06ca4afSHartmut Brandt snmp_pdu_create(struct snmp_pdu *pdu, u_int op) 1152f06ca4afSHartmut Brandt { 1153f06ca4afSHartmut Brandt memset(pdu, 0, sizeof(struct snmp_pdu)); 1154135f7de5SShteryana Shopova 1155f06ca4afSHartmut Brandt if (op == SNMP_PDU_SET) 1156f06ca4afSHartmut Brandt strlcpy(pdu->community, snmp_client.write_community, 1157f06ca4afSHartmut Brandt sizeof(pdu->community)); 1158f06ca4afSHartmut Brandt else 1159f06ca4afSHartmut Brandt strlcpy(pdu->community, snmp_client.read_community, 1160f06ca4afSHartmut Brandt sizeof(pdu->community)); 1161f06ca4afSHartmut Brandt 1162f06ca4afSHartmut Brandt pdu->type = op; 1163f06ca4afSHartmut Brandt pdu->version = snmp_client.version; 1164f06ca4afSHartmut Brandt pdu->error_status = 0; 1165f06ca4afSHartmut Brandt pdu->error_index = 0; 1166f06ca4afSHartmut Brandt pdu->nbindings = 0; 1167135f7de5SShteryana Shopova 1168135f7de5SShteryana Shopova if (snmp_client.version != SNMP_V3) 1169135f7de5SShteryana Shopova return; 1170135f7de5SShteryana Shopova 1171135f7de5SShteryana Shopova pdu->identifier = ++snmp_client.identifier; 1172135f7de5SShteryana Shopova pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; 1173135f7de5SShteryana Shopova pdu->flags = 0; 1174135f7de5SShteryana Shopova pdu->security_model = snmp_client.security_model; 1175135f7de5SShteryana Shopova 117672cd7a52SShteryana Shopova if (snmp_client.security_model == SNMP_SECMODEL_USM) { 117772cd7a52SShteryana Shopova memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 117872cd7a52SShteryana Shopova memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 117972cd7a52SShteryana Shopova snmp_pdu_init_secparams(pdu); 118072cd7a52SShteryana Shopova } else 1181135f7de5SShteryana Shopova seterr(&snmp_client, "unknown security model"); 1182135f7de5SShteryana Shopova 1183135f7de5SShteryana Shopova if (snmp_client.clen > 0) { 1184135f7de5SShteryana Shopova memcpy(pdu->context_engine, snmp_client.cengine, 1185135f7de5SShteryana Shopova snmp_client.clen); 1186135f7de5SShteryana Shopova pdu->context_engine_len = snmp_client.clen; 1187135f7de5SShteryana Shopova } else { 1188135f7de5SShteryana Shopova memcpy(pdu->context_engine, snmp_client.engine.engine_id, 1189135f7de5SShteryana Shopova snmp_client.engine.engine_len); 1190135f7de5SShteryana Shopova pdu->context_engine_len = snmp_client.engine.engine_len; 1191135f7de5SShteryana Shopova } 1192135f7de5SShteryana Shopova 1193135f7de5SShteryana Shopova strlcpy(pdu->context_name, snmp_client.cname, 1194135f7de5SShteryana Shopova sizeof(pdu->context_name)); 1195f06ca4afSHartmut Brandt } 1196f06ca4afSHartmut Brandt 1197f06ca4afSHartmut Brandt /* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */ 1198f06ca4afSHartmut Brandt /* added 10/04/02 by kek: check for MAX_BINDINGS */ 1199f06ca4afSHartmut Brandt int 1200f06ca4afSHartmut Brandt snmp_add_binding(struct snmp_v1_pdu *pdu, ...) 1201f06ca4afSHartmut Brandt { 1202f06ca4afSHartmut Brandt va_list ap; 1203f06ca4afSHartmut Brandt const struct asn_oid *oid; 1204f06ca4afSHartmut Brandt u_int ret; 1205f06ca4afSHartmut Brandt 1206f06ca4afSHartmut Brandt va_start(ap, pdu); 1207f06ca4afSHartmut Brandt 1208f06ca4afSHartmut Brandt ret = pdu->nbindings; 1209f06ca4afSHartmut Brandt while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) { 1210f06ca4afSHartmut Brandt if (pdu->nbindings >= SNMP_MAX_BINDINGS){ 1211f06ca4afSHartmut Brandt va_end(ap); 1212f06ca4afSHartmut Brandt return (-1); 1213f06ca4afSHartmut Brandt } 1214f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].var = *oid; 1215f06ca4afSHartmut Brandt pdu->bindings[pdu->nbindings].syntax = 1216f06ca4afSHartmut Brandt va_arg(ap, enum snmp_syntax); 1217f06ca4afSHartmut Brandt pdu->nbindings++; 1218f06ca4afSHartmut Brandt } 1219f06ca4afSHartmut Brandt va_end(ap); 1220f06ca4afSHartmut Brandt return (ret); 1221f06ca4afSHartmut Brandt } 1222f06ca4afSHartmut Brandt 1223f06ca4afSHartmut Brandt 1224f06ca4afSHartmut Brandt static int32_t 1225f06ca4afSHartmut Brandt snmp_next_reqid(struct snmp_client * c) 1226f06ca4afSHartmut Brandt { 1227f06ca4afSHartmut Brandt int32_t i; 1228f06ca4afSHartmut Brandt 1229f06ca4afSHartmut Brandt i = c->next_reqid; 1230f06ca4afSHartmut Brandt if (c->next_reqid >= c->max_reqid) 1231f06ca4afSHartmut Brandt c->next_reqid = c->min_reqid; 1232f06ca4afSHartmut Brandt else 1233f06ca4afSHartmut Brandt c->next_reqid++; 1234f06ca4afSHartmut Brandt return (i); 1235f06ca4afSHartmut Brandt } 1236f06ca4afSHartmut Brandt 1237f06ca4afSHartmut Brandt /* 1238f06ca4afSHartmut Brandt * Send request and return request id. 1239f06ca4afSHartmut Brandt */ 1240f06ca4afSHartmut Brandt static int32_t 1241f06ca4afSHartmut Brandt snmp_send_packet(struct snmp_pdu * pdu) 1242f06ca4afSHartmut Brandt { 1243f06ca4afSHartmut Brandt u_char *buf; 1244f06ca4afSHartmut Brandt struct asn_buf b; 1245f06ca4afSHartmut Brandt ssize_t ret; 1246f06ca4afSHartmut Brandt 12472e590d59SEnji Cooper if ((buf = calloc(1, snmp_client.txbuflen)) == NULL) { 124869292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1249f06ca4afSHartmut Brandt return (-1); 1250f06ca4afSHartmut Brandt } 1251f06ca4afSHartmut Brandt 1252f06ca4afSHartmut Brandt pdu->request_id = snmp_next_reqid(&snmp_client); 1253f06ca4afSHartmut Brandt 1254f06ca4afSHartmut Brandt b.asn_ptr = buf; 1255f06ca4afSHartmut Brandt b.asn_len = snmp_client.txbuflen; 1256f06ca4afSHartmut Brandt if (snmp_pdu_encode(pdu, &b)) { 125769292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1258f06ca4afSHartmut Brandt free(buf); 1259f06ca4afSHartmut Brandt return (-1); 1260f06ca4afSHartmut Brandt } 1261f06ca4afSHartmut Brandt 1262f06ca4afSHartmut Brandt if (snmp_client.dump_pdus) 1263f06ca4afSHartmut Brandt snmp_pdu_dump(pdu); 1264f06ca4afSHartmut Brandt 1265f06ca4afSHartmut Brandt if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) { 126669292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1267f06ca4afSHartmut Brandt free(buf); 1268f06ca4afSHartmut Brandt return (-1); 1269f06ca4afSHartmut Brandt } 1270f06ca4afSHartmut Brandt free(buf); 1271f06ca4afSHartmut Brandt 12722e590d59SEnji Cooper return (pdu->request_id); 1273f06ca4afSHartmut Brandt } 1274f06ca4afSHartmut Brandt 1275f06ca4afSHartmut Brandt /* 1276f06ca4afSHartmut Brandt * to be called when a snmp request timed out 1277f06ca4afSHartmut Brandt */ 1278f06ca4afSHartmut Brandt static void 1279f06ca4afSHartmut Brandt snmp_timeout(void * listentry_ptr) 1280f06ca4afSHartmut Brandt { 1281f06ca4afSHartmut Brandt struct sent_pdu *listentry = listentry_ptr; 1282f06ca4afSHartmut Brandt 1283f06ca4afSHartmut Brandt #if 0 1284f06ca4afSHartmut Brandt warnx("snmp request %i timed out, attempt (%i/%i)", 1285f06ca4afSHartmut Brandt listentry->reqid, listentry->retrycount, snmp_client.retries); 1286f06ca4afSHartmut Brandt #endif 1287f06ca4afSHartmut Brandt 1288f06ca4afSHartmut Brandt listentry->retrycount++; 1289f06ca4afSHartmut Brandt if (listentry->retrycount > snmp_client.retries) { 1290f06ca4afSHartmut Brandt /* there is no answer at all */ 1291f06ca4afSHartmut Brandt LIST_REMOVE(listentry, entries); 1292f06ca4afSHartmut Brandt listentry->callback(listentry->pdu, NULL, listentry->arg); 1293f06ca4afSHartmut Brandt free(listentry); 1294f06ca4afSHartmut Brandt } else { 1295f06ca4afSHartmut Brandt /* try again */ 1296f06ca4afSHartmut Brandt /* new request with new request ID */ 1297f06ca4afSHartmut Brandt listentry->reqid = snmp_send_packet(listentry->pdu); 1298f06ca4afSHartmut Brandt listentry->timeout_id = 1299f06ca4afSHartmut Brandt snmp_client.timeout_start(&snmp_client.timeout, 1300f06ca4afSHartmut Brandt snmp_timeout, listentry); 1301f06ca4afSHartmut Brandt } 1302f06ca4afSHartmut Brandt } 1303f06ca4afSHartmut Brandt 1304f06ca4afSHartmut Brandt int32_t 1305f06ca4afSHartmut Brandt snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg) 1306f06ca4afSHartmut Brandt { 1307f06ca4afSHartmut Brandt struct sent_pdu *listentry; 1308f06ca4afSHartmut Brandt int32_t id; 1309f06ca4afSHartmut Brandt 1310f06ca4afSHartmut Brandt if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) { 131169292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1312f06ca4afSHartmut Brandt return (-1); 1313f06ca4afSHartmut Brandt } 1314f06ca4afSHartmut Brandt 1315f06ca4afSHartmut Brandt /* here we really send */ 1316f06ca4afSHartmut Brandt if ((id = snmp_send_packet(pdu)) == -1) { 1317f06ca4afSHartmut Brandt free(listentry); 1318f06ca4afSHartmut Brandt return (-1); 1319f06ca4afSHartmut Brandt } 1320f06ca4afSHartmut Brandt 1321f06ca4afSHartmut Brandt /* add entry to list of sent PDUs */ 1322f06ca4afSHartmut Brandt listentry->pdu = pdu; 1323f06ca4afSHartmut Brandt if (gettimeofday(&listentry->time, NULL) == -1) 1324f06ca4afSHartmut Brandt warn("gettimeofday() failed"); 1325f06ca4afSHartmut Brandt 1326f06ca4afSHartmut Brandt listentry->reqid = pdu->request_id; 1327f06ca4afSHartmut Brandt listentry->callback = func; 1328f06ca4afSHartmut Brandt listentry->arg = arg; 1329f06ca4afSHartmut Brandt listentry->retrycount=1; 1330f06ca4afSHartmut Brandt listentry->timeout_id = 1331f06ca4afSHartmut Brandt snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout, 1332f06ca4afSHartmut Brandt listentry); 1333f06ca4afSHartmut Brandt 1334f06ca4afSHartmut Brandt LIST_INSERT_HEAD(&sent_pdus, listentry, entries); 1335f06ca4afSHartmut Brandt 1336f06ca4afSHartmut Brandt return (id); 1337f06ca4afSHartmut Brandt } 1338f06ca4afSHartmut Brandt 1339f06ca4afSHartmut Brandt /* 1340f06ca4afSHartmut Brandt * Receive an SNMP packet. 1341f06ca4afSHartmut Brandt * 1342f06ca4afSHartmut Brandt * tv controls how we wait for a packet: if tv is a NULL pointer, 1343f06ca4afSHartmut Brandt * the receive blocks forever, if tv points to a structure with all 1344f06ca4afSHartmut Brandt * members 0 the socket is polled, in all other cases tv specifies the 1345f06ca4afSHartmut Brandt * maximum time to wait for a packet. 1346f06ca4afSHartmut Brandt * 1347f06ca4afSHartmut Brandt * Return: 1348f06ca4afSHartmut Brandt * -1 on errors 1349f06ca4afSHartmut Brandt * 0 on timeout 1350f06ca4afSHartmut Brandt * +1 if packet received 1351f06ca4afSHartmut Brandt */ 1352f06ca4afSHartmut Brandt static int 1353f06ca4afSHartmut Brandt snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv) 1354f06ca4afSHartmut Brandt { 1355f06ca4afSHartmut Brandt int dopoll, setpoll; 1356f06ca4afSHartmut Brandt int flags; 1357f06ca4afSHartmut Brandt int saved_errno; 1358f06ca4afSHartmut Brandt u_char *buf; 1359f06ca4afSHartmut Brandt int ret; 1360f06ca4afSHartmut Brandt struct asn_buf abuf; 1361f06ca4afSHartmut Brandt int32_t ip; 1362896052c1SHartmut Brandt #ifdef bsdi 1363896052c1SHartmut Brandt int optlen; 1364896052c1SHartmut Brandt #else 1365f06ca4afSHartmut Brandt socklen_t optlen; 1366896052c1SHartmut Brandt #endif 1367f06ca4afSHartmut Brandt 13682e590d59SEnji Cooper if ((buf = calloc(1, snmp_client.rxbuflen)) == NULL) { 136969292cedSHartmut Brandt seterr(&snmp_client, "%s", strerror(errno)); 1370f06ca4afSHartmut Brandt return (-1); 1371f06ca4afSHartmut Brandt } 1372f06ca4afSHartmut Brandt dopoll = setpoll = 0; 1373f06ca4afSHartmut Brandt flags = 0; 1374f06ca4afSHartmut Brandt if (tv != NULL) { 1375f06ca4afSHartmut Brandt /* poll or timeout */ 1376f06ca4afSHartmut Brandt if (tv->tv_sec != 0 || tv->tv_usec != 0) { 1377f06ca4afSHartmut Brandt /* wait with timeout */ 1378f06ca4afSHartmut Brandt if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1379f06ca4afSHartmut Brandt tv, sizeof(*tv)) == -1) { 138069292cedSHartmut Brandt seterr(&snmp_client, "setsockopt: %s", 138169292cedSHartmut Brandt strerror(errno)); 1382f06ca4afSHartmut Brandt free(buf); 1383f06ca4afSHartmut Brandt return (-1); 1384f06ca4afSHartmut Brandt } 1385f06ca4afSHartmut Brandt optlen = sizeof(*tv); 1386f06ca4afSHartmut Brandt if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1387f06ca4afSHartmut Brandt tv, &optlen) == -1) { 138869292cedSHartmut Brandt seterr(&snmp_client, "getsockopt: %s", 138969292cedSHartmut Brandt strerror(errno)); 1390f06ca4afSHartmut Brandt free(buf); 1391f06ca4afSHartmut Brandt return (-1); 1392f06ca4afSHartmut Brandt } 1393f06ca4afSHartmut Brandt /* at this point tv_sec and tv_usec may appear 1394f06ca4afSHartmut Brandt * as 0. This happens for timeouts lesser than 1395f06ca4afSHartmut Brandt * the clock granularity. The kernel rounds these to 1396f06ca4afSHartmut Brandt * 0 and this would result in a blocking receive. 1397f06ca4afSHartmut Brandt * Instead of an else we check tv_sec and tv_usec 1398f06ca4afSHartmut Brandt * again below and if this rounding happens, 1399f06ca4afSHartmut Brandt * switch to a polling receive. */ 1400f06ca4afSHartmut Brandt } 1401f06ca4afSHartmut Brandt if (tv->tv_sec == 0 && tv->tv_usec == 0) { 1402f06ca4afSHartmut Brandt /* poll */ 1403f06ca4afSHartmut Brandt dopoll = 1; 1404f06ca4afSHartmut Brandt if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) { 140569292cedSHartmut Brandt seterr(&snmp_client, "fcntl: %s", 140669292cedSHartmut Brandt strerror(errno)); 1407f06ca4afSHartmut Brandt free(buf); 1408f06ca4afSHartmut Brandt return (-1); 1409f06ca4afSHartmut Brandt } 1410f06ca4afSHartmut Brandt if (!(flags & O_NONBLOCK)) { 1411f06ca4afSHartmut Brandt setpoll = 1; 1412f06ca4afSHartmut Brandt flags |= O_NONBLOCK; 1413f06ca4afSHartmut Brandt if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) { 141469292cedSHartmut Brandt seterr(&snmp_client, "fcntl: %s", 141569292cedSHartmut Brandt strerror(errno)); 1416f06ca4afSHartmut Brandt free(buf); 1417f06ca4afSHartmut Brandt return (-1); 1418f06ca4afSHartmut Brandt } 1419f06ca4afSHartmut Brandt } 1420f06ca4afSHartmut Brandt } 1421f06ca4afSHartmut Brandt } 1422f06ca4afSHartmut Brandt ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0); 1423f06ca4afSHartmut Brandt saved_errno = errno; 1424f06ca4afSHartmut Brandt if (tv != NULL) { 1425f06ca4afSHartmut Brandt if (dopoll) { 1426f06ca4afSHartmut Brandt if (setpoll) { 1427f06ca4afSHartmut Brandt flags &= ~O_NONBLOCK; 1428f06ca4afSHartmut Brandt (void)fcntl(snmp_client.fd, F_SETFL, flags); 1429f06ca4afSHartmut Brandt } 1430f06ca4afSHartmut Brandt } else { 1431f06ca4afSHartmut Brandt tv->tv_sec = 0; 1432f06ca4afSHartmut Brandt tv->tv_usec = 0; 1433f06ca4afSHartmut Brandt (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, 1434f06ca4afSHartmut Brandt tv, sizeof(*tv)); 1435f06ca4afSHartmut Brandt } 1436f06ca4afSHartmut Brandt } 1437f06ca4afSHartmut Brandt if (ret == -1) { 1438f06ca4afSHartmut Brandt free(buf); 1439f06ca4afSHartmut Brandt if (errno == EAGAIN || errno == EWOULDBLOCK) 1440f06ca4afSHartmut Brandt return (0); 144169292cedSHartmut Brandt seterr(&snmp_client, "recv: %s", strerror(saved_errno)); 1442f06ca4afSHartmut Brandt return (-1); 1443f06ca4afSHartmut Brandt } 1444896052c1SHartmut Brandt if (ret == 0) { 1445896052c1SHartmut Brandt /* this happens when we have a streaming socket and the 1446896052c1SHartmut Brandt * remote side has closed it */ 1447896052c1SHartmut Brandt free(buf); 144869292cedSHartmut Brandt seterr(&snmp_client, "recv: socket closed by peer"); 1449896052c1SHartmut Brandt errno = EPIPE; 1450896052c1SHartmut Brandt return (-1); 1451896052c1SHartmut Brandt } 1452f06ca4afSHartmut Brandt 1453f06ca4afSHartmut Brandt abuf.asn_ptr = buf; 1454f06ca4afSHartmut Brandt abuf.asn_len = ret; 1455f06ca4afSHartmut Brandt 1456135f7de5SShteryana Shopova memset(pdu, 0, sizeof(*pdu)); 145772cd7a52SShteryana Shopova if (snmp_client.security_model == SNMP_SECMODEL_USM) { 145872cd7a52SShteryana Shopova memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); 145972cd7a52SShteryana Shopova memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); 146072cd7a52SShteryana Shopova snmp_pdu_init_secparams(pdu); 146172cd7a52SShteryana Shopova } 1462135f7de5SShteryana Shopova 1463f06ca4afSHartmut Brandt if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { 146469292cedSHartmut Brandt seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret); 1465f06ca4afSHartmut Brandt free(buf); 1466f06ca4afSHartmut Brandt return (-1); 1467f06ca4afSHartmut Brandt } 1468135f7de5SShteryana Shopova 1469f06ca4afSHartmut Brandt free(buf); 1470f06ca4afSHartmut Brandt if (snmp_client.dump_pdus) 1471f06ca4afSHartmut Brandt snmp_pdu_dump(pdu); 1472f06ca4afSHartmut Brandt 1473135f7de5SShteryana Shopova snmp_client.engine.engine_time = pdu->engine.engine_time; 1474135f7de5SShteryana Shopova snmp_client.engine.engine_boots = pdu->engine.engine_boots; 1475135f7de5SShteryana Shopova 1476f06ca4afSHartmut Brandt return (+1); 1477f06ca4afSHartmut Brandt } 1478f06ca4afSHartmut Brandt 1479f06ca4afSHartmut Brandt static int 1480f06ca4afSHartmut Brandt snmp_deliver_packet(struct snmp_pdu * resp) 1481f06ca4afSHartmut Brandt { 1482f06ca4afSHartmut Brandt struct sent_pdu *listentry; 1483f06ca4afSHartmut Brandt 1484f06ca4afSHartmut Brandt if (resp->type != SNMP_PDU_RESPONSE) { 1485f06ca4afSHartmut Brandt warn("ignoring snmp pdu %u", resp->type); 1486f06ca4afSHartmut Brandt return (-1); 1487f06ca4afSHartmut Brandt } 1488f06ca4afSHartmut Brandt 1489f06ca4afSHartmut Brandt LIST_FOREACH(listentry, &sent_pdus, entries) 1490f06ca4afSHartmut Brandt if (listentry->reqid == resp->request_id) 1491f06ca4afSHartmut Brandt break; 1492f06ca4afSHartmut Brandt if (listentry == NULL) 1493f06ca4afSHartmut Brandt return (-1); 1494f06ca4afSHartmut Brandt 1495f06ca4afSHartmut Brandt LIST_REMOVE(listentry, entries); 1496f06ca4afSHartmut Brandt listentry->callback(listentry->pdu, resp, listentry->arg); 1497f06ca4afSHartmut Brandt 1498f06ca4afSHartmut Brandt snmp_client.timeout_stop(listentry->timeout_id); 1499f06ca4afSHartmut Brandt 1500f06ca4afSHartmut Brandt free(listentry); 1501f06ca4afSHartmut Brandt return (0); 1502f06ca4afSHartmut Brandt } 1503f06ca4afSHartmut Brandt 1504f06ca4afSHartmut Brandt int 1505f06ca4afSHartmut Brandt snmp_receive(int blocking) 1506f06ca4afSHartmut Brandt { 1507f06ca4afSHartmut Brandt int ret; 1508f06ca4afSHartmut Brandt 1509f06ca4afSHartmut Brandt struct timeval tv; 1510f06ca4afSHartmut Brandt struct snmp_pdu * resp; 1511f06ca4afSHartmut Brandt 1512f06ca4afSHartmut Brandt memset(&tv, 0, sizeof(tv)); 1513f06ca4afSHartmut Brandt 1514f06ca4afSHartmut Brandt resp = malloc(sizeof(struct snmp_pdu)); 1515f06ca4afSHartmut Brandt if (resp == NULL) { 151669292cedSHartmut Brandt seterr(&snmp_client, "no memory for returning PDU"); 1517f06ca4afSHartmut Brandt return (-1) ; 1518f06ca4afSHartmut Brandt } 1519f06ca4afSHartmut Brandt 1520f06ca4afSHartmut Brandt if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) { 1521f06ca4afSHartmut Brandt free(resp); 1522f06ca4afSHartmut Brandt return (ret); 1523f06ca4afSHartmut Brandt } 1524f06ca4afSHartmut Brandt ret = snmp_deliver_packet(resp); 1525f06ca4afSHartmut Brandt snmp_pdu_free(resp); 1526f06ca4afSHartmut Brandt free(resp); 1527f06ca4afSHartmut Brandt return (ret); 1528f06ca4afSHartmut Brandt } 1529f06ca4afSHartmut Brandt 1530f06ca4afSHartmut Brandt 1531f06ca4afSHartmut Brandt /* 1532f06ca4afSHartmut Brandt * Check a GETNEXT response. Here we have three possible outcomes: -1 an 1533f06ca4afSHartmut Brandt * unexpected error happened. +1 response is ok and is within the table 0 1534f06ca4afSHartmut Brandt * response is ok, but is behind the table or error is NOSUCHNAME. The req 1535f06ca4afSHartmut Brandt * should point to a template PDU which contains the base OIDs and the 1536f06ca4afSHartmut Brandt * syntaxes. This is really only useful to sweep non-sparse tables. 1537f06ca4afSHartmut Brandt */ 1538f06ca4afSHartmut Brandt static int 1539f06ca4afSHartmut Brandt ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1540f06ca4afSHartmut Brandt { 1541f06ca4afSHartmut Brandt u_int i; 1542f06ca4afSHartmut Brandt 1543f06ca4afSHartmut Brandt if (resp->version != req->version) { 1544f06ca4afSHartmut Brandt warnx("SNMP GETNEXT: response has wrong version"); 1545f06ca4afSHartmut Brandt return (-1); 1546f06ca4afSHartmut Brandt } 1547f06ca4afSHartmut Brandt 1548f06ca4afSHartmut Brandt if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1549f06ca4afSHartmut Brandt return (0); 1550f06ca4afSHartmut Brandt 1551f06ca4afSHartmut Brandt if (resp->error_status != SNMP_ERR_NOERROR) { 1552f06ca4afSHartmut Brandt warnx("SNMP GETNEXT: error %d", resp->error_status); 1553f06ca4afSHartmut Brandt return (-1); 1554f06ca4afSHartmut Brandt } 1555f06ca4afSHartmut Brandt if (resp->nbindings != req->nbindings) { 1556f06ca4afSHartmut Brandt warnx("SNMP GETNEXT: bad number of bindings in response"); 1557f06ca4afSHartmut Brandt return (-1); 1558f06ca4afSHartmut Brandt } 1559f06ca4afSHartmut Brandt for (i = 0; i < req->nbindings; i++) { 1560f06ca4afSHartmut Brandt if (!asn_is_suboid(&req->bindings[i].var, 1561f06ca4afSHartmut Brandt &resp->bindings[i].var)) { 1562f06ca4afSHartmut Brandt if (i != 0) 1563f06ca4afSHartmut Brandt warnx("SNMP GETNEXT: inconsistent table " 1564f06ca4afSHartmut Brandt "response"); 1565f06ca4afSHartmut Brandt return (0); 1566f06ca4afSHartmut Brandt } 1567f06ca4afSHartmut Brandt if (resp->version != SNMP_V1 && 1568f06ca4afSHartmut Brandt resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1569f06ca4afSHartmut Brandt return (0); 1570f06ca4afSHartmut Brandt 1571f06ca4afSHartmut Brandt if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1572f06ca4afSHartmut Brandt warnx("SNMP GETNEXT: bad syntax in response"); 1573f06ca4afSHartmut Brandt return (0); 1574f06ca4afSHartmut Brandt } 1575f06ca4afSHartmut Brandt } 1576f06ca4afSHartmut Brandt return (1); 1577f06ca4afSHartmut Brandt } 1578f06ca4afSHartmut Brandt 1579f06ca4afSHartmut Brandt /* 1580f06ca4afSHartmut Brandt * Check a GET response. Here we have three possible outcomes: -1 an 1581f06ca4afSHartmut Brandt * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should 1582f06ca4afSHartmut Brandt * point to a template PDU which contains the OIDs and the syntaxes. This 1583f06ca4afSHartmut Brandt * is only useful for SNMPv1 or single object GETS. 1584f06ca4afSHartmut Brandt */ 1585f06ca4afSHartmut Brandt static int 1586f06ca4afSHartmut Brandt ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1587f06ca4afSHartmut Brandt { 1588f06ca4afSHartmut Brandt u_int i; 1589f06ca4afSHartmut Brandt 1590f06ca4afSHartmut Brandt if (resp->version != req->version) { 1591f06ca4afSHartmut Brandt warnx("SNMP GET: response has wrong version"); 1592f06ca4afSHartmut Brandt return (-1); 1593f06ca4afSHartmut Brandt } 1594f06ca4afSHartmut Brandt 1595f06ca4afSHartmut Brandt if (resp->error_status == SNMP_ERR_NOSUCHNAME) 1596f06ca4afSHartmut Brandt return (0); 1597f06ca4afSHartmut Brandt 1598f06ca4afSHartmut Brandt if (resp->error_status != SNMP_ERR_NOERROR) { 1599f06ca4afSHartmut Brandt warnx("SNMP GET: error %d", resp->error_status); 1600f06ca4afSHartmut Brandt return (-1); 1601f06ca4afSHartmut Brandt } 1602f06ca4afSHartmut Brandt 1603f06ca4afSHartmut Brandt if (resp->nbindings != req->nbindings) { 1604f06ca4afSHartmut Brandt warnx("SNMP GET: bad number of bindings in response"); 1605f06ca4afSHartmut Brandt return (-1); 1606f06ca4afSHartmut Brandt } 1607f06ca4afSHartmut Brandt for (i = 0; i < req->nbindings; i++) { 1608f06ca4afSHartmut Brandt if (asn_compare_oid(&req->bindings[i].var, 1609f06ca4afSHartmut Brandt &resp->bindings[i].var) != 0) { 1610f06ca4afSHartmut Brandt warnx("SNMP GET: bad OID in response"); 1611f06ca4afSHartmut Brandt return (-1); 1612f06ca4afSHartmut Brandt } 1613f06ca4afSHartmut Brandt if (snmp_client.version != SNMP_V1 && 1614f06ca4afSHartmut Brandt (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || 1615f06ca4afSHartmut Brandt resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) 1616f06ca4afSHartmut Brandt return (0); 1617f06ca4afSHartmut Brandt if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1618f06ca4afSHartmut Brandt warnx("SNMP GET: bad syntax in response"); 1619f06ca4afSHartmut Brandt return (-1); 1620f06ca4afSHartmut Brandt } 1621f06ca4afSHartmut Brandt } 1622f06ca4afSHartmut Brandt return (1); 1623f06ca4afSHartmut Brandt } 1624f06ca4afSHartmut Brandt 1625f06ca4afSHartmut Brandt /* 1626165c5d31SHartmut Brandt * Check the response to a SET PDU. We check: - the error status must be 0 - 1627f06ca4afSHartmut Brandt * the number of bindings must be equal in response and request - the 1628f06ca4afSHartmut Brandt * syntaxes must be the same in response and request - the OIDs must be the 1629f06ca4afSHartmut Brandt * same in response and request 1630f06ca4afSHartmut Brandt */ 1631f06ca4afSHartmut Brandt static int 1632f06ca4afSHartmut Brandt ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp) 1633f06ca4afSHartmut Brandt { 1634f06ca4afSHartmut Brandt u_int i; 1635f06ca4afSHartmut Brandt 1636f06ca4afSHartmut Brandt if (resp->version != req->version) { 1637f06ca4afSHartmut Brandt warnx("SNMP SET: response has wrong version"); 1638f06ca4afSHartmut Brandt return (-1); 1639f06ca4afSHartmut Brandt } 1640f06ca4afSHartmut Brandt 1641f06ca4afSHartmut Brandt if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1642f06ca4afSHartmut Brandt warnx("SNMP SET: error %d", resp->error_status); 1643f06ca4afSHartmut Brandt return (0); 1644f06ca4afSHartmut Brandt } 1645f06ca4afSHartmut Brandt if (resp->error_status != SNMP_ERR_NOERROR) { 1646f06ca4afSHartmut Brandt warnx("SNMP SET: error %d", resp->error_status); 1647f06ca4afSHartmut Brandt return (-1); 1648f06ca4afSHartmut Brandt } 1649f06ca4afSHartmut Brandt 1650f06ca4afSHartmut Brandt if (resp->nbindings != req->nbindings) { 1651f06ca4afSHartmut Brandt warnx("SNMP SET: bad number of bindings in response"); 1652f06ca4afSHartmut Brandt return (-1); 1653f06ca4afSHartmut Brandt } 1654f06ca4afSHartmut Brandt for (i = 0; i < req->nbindings; i++) { 1655f06ca4afSHartmut Brandt if (asn_compare_oid(&req->bindings[i].var, 1656f06ca4afSHartmut Brandt &resp->bindings[i].var) != 0) { 1657f06ca4afSHartmut Brandt warnx("SNMP SET: wrong OID in response to SET"); 1658f06ca4afSHartmut Brandt return (-1); 1659f06ca4afSHartmut Brandt } 1660f06ca4afSHartmut Brandt if (resp->bindings[i].syntax != req->bindings[i].syntax) { 1661f06ca4afSHartmut Brandt warnx("SNMP SET: bad syntax in response"); 1662f06ca4afSHartmut Brandt return (-1); 1663f06ca4afSHartmut Brandt } 1664f06ca4afSHartmut Brandt } 1665f06ca4afSHartmut Brandt return (1); 1666f06ca4afSHartmut Brandt } 1667f06ca4afSHartmut Brandt 1668f06ca4afSHartmut Brandt /* 1669f06ca4afSHartmut Brandt * Simple checks for response PDUs against request PDUs. Return values: 1=ok, 1670f06ca4afSHartmut Brandt * 0=nosuchname or similar, -1=failure, -2=no response at all 1671f06ca4afSHartmut Brandt */ 1672f06ca4afSHartmut Brandt int 1673f06ca4afSHartmut Brandt snmp_pdu_check(const struct snmp_pdu *req, 1674f06ca4afSHartmut Brandt const struct snmp_pdu *resp) 1675f06ca4afSHartmut Brandt { 1676f06ca4afSHartmut Brandt if (resp == NULL) 1677f06ca4afSHartmut Brandt return (-2); 1678f06ca4afSHartmut Brandt 1679f06ca4afSHartmut Brandt switch (req->type) { 1680f06ca4afSHartmut Brandt 1681f06ca4afSHartmut Brandt case SNMP_PDU_GET: 1682f06ca4afSHartmut Brandt return (ok_get(req, resp)); 1683f06ca4afSHartmut Brandt 1684f06ca4afSHartmut Brandt case SNMP_PDU_SET: 1685f06ca4afSHartmut Brandt return (ok_set(req, resp)); 1686f06ca4afSHartmut Brandt 1687f06ca4afSHartmut Brandt case SNMP_PDU_GETNEXT: 1688f06ca4afSHartmut Brandt return (ok_getnext(req, resp)); 1689f06ca4afSHartmut Brandt 1690f06ca4afSHartmut Brandt } 1691f06ca4afSHartmut Brandt errx(1, "%s: bad pdu type %i", __func__, req->type); 1692f06ca4afSHartmut Brandt } 1693f06ca4afSHartmut Brandt 1694f06ca4afSHartmut Brandt int 1695f06ca4afSHartmut Brandt snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp) 1696f06ca4afSHartmut Brandt { 1697f06ca4afSHartmut Brandt struct timeval tv = snmp_client.timeout; 1698f06ca4afSHartmut Brandt struct timeval end; 169969292cedSHartmut Brandt struct snmp_pdu pdu; 17007c254947SEnji Cooper int ret; 17013b9712faSEnji Cooper int32_t reqid; 17023b9712faSEnji Cooper u_int i; 170369292cedSHartmut Brandt 170469292cedSHartmut Brandt /* 170569292cedSHartmut Brandt * Make a copy of the request and replace the syntaxes by NULL 170669292cedSHartmut Brandt * if this is a GET,GETNEXT or GETBULK. 170769292cedSHartmut Brandt */ 170869292cedSHartmut Brandt pdu = *req; 170969292cedSHartmut Brandt if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || 171069292cedSHartmut Brandt pdu.type == SNMP_PDU_GETBULK) { 171169292cedSHartmut Brandt for (i = 0; i < pdu.nbindings; i++) 171269292cedSHartmut Brandt pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; 171369292cedSHartmut Brandt } 1714f06ca4afSHartmut Brandt 1715f06ca4afSHartmut Brandt for (i = 0; i <= snmp_client.retries; i++) { 1716f06ca4afSHartmut Brandt (void)gettimeofday(&end, NULL); 1717f06ca4afSHartmut Brandt timeradd(&end, &snmp_client.timeout, &end); 171869292cedSHartmut Brandt if ((reqid = snmp_send_packet(&pdu)) == -1) 1719f06ca4afSHartmut Brandt return (-1); 1720f06ca4afSHartmut Brandt for (;;) { 1721f06ca4afSHartmut Brandt (void)gettimeofday(&tv, NULL); 1722f06ca4afSHartmut Brandt if (timercmp(&end, &tv, <=)) 1723f06ca4afSHartmut Brandt break; 1724f06ca4afSHartmut Brandt timersub(&end, &tv, &tv); 1725f06ca4afSHartmut Brandt if ((ret = snmp_receive_packet(resp, &tv)) == 0) 1726f06ca4afSHartmut Brandt /* timeout */ 1727f06ca4afSHartmut Brandt break; 1728f06ca4afSHartmut Brandt 1729f06ca4afSHartmut Brandt if (ret > 0) { 1730f06ca4afSHartmut Brandt if (reqid == resp->request_id) 1731f06ca4afSHartmut Brandt return (0); 1732f06ca4afSHartmut Brandt /* not for us */ 1733f06ca4afSHartmut Brandt (void)snmp_deliver_packet(resp); 1734f06ca4afSHartmut Brandt } 1735896052c1SHartmut Brandt if (ret < 0 && errno == EPIPE) 1736896052c1SHartmut Brandt /* stream closed */ 1737896052c1SHartmut Brandt return (-1); 1738f06ca4afSHartmut Brandt } 1739f06ca4afSHartmut Brandt } 1740896052c1SHartmut Brandt errno = ETIMEDOUT; 174169292cedSHartmut Brandt seterr(&snmp_client, "retry count exceeded"); 1742f06ca4afSHartmut Brandt return (-1); 1743f06ca4afSHartmut Brandt } 1744f06ca4afSHartmut Brandt 1745f06ca4afSHartmut Brandt int 1746135f7de5SShteryana Shopova snmp_discover_engine(char *passwd) 1747135f7de5SShteryana Shopova { 1748135f7de5SShteryana Shopova char cname[SNMP_ADM_STR32_SIZ]; 1749135f7de5SShteryana Shopova enum snmp_authentication cap; 1750135f7de5SShteryana Shopova enum snmp_privacy cpp; 1751135f7de5SShteryana Shopova struct snmp_pdu req, resp; 1752135f7de5SShteryana Shopova 1753135f7de5SShteryana Shopova if (snmp_client.version != SNMP_V3) 1754135f7de5SShteryana Shopova seterr(&snmp_client, "wrong version"); 1755135f7de5SShteryana Shopova 1756135f7de5SShteryana Shopova strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); 1757135f7de5SShteryana Shopova cap = snmp_client.user.auth_proto; 1758135f7de5SShteryana Shopova cpp = snmp_client.user.priv_proto; 1759135f7de5SShteryana Shopova 1760135f7de5SShteryana Shopova snmp_client.engine.engine_len = 0; 1761135f7de5SShteryana Shopova snmp_client.engine.engine_boots = 0; 1762135f7de5SShteryana Shopova snmp_client.engine.engine_time = 0; 1763135f7de5SShteryana Shopova snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; 1764135f7de5SShteryana Shopova snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; 1765135f7de5SShteryana Shopova memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); 1766135f7de5SShteryana Shopova 1767135f7de5SShteryana Shopova snmp_pdu_create(&req, SNMP_PDU_GET); 1768135f7de5SShteryana Shopova 1769135f7de5SShteryana Shopova if (snmp_dialog(&req, &resp) == -1) 1770135f7de5SShteryana Shopova return (-1); 1771135f7de5SShteryana Shopova 1772135f7de5SShteryana Shopova if (resp.version != req.version) { 1773135f7de5SShteryana Shopova seterr(&snmp_client, "wrong version"); 1774135f7de5SShteryana Shopova return (-1); 1775135f7de5SShteryana Shopova } 1776135f7de5SShteryana Shopova 1777135f7de5SShteryana Shopova if (resp.error_status != SNMP_ERR_NOERROR) { 17784731124cSGordon Bergling seterr(&snmp_client, "Error %d in response", resp.error_status); 1779135f7de5SShteryana Shopova return (-1); 1780135f7de5SShteryana Shopova } 1781135f7de5SShteryana Shopova 1782135f7de5SShteryana Shopova snmp_client.engine.engine_len = resp.engine.engine_len; 1783135f7de5SShteryana Shopova snmp_client.engine.max_msg_size = resp.engine.max_msg_size; 1784135f7de5SShteryana Shopova memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, 1785135f7de5SShteryana Shopova resp.engine.engine_len); 1786135f7de5SShteryana Shopova 1787135f7de5SShteryana Shopova strlcpy(snmp_client.user.sec_name, cname, 1788135f7de5SShteryana Shopova sizeof(snmp_client.user.sec_name)); 1789135f7de5SShteryana Shopova snmp_client.user.auth_proto = cap; 1790135f7de5SShteryana Shopova snmp_client.user.priv_proto = cpp; 1791135f7de5SShteryana Shopova 1792135f7de5SShteryana Shopova if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) 1793135f7de5SShteryana Shopova return (0); 1794135f7de5SShteryana Shopova 1795*4dc1820aSGleb Smirnoff if (passwd == NULL || strlen(passwd) == 0 || 1796135f7de5SShteryana Shopova snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || 1797135f7de5SShteryana Shopova snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, 1798135f7de5SShteryana Shopova snmp_client.engine.engine_len) != SNMP_CODE_OK) 1799135f7de5SShteryana Shopova return (-1); 1800135f7de5SShteryana Shopova 1801135f7de5SShteryana Shopova if (resp.engine.engine_boots != 0) 1802135f7de5SShteryana Shopova snmp_client.engine.engine_boots = resp.engine.engine_boots; 1803135f7de5SShteryana Shopova 1804135f7de5SShteryana Shopova if (resp.engine.engine_time != 0) { 1805135f7de5SShteryana Shopova snmp_client.engine.engine_time = resp.engine.engine_time; 1806135f7de5SShteryana Shopova return (0); 1807135f7de5SShteryana Shopova } 1808135f7de5SShteryana Shopova 18091cc49661SEnji Cooper snmp_pdu_free(&req); 18101cc49661SEnji Cooper 1811135f7de5SShteryana Shopova snmp_pdu_create(&req, SNMP_PDU_GET); 1812135f7de5SShteryana Shopova req.engine.engine_boots = 0; 1813135f7de5SShteryana Shopova req.engine.engine_time = 0; 1814135f7de5SShteryana Shopova 1815135f7de5SShteryana Shopova if (snmp_dialog(&req, &resp) == -1) 1816135f7de5SShteryana Shopova return (-1); 1817135f7de5SShteryana Shopova 1818135f7de5SShteryana Shopova if (resp.version != req.version) { 1819135f7de5SShteryana Shopova seterr(&snmp_client, "wrong version"); 1820135f7de5SShteryana Shopova return (-1); 1821135f7de5SShteryana Shopova } 1822135f7de5SShteryana Shopova 1823135f7de5SShteryana Shopova if (resp.error_status != SNMP_ERR_NOERROR) { 18244731124cSGordon Bergling seterr(&snmp_client, "Error %d in response", resp.error_status); 1825135f7de5SShteryana Shopova return (-1); 1826135f7de5SShteryana Shopova } 1827135f7de5SShteryana Shopova 1828135f7de5SShteryana Shopova snmp_client.engine.engine_boots = resp.engine.engine_boots; 1829135f7de5SShteryana Shopova snmp_client.engine.engine_time = resp.engine.engine_time; 1830135f7de5SShteryana Shopova 18311cc49661SEnji Cooper snmp_pdu_free(&req); 18321cc49661SEnji Cooper snmp_pdu_free(&resp); 18331cc49661SEnji Cooper 1834135f7de5SShteryana Shopova return (0); 1835135f7de5SShteryana Shopova } 1836135f7de5SShteryana Shopova 1837135f7de5SShteryana Shopova int 1838f06ca4afSHartmut Brandt snmp_client_set_host(struct snmp_client *cl, const char *h) 1839f06ca4afSHartmut Brandt { 1840f06ca4afSHartmut Brandt char *np; 1841f06ca4afSHartmut Brandt 1842f06ca4afSHartmut Brandt if (h == NULL) { 1843f06ca4afSHartmut Brandt if (cl->chost != NULL) 1844f06ca4afSHartmut Brandt free(cl->chost); 1845f06ca4afSHartmut Brandt cl->chost = NULL; 1846f06ca4afSHartmut Brandt } else { 1847f06ca4afSHartmut Brandt if ((np = malloc(strlen(h) + 1)) == NULL) 1848f06ca4afSHartmut Brandt return (-1); 1849f06ca4afSHartmut Brandt strcpy(np, h); 1850f06ca4afSHartmut Brandt if (cl->chost != NULL) 1851f06ca4afSHartmut Brandt free(cl->chost); 1852f06ca4afSHartmut Brandt cl->chost = np; 1853f06ca4afSHartmut Brandt } 1854f06ca4afSHartmut Brandt return (0); 1855f06ca4afSHartmut Brandt } 1856f06ca4afSHartmut Brandt 1857f06ca4afSHartmut Brandt int 1858f06ca4afSHartmut Brandt snmp_client_set_port(struct snmp_client *cl, const char *p) 1859f06ca4afSHartmut Brandt { 1860f06ca4afSHartmut Brandt char *np; 1861f06ca4afSHartmut Brandt 1862f06ca4afSHartmut Brandt if (p == NULL) { 1863f06ca4afSHartmut Brandt if (cl->cport != NULL) 1864f06ca4afSHartmut Brandt free(cl->cport); 1865f06ca4afSHartmut Brandt cl->cport = NULL; 1866f06ca4afSHartmut Brandt } else { 1867f06ca4afSHartmut Brandt if ((np = malloc(strlen(p) + 1)) == NULL) 1868f06ca4afSHartmut Brandt return (-1); 1869f06ca4afSHartmut Brandt strcpy(np, p); 1870f06ca4afSHartmut Brandt if (cl->cport != NULL) 1871f06ca4afSHartmut Brandt free(cl->cport); 1872f06ca4afSHartmut Brandt cl->cport = np; 1873f06ca4afSHartmut Brandt } 1874f06ca4afSHartmut Brandt return (0); 1875f06ca4afSHartmut Brandt } 187669292cedSHartmut Brandt 187748e091f9SAndrey V. Elsukov static const char *const trans_list[] = { 187848e091f9SAndrey V. Elsukov [SNMP_TRANS_UDP] = "udp::", 187948e091f9SAndrey V. Elsukov [SNMP_TRANS_LOC_DGRAM] = "dgram::", 188048e091f9SAndrey V. Elsukov [SNMP_TRANS_LOC_STREAM] = "stream::", 188148e091f9SAndrey V. Elsukov [SNMP_TRANS_UDP6] = "udp6::", 188248e091f9SAndrey V. Elsukov }; 188348e091f9SAndrey V. Elsukov 188404d17814SAndrey V. Elsukov /** 188504d17814SAndrey V. Elsukov * Try to get a transport identifier which is a leading alphanumeric string 188648e091f9SAndrey V. Elsukov * terminated by a double colon. The string may not be empty. The transport 18870bf56da3SHartmut Brandt * identifier is optional. Unknown transport identifiers are reject. 18880bf56da3SHartmut Brandt * Be careful: a double colon can also occur in a numeric IPv6 address. 188969292cedSHartmut Brandt * 189004d17814SAndrey V. Elsukov * \param sc client struct to set errors 189104d17814SAndrey V. Elsukov * \param strp possible start of transport; updated to point to 189204d17814SAndrey V. Elsukov * the next character to parse 189304d17814SAndrey V. Elsukov * 189448e091f9SAndrey V. Elsukov * \return transport identifier 189504d17814SAndrey V. Elsukov */ 189648e091f9SAndrey V. Elsukov static inline int 189704d17814SAndrey V. Elsukov get_transp(struct snmp_client *sc, const char **strp) 189804d17814SAndrey V. Elsukov { 189948e091f9SAndrey V. Elsukov const char *p; 190048e091f9SAndrey V. Elsukov size_t i; 190104d17814SAndrey V. Elsukov 190248e091f9SAndrey V. Elsukov for (i = 0; i < nitems(trans_list); i++) { 190348e091f9SAndrey V. Elsukov p = strstr(*strp, trans_list[i]); 190448e091f9SAndrey V. Elsukov if (p == *strp) { 190548e091f9SAndrey V. Elsukov *strp += strlen(trans_list[i]); 190648e091f9SAndrey V. Elsukov return ((int)i); 190704d17814SAndrey V. Elsukov } 190804d17814SAndrey V. Elsukov } 190948e091f9SAndrey V. Elsukov 19100bf56da3SHartmut Brandt p = strstr(*strp, "::"); 19110bf56da3SHartmut Brandt if (p == *strp) { 191204d17814SAndrey V. Elsukov seterr(sc, "empty transport specifier"); 191348e091f9SAndrey V. Elsukov return (-1); 191404d17814SAndrey V. Elsukov } 19150bf56da3SHartmut Brandt if (p == NULL) 191648e091f9SAndrey V. Elsukov /* by default assume UDP */ 191748e091f9SAndrey V. Elsukov return (SNMP_TRANS_UDP); 19180bf56da3SHartmut Brandt 19190bf56da3SHartmut Brandt /* ignore :: after [ */ 19200bf56da3SHartmut Brandt const char *ob = strchr(*strp, '['); 19210bf56da3SHartmut Brandt if (ob != NULL && p > ob) 19220bf56da3SHartmut Brandt /* by default assume UDP */ 19230bf56da3SHartmut Brandt return (SNMP_TRANS_UDP); 19240bf56da3SHartmut Brandt 19250bf56da3SHartmut Brandt seterr(sc, "unknown transport specifier '%.*s'", p - *strp, *strp); 19260bf56da3SHartmut Brandt return (-1); 192704d17814SAndrey V. Elsukov } 192804d17814SAndrey V. Elsukov 192904d17814SAndrey V. Elsukov /** 193004d17814SAndrey V. Elsukov * Try to get community string. Eat everything up to the last @ (if there is 193104d17814SAndrey V. Elsukov * any) but only if it is not longer than SNMP_COMMUNITY_MAXLEN. Empty 193204d17814SAndrey V. Elsukov * community strings are legal. 193304d17814SAndrey V. Elsukov * 193404d17814SAndrey V. Elsukov * \param sc client struct to set errors 193504d17814SAndrey V. Elsukov * \param strp possible start of community; updated to the point to 193604d17814SAndrey V. Elsukov * the next character to parse 193704d17814SAndrey V. Elsukov * 193804d17814SAndrey V. Elsukov * \return end of community; equals *strp if there is none; NULL if there 193904d17814SAndrey V. Elsukov * was an error 194004d17814SAndrey V. Elsukov */ 194104d17814SAndrey V. Elsukov static inline const char * 194204d17814SAndrey V. Elsukov get_comm(struct snmp_client *sc, const char **strp) 194304d17814SAndrey V. Elsukov { 194404d17814SAndrey V. Elsukov const char *p = strrchr(*strp, '@'); 194504d17814SAndrey V. Elsukov 194604d17814SAndrey V. Elsukov if (p == NULL) 194704d17814SAndrey V. Elsukov /* no community string */ 194804d17814SAndrey V. Elsukov return (*strp); 194904d17814SAndrey V. Elsukov 195004d17814SAndrey V. Elsukov if (p - *strp > SNMP_COMMUNITY_MAXLEN) { 195104d17814SAndrey V. Elsukov seterr(sc, "community string too long '%.*s'", 195204d17814SAndrey V. Elsukov p - *strp, *strp); 195304d17814SAndrey V. Elsukov return (NULL); 195404d17814SAndrey V. Elsukov } 195504d17814SAndrey V. Elsukov 195604d17814SAndrey V. Elsukov *strp = p + 1; 195704d17814SAndrey V. Elsukov return (p); 195804d17814SAndrey V. Elsukov } 195904d17814SAndrey V. Elsukov 196004d17814SAndrey V. Elsukov /** 196104d17814SAndrey V. Elsukov * Try to get an IPv6 address. This starts with an [ and should end with an ] 196204d17814SAndrey V. Elsukov * and everything between should be not longer than INET6_ADDRSTRLEN and 196304d17814SAndrey V. Elsukov * parseable by inet_pton(). 196404d17814SAndrey V. Elsukov * 196504d17814SAndrey V. Elsukov * \param sc client struct to set errors 196604d17814SAndrey V. Elsukov * \param strp possible start of IPv6 address (the '['); updated to point to 196704d17814SAndrey V. Elsukov * the next character to parse (the one after the closing ']') 196804d17814SAndrey V. Elsukov * 196904d17814SAndrey V. Elsukov * \return end of address (equals *strp + 1 if there is none) or NULL 197004d17814SAndrey V. Elsukov * on errors 197104d17814SAndrey V. Elsukov */ 197204d17814SAndrey V. Elsukov static inline const char * 197304d17814SAndrey V. Elsukov get_ipv6(struct snmp_client *sc, const char **strp) 197404d17814SAndrey V. Elsukov { 197504d17814SAndrey V. Elsukov char str[INET6_ADDRSTRLEN + IF_NAMESIZE]; 197604d17814SAndrey V. Elsukov struct addrinfo hints, *res; 197704d17814SAndrey V. Elsukov int error; 197804d17814SAndrey V. Elsukov 197904d17814SAndrey V. Elsukov if (**strp != '[') 198004d17814SAndrey V. Elsukov return (*strp + 1); 198104d17814SAndrey V. Elsukov 198204d17814SAndrey V. Elsukov const char *p = *strp + 1; 198304d17814SAndrey V. Elsukov while (*p != ']' ) { 198404d17814SAndrey V. Elsukov if (*p == '\0') { 198504d17814SAndrey V. Elsukov seterr(sc, "unterminated IPv6 address '%.*s'", 198604d17814SAndrey V. Elsukov p - *strp, *strp); 198704d17814SAndrey V. Elsukov return (NULL); 198804d17814SAndrey V. Elsukov } 198904d17814SAndrey V. Elsukov p++; 199004d17814SAndrey V. Elsukov } 199104d17814SAndrey V. Elsukov 199204d17814SAndrey V. Elsukov if (p - *strp > INET6_ADDRSTRLEN + IF_NAMESIZE) { 199304d17814SAndrey V. Elsukov seterr(sc, "IPv6 address too long '%.*s'", p - *strp, *strp); 199404d17814SAndrey V. Elsukov return (NULL); 199504d17814SAndrey V. Elsukov } 199604d17814SAndrey V. Elsukov 199704d17814SAndrey V. Elsukov strncpy(str, *strp + 1, p - (*strp + 1)); 199804d17814SAndrey V. Elsukov str[p - (*strp + 1)] = '\0'; 199904d17814SAndrey V. Elsukov 200004d17814SAndrey V. Elsukov memset(&hints, 0, sizeof(hints)); 200104d17814SAndrey V. Elsukov hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST; 200204d17814SAndrey V. Elsukov hints.ai_family = AF_INET6; 200304d17814SAndrey V. Elsukov hints.ai_socktype = SOCK_DGRAM; 200404d17814SAndrey V. Elsukov hints.ai_protocol = IPPROTO_UDP; 200504d17814SAndrey V. Elsukov error = getaddrinfo(str, NULL, &hints, &res); 200604d17814SAndrey V. Elsukov if (error != 0) { 200704d17814SAndrey V. Elsukov seterr(sc, "%s: %s", str, gai_strerror(error)); 200804d17814SAndrey V. Elsukov return (NULL); 200904d17814SAndrey V. Elsukov } 201004d17814SAndrey V. Elsukov freeaddrinfo(res); 201104d17814SAndrey V. Elsukov *strp = p + 1; 201204d17814SAndrey V. Elsukov return (p); 201304d17814SAndrey V. Elsukov } 201404d17814SAndrey V. Elsukov 201504d17814SAndrey V. Elsukov /** 201604d17814SAndrey V. Elsukov * Try to get an IPv4 address. This starts with a digit and consists of digits 201704d17814SAndrey V. Elsukov * and dots, is not longer INET_ADDRSTRLEN and must be parseable by 201804d17814SAndrey V. Elsukov * inet_aton(). 201904d17814SAndrey V. Elsukov * 202004d17814SAndrey V. Elsukov * \param sc client struct to set errors 202104d17814SAndrey V. Elsukov * \param strp possible start of IPv4 address; updated to point to the 202204d17814SAndrey V. Elsukov * next character to parse 202304d17814SAndrey V. Elsukov * 202404d17814SAndrey V. Elsukov * \return end of address (equals *strp if there is none) or NULL 202504d17814SAndrey V. Elsukov * on errors 202604d17814SAndrey V. Elsukov */ 202704d17814SAndrey V. Elsukov static inline const char * 202804d17814SAndrey V. Elsukov get_ipv4(struct snmp_client *sc, const char **strp) 202904d17814SAndrey V. Elsukov { 203004d17814SAndrey V. Elsukov const char *p = *strp; 203104d17814SAndrey V. Elsukov 203204d17814SAndrey V. Elsukov while (isascii(*p) && (isdigit(*p) || *p == '.')) 203304d17814SAndrey V. Elsukov p++; 203404d17814SAndrey V. Elsukov 203504d17814SAndrey V. Elsukov if (p - *strp > INET_ADDRSTRLEN) { 203604d17814SAndrey V. Elsukov seterr(sc, "IPv4 address too long '%.*s'", p - *strp, *strp); 203704d17814SAndrey V. Elsukov return (NULL); 203804d17814SAndrey V. Elsukov } 203904d17814SAndrey V. Elsukov if (*strp == p) 204004d17814SAndrey V. Elsukov return *strp; 204104d17814SAndrey V. Elsukov 204204d17814SAndrey V. Elsukov char str[INET_ADDRSTRLEN + 1]; 204304d17814SAndrey V. Elsukov strncpy(str, *strp, p - *strp); 204404d17814SAndrey V. Elsukov str[p - *strp] = '\0'; 204504d17814SAndrey V. Elsukov 204604d17814SAndrey V. Elsukov struct in_addr addr; 204704d17814SAndrey V. Elsukov if (inet_aton(str, &addr) != 1) { 204804d17814SAndrey V. Elsukov seterr(sc, "illegal IPv4 address '%s'", str); 204904d17814SAndrey V. Elsukov return (NULL); 205004d17814SAndrey V. Elsukov } 205104d17814SAndrey V. Elsukov 205204d17814SAndrey V. Elsukov *strp = p; 205304d17814SAndrey V. Elsukov return (p); 205404d17814SAndrey V. Elsukov } 205504d17814SAndrey V. Elsukov 205604d17814SAndrey V. Elsukov /** 205704d17814SAndrey V. Elsukov * Try to get a hostname. This includes everything up to but not including 205804d17814SAndrey V. Elsukov * the last colon (if any). There is no length restriction. 205904d17814SAndrey V. Elsukov * 206004d17814SAndrey V. Elsukov * \param sc client struct to set errors 206104d17814SAndrey V. Elsukov * \param strp possible start of hostname; updated to point to the next 206204d17814SAndrey V. Elsukov * character to parse (the trailing NUL character or the last 206304d17814SAndrey V. Elsukov * colon) 206404d17814SAndrey V. Elsukov * 206504d17814SAndrey V. Elsukov * \return end of address (equals *strp if there is none) 206604d17814SAndrey V. Elsukov */ 206704d17814SAndrey V. Elsukov static inline const char * 206804d17814SAndrey V. Elsukov get_host(struct snmp_client *sc __unused, const char **strp) 206904d17814SAndrey V. Elsukov { 207004d17814SAndrey V. Elsukov const char *p = strrchr(*strp, ':'); 207104d17814SAndrey V. Elsukov 207204d17814SAndrey V. Elsukov if (p == NULL) { 207304d17814SAndrey V. Elsukov *strp += strlen(*strp); 207404d17814SAndrey V. Elsukov return (*strp); 207504d17814SAndrey V. Elsukov } 207604d17814SAndrey V. Elsukov 207704d17814SAndrey V. Elsukov *strp = p; 207804d17814SAndrey V. Elsukov return (p); 207904d17814SAndrey V. Elsukov } 208004d17814SAndrey V. Elsukov 208104d17814SAndrey V. Elsukov /** 208204d17814SAndrey V. Elsukov * Try to get a port number. This start with a colon and extends to the end 208304d17814SAndrey V. Elsukov * of string. The port number must not be empty. 208404d17814SAndrey V. Elsukov * 208504d17814SAndrey V. Elsukov * \param sc client struct to set errors 208604d17814SAndrey V. Elsukov * \param strp possible start of port specification; if this points to a 208704d17814SAndrey V. Elsukov * colon there is a port specification 208804d17814SAndrey V. Elsukov * 208904d17814SAndrey V. Elsukov * \return end of port number (equals *strp if there is none); NULL 209004d17814SAndrey V. Elsukov * if there is no port number 209104d17814SAndrey V. Elsukov */ 209204d17814SAndrey V. Elsukov static inline const char * 209304d17814SAndrey V. Elsukov get_port(struct snmp_client *sc, const char **strp) 209404d17814SAndrey V. Elsukov { 209504d17814SAndrey V. Elsukov if (**strp != ':') 209604d17814SAndrey V. Elsukov return (*strp + 1); 209704d17814SAndrey V. Elsukov 209804d17814SAndrey V. Elsukov if ((*strp)[1] == '\0') { 209904d17814SAndrey V. Elsukov seterr(sc, "empty port name"); 210004d17814SAndrey V. Elsukov return (NULL); 210104d17814SAndrey V. Elsukov } 210204d17814SAndrey V. Elsukov 210304d17814SAndrey V. Elsukov *strp += strlen(*strp); 210404d17814SAndrey V. Elsukov return (*strp); 210504d17814SAndrey V. Elsukov } 210604d17814SAndrey V. Elsukov 210704d17814SAndrey V. Elsukov /** 210804d17814SAndrey V. Elsukov * Save the string in the range given by two pointers. 210904d17814SAndrey V. Elsukov * 211004d17814SAndrey V. Elsukov * \param sc client struct to set errors 211104d17814SAndrey V. Elsukov * \param s begin and end pointers 211204d17814SAndrey V. Elsukov * 211304d17814SAndrey V. Elsukov * \return freshly allocated copy of the string between s[0] and s[1] 211404d17814SAndrey V. Elsukov */ 211504d17814SAndrey V. Elsukov static inline char * 211604d17814SAndrey V. Elsukov save_str(struct snmp_client *sc, const char *const s[2]) 211704d17814SAndrey V. Elsukov { 211804d17814SAndrey V. Elsukov char *m; 211904d17814SAndrey V. Elsukov 212004d17814SAndrey V. Elsukov if ((m = malloc(s[1] - s[0] + 1)) == NULL) { 212104d17814SAndrey V. Elsukov seterr(sc, "%s: %s", __func__, strerror(errno)); 212204d17814SAndrey V. Elsukov return (NULL); 212304d17814SAndrey V. Elsukov } 212404d17814SAndrey V. Elsukov strncpy(m, s[0], s[1] - s[0]); 212504d17814SAndrey V. Elsukov m[s[1] - s[0]] = '\0'; 212604d17814SAndrey V. Elsukov 212704d17814SAndrey V. Elsukov return (m); 212804d17814SAndrey V. Elsukov } 212904d17814SAndrey V. Elsukov 213004d17814SAndrey V. Elsukov /** 213104d17814SAndrey V. Elsukov * Parse a server specification. All parts are optional: 213204d17814SAndrey V. Elsukov * 213304d17814SAndrey V. Elsukov * [<trans>::][<comm>@][<host-or-ip>][:<port>] 213404d17814SAndrey V. Elsukov * 213504d17814SAndrey V. Elsukov * The transport string consists of letters, digits or '_' and starts with 213604d17814SAndrey V. Elsukov * a letter or digit. It is terminated by two colons and may not be empty. 213704d17814SAndrey V. Elsukov * 213804d17814SAndrey V. Elsukov * The community string is terminated by the last '@' and does not exceed 213904d17814SAndrey V. Elsukov * SNMP_COMMUNITY_MAXLEN. It may be empty. 214004d17814SAndrey V. Elsukov * 214104d17814SAndrey V. Elsukov * The host or ip is either an IPv4 address (as parsed by inet_pton()), an 214204d17814SAndrey V. Elsukov * IPv6 address in '[' and ']' and parseable by inet_aton() or a hostname 214304d17814SAndrey V. Elsukov * terminated by the last colon or by the NUL character. 214404d17814SAndrey V. Elsukov * 214504d17814SAndrey V. Elsukov * The port number may be specified numerically or symbolically and starts 214604d17814SAndrey V. Elsukov * with the last colon. 214704d17814SAndrey V. Elsukov * 214804d17814SAndrey V. Elsukov * The functions sets the chost, cport, trans, read_community and 214904d17814SAndrey V. Elsukov * write_community fields on success and the error field on errors. 215004d17814SAndrey V. Elsukov * The chost and cport fields are allocated by malloc(3), their previous 215104d17814SAndrey V. Elsukov * content is deallocated by free(3). 215204d17814SAndrey V. Elsukov * 215304d17814SAndrey V. Elsukov * The function explicitly allows mismatches between the transport and 215404d17814SAndrey V. Elsukov * the address type in order to support IPv4 in IPv6 addresses. 215504d17814SAndrey V. Elsukov * 215604d17814SAndrey V. Elsukov * \param sc client struct to fill 215704d17814SAndrey V. Elsukov * \param str string to parse 215804d17814SAndrey V. Elsukov * 215904d17814SAndrey V. Elsukov * \return 0 on success and -1 on errors 216069292cedSHartmut Brandt */ 216169292cedSHartmut Brandt int 216269292cedSHartmut Brandt snmp_parse_server(struct snmp_client *sc, const char *str) 216369292cedSHartmut Brandt { 216404d17814SAndrey V. Elsukov const char *const orig = str; 21650bf56da3SHartmut Brandt 216604d17814SAndrey V. Elsukov /* parse input */ 21670bf56da3SHartmut Brandt int def_trans = 0, trans = get_transp(sc, &str); 216848e091f9SAndrey V. Elsukov if (trans < 0) 216904d17814SAndrey V. Elsukov return (-1); 217048e091f9SAndrey V. Elsukov /* choose automatically */ 21710bf56da3SHartmut Brandt if (orig == str) 21720bf56da3SHartmut Brandt def_trans = 1; 217304d17814SAndrey V. Elsukov 217404d17814SAndrey V. Elsukov const char *const comm[2] = { 217504d17814SAndrey V. Elsukov str, 217604d17814SAndrey V. Elsukov get_comm(sc, &str), 217704d17814SAndrey V. Elsukov }; 217804d17814SAndrey V. Elsukov if (comm[1] == NULL) 217904d17814SAndrey V. Elsukov return (-1); 218004d17814SAndrey V. Elsukov 218104d17814SAndrey V. Elsukov const char *const ipv6[2] = { 218204d17814SAndrey V. Elsukov str + 1, 218304d17814SAndrey V. Elsukov get_ipv6(sc, &str), 218404d17814SAndrey V. Elsukov }; 218504d17814SAndrey V. Elsukov if (ipv6[1] == NULL) 218604d17814SAndrey V. Elsukov return (-1); 218704d17814SAndrey V. Elsukov 218804d17814SAndrey V. Elsukov const char *ipv4[2] = { 218904d17814SAndrey V. Elsukov str, 219004d17814SAndrey V. Elsukov str, 219104d17814SAndrey V. Elsukov }; 219204d17814SAndrey V. Elsukov 219304d17814SAndrey V. Elsukov const char *host[2] = { 219404d17814SAndrey V. Elsukov str, 219504d17814SAndrey V. Elsukov str, 219604d17814SAndrey V. Elsukov }; 219704d17814SAndrey V. Elsukov 219804d17814SAndrey V. Elsukov if (ipv6[0] == ipv6[1]) { 219904d17814SAndrey V. Elsukov ipv4[1] = get_ipv4(sc, &str); 220004d17814SAndrey V. Elsukov 220104d17814SAndrey V. Elsukov if (ipv4[0] == ipv4[1]) 220204d17814SAndrey V. Elsukov host[1] = get_host(sc, &str); 220369292cedSHartmut Brandt } 220404d17814SAndrey V. Elsukov 220504d17814SAndrey V. Elsukov const char *port[2] = { 220604d17814SAndrey V. Elsukov str + 1, 220704d17814SAndrey V. Elsukov get_port(sc, &str), 220804d17814SAndrey V. Elsukov }; 220904d17814SAndrey V. Elsukov if (port[1] == NULL) 221004d17814SAndrey V. Elsukov return (-1); 221104d17814SAndrey V. Elsukov 221204d17814SAndrey V. Elsukov if (*str != '\0') { 221304d17814SAndrey V. Elsukov seterr(sc, "junk at end of server specification '%s'", str); 221404d17814SAndrey V. Elsukov return (-1); 221504d17814SAndrey V. Elsukov } 221604d17814SAndrey V. Elsukov 221704d17814SAndrey V. Elsukov #if DEBUG_PARSE 22180bf56da3SHartmut Brandt printf("transp: %d (def=%d)\n", trans, def_trans); 221904d17814SAndrey V. Elsukov printf("comm: %zu %zu\n", comm[0] - orig, comm[1] - orig); 222004d17814SAndrey V. Elsukov printf("ipv6: %zu %zu\n", ipv6[0] - orig, ipv6[1] - orig); 222104d17814SAndrey V. Elsukov printf("ipv4: %zu %zu\n", ipv4[0] - orig, ipv4[1] - orig); 222204d17814SAndrey V. Elsukov printf("host: %zu %zu\n", host[0] - orig, host[1] - orig); 222304d17814SAndrey V. Elsukov printf("port: %zu %zu\n", port[0] - orig, port[1] - orig); 222404d17814SAndrey V. Elsukov #endif 222504d17814SAndrey V. Elsukov 222604d17814SAndrey V. Elsukov /* analyse and allocate */ 222704d17814SAndrey V. Elsukov char *chost; 222804d17814SAndrey V. Elsukov 222904d17814SAndrey V. Elsukov if (ipv6[0] != ipv6[1]) { 223004d17814SAndrey V. Elsukov if ((chost = save_str(sc, ipv6)) == NULL) 223104d17814SAndrey V. Elsukov return (-1); 22320bf56da3SHartmut Brandt if (def_trans || trans == SNMP_TRANS_UDP) 22330bf56da3SHartmut Brandt /* assume the user meant udp6:: */ 223448e091f9SAndrey V. Elsukov trans = SNMP_TRANS_UDP6; 223504d17814SAndrey V. Elsukov } else if (ipv4[0] != ipv4[1]) { 223604d17814SAndrey V. Elsukov if ((chost = save_str(sc, ipv4)) == NULL) 223704d17814SAndrey V. Elsukov return (-1); 22380bf56da3SHartmut Brandt if (def_trans) 223948e091f9SAndrey V. Elsukov trans = SNMP_TRANS_UDP; 224004d17814SAndrey V. Elsukov } else { 224104d17814SAndrey V. Elsukov if ((chost = save_str(sc, host)) == NULL) 224204d17814SAndrey V. Elsukov return (-1); 224304d17814SAndrey V. Elsukov 22440bf56da3SHartmut Brandt if (def_trans) { 224548e091f9SAndrey V. Elsukov /* 224648e091f9SAndrey V. Elsukov * Default transport is UDP unless the host contains 224748e091f9SAndrey V. Elsukov * a slash in which case we default to DGRAM. 224848e091f9SAndrey V. Elsukov */ 224904d17814SAndrey V. Elsukov for (const char *p = host[0]; p < host[1]; p++) 225004d17814SAndrey V. Elsukov if (*p == '/') { 225148e091f9SAndrey V. Elsukov trans = SNMP_TRANS_LOC_DGRAM; 225269292cedSHartmut Brandt break; 225369292cedSHartmut Brandt } 225404d17814SAndrey V. Elsukov } 225504d17814SAndrey V. Elsukov } 225669292cedSHartmut Brandt 225748e091f9SAndrey V. Elsukov char *cport; 225848e091f9SAndrey V. Elsukov 225948e091f9SAndrey V. Elsukov if (port[0] == port[1] && ( 226048e091f9SAndrey V. Elsukov trans == SNMP_TRANS_UDP || trans == SNMP_TRANS_UDP6)) { 226148e091f9SAndrey V. Elsukov /* If port was not specified, use "snmp" name by default */ 226248e091f9SAndrey V. Elsukov cport = strdup("snmp"); 226348e091f9SAndrey V. Elsukov } else 226448e091f9SAndrey V. Elsukov cport = save_str(sc, port); 226548e091f9SAndrey V. Elsukov 226604d17814SAndrey V. Elsukov if (cport == NULL) { 226704d17814SAndrey V. Elsukov free(chost); 226869292cedSHartmut Brandt return (-1); 226969292cedSHartmut Brandt } 227069292cedSHartmut Brandt 227104d17814SAndrey V. Elsukov /* commit */ 227248e091f9SAndrey V. Elsukov sc->trans = trans; 22730bf56da3SHartmut Brandt 227448e091f9SAndrey V. Elsukov /* 227548e091f9SAndrey V. Elsukov * If community string was specified and it is empty, overwrite it. 227648e091f9SAndrey V. Elsukov * If it was not specified, use default. 227748e091f9SAndrey V. Elsukov */ 227848e091f9SAndrey V. Elsukov if (comm[0] != comm[1] || strrchr(comm[0], '@') != NULL) { 227904d17814SAndrey V. Elsukov strncpy(sc->read_community, comm[0], comm[1] - comm[0]); 228004d17814SAndrey V. Elsukov sc->read_community[comm[1] - comm[0]] = '\0'; 228104d17814SAndrey V. Elsukov strncpy(sc->write_community, comm[0], comm[1] - comm[0]); 228204d17814SAndrey V. Elsukov sc->write_community[comm[1] - comm[0]] = '\0'; 228348e091f9SAndrey V. Elsukov } 228404d17814SAndrey V. Elsukov 228569292cedSHartmut Brandt free(sc->chost); 228604d17814SAndrey V. Elsukov sc->chost = chost; 228769292cedSHartmut Brandt free(sc->cport); 228804d17814SAndrey V. Elsukov sc->cport = cport; 228969292cedSHartmut Brandt 229048e091f9SAndrey V. Elsukov #if DEBUG_PARSE 229148e091f9SAndrey V. Elsukov printf("Committed values:\n"); 22920bf56da3SHartmut Brandt printf("trans: %d\n", sc->trans); 229348e091f9SAndrey V. Elsukov printf("comm: '%s'/'%s'\n", sc->read_community, sc->write_community); 229448e091f9SAndrey V. Elsukov printf("host: '%s'\n", sc->chost); 229548e091f9SAndrey V. Elsukov printf("port: '%s'\n", sc->cport); 229648e091f9SAndrey V. Elsukov #endif 229769292cedSHartmut Brandt return (0); 229869292cedSHartmut Brandt } 2299