1f06ca4afSHartmut Brandt /* 2f06ca4afSHartmut Brandt * Copyright (c) 2001-2003 3f06ca4afSHartmut Brandt * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4f06ca4afSHartmut Brandt * All rights reserved. 5f06ca4afSHartmut Brandt * 6f06ca4afSHartmut Brandt * Author: Harti Brandt <harti@freebsd.org> 7f06ca4afSHartmut Brandt * 8896052c1SHartmut Brandt * Redistribution and use in source and binary forms, with or without 9896052c1SHartmut Brandt * modification, are permitted provided that the following conditions 10896052c1SHartmut Brandt * are met: 11896052c1SHartmut Brandt * 1. Redistributions of source code must retain the above copyright 12896052c1SHartmut Brandt * notice, this list of conditions and the following disclaimer. 13f06ca4afSHartmut Brandt * 2. Redistributions in binary form must reproduce the above copyright 14f06ca4afSHartmut Brandt * notice, this list of conditions and the following disclaimer in the 15f06ca4afSHartmut Brandt * documentation and/or other materials provided with the distribution. 16f06ca4afSHartmut Brandt * 17896052c1SHartmut Brandt * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18896052c1SHartmut Brandt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19896052c1SHartmut Brandt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20896052c1SHartmut Brandt * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21896052c1SHartmut Brandt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22896052c1SHartmut Brandt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23896052c1SHartmut Brandt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24896052c1SHartmut Brandt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25896052c1SHartmut Brandt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26896052c1SHartmut Brandt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27896052c1SHartmut Brandt * SUCH DAMAGE. 28f06ca4afSHartmut Brandt * 29165c5d31SHartmut Brandt * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ 30f06ca4afSHartmut Brandt * 31f06ca4afSHartmut Brandt * SNMP Agent functions 32f06ca4afSHartmut Brandt */ 33f06ca4afSHartmut Brandt #include <sys/types.h> 34f06ca4afSHartmut Brandt #include <sys/queue.h> 35f06ca4afSHartmut Brandt #include <stdio.h> 36f06ca4afSHartmut Brandt #include <stdlib.h> 37f06ca4afSHartmut Brandt #include <stddef.h> 38f06ca4afSHartmut Brandt #include <stdarg.h> 39165c5d31SHartmut Brandt #ifdef HAVE_STDINT_H 40896052c1SHartmut Brandt #include <stdint.h> 41165c5d31SHartmut Brandt #elif defined(HAVE_INTTYPES_H) 42165c5d31SHartmut Brandt #include <inttypes.h> 43165c5d31SHartmut Brandt #endif 44f06ca4afSHartmut Brandt #include <string.h> 45f06ca4afSHartmut Brandt 46f06ca4afSHartmut Brandt #include "asn1.h" 47f06ca4afSHartmut Brandt #include "snmp.h" 48f06ca4afSHartmut Brandt #include "snmppriv.h" 49f06ca4afSHartmut Brandt #include "snmpagent.h" 50f06ca4afSHartmut Brandt 51f06ca4afSHartmut Brandt static void snmp_debug_func(const char *fmt, ...); 52f06ca4afSHartmut Brandt 53f06ca4afSHartmut Brandt void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; 54f06ca4afSHartmut Brandt 55f06ca4afSHartmut Brandt struct snmp_node *tree; 56f06ca4afSHartmut Brandt u_int tree_size; 57f06ca4afSHartmut Brandt 58f06ca4afSHartmut Brandt /* 59f06ca4afSHartmut Brandt * Structure to hold dependencies during SET processing 60f06ca4afSHartmut Brandt * The last two members of this structure must be the 61f06ca4afSHartmut Brandt * dependency visible by the user and the user data. 62f06ca4afSHartmut Brandt */ 63f06ca4afSHartmut Brandt struct depend { 64f06ca4afSHartmut Brandt TAILQ_ENTRY(depend) link; 65f06ca4afSHartmut Brandt size_t len; /* size of data part */ 66f06ca4afSHartmut Brandt snmp_depop_t func; 67f06ca4afSHartmut Brandt struct snmp_dependency dep; 68896052c1SHartmut Brandt #if defined(__GNUC__) && __GNUC__ < 3 69896052c1SHartmut Brandt u_char data[0]; 70896052c1SHartmut Brandt #else 71f06ca4afSHartmut Brandt u_char data[]; 72896052c1SHartmut Brandt #endif 73f06ca4afSHartmut Brandt }; 74f06ca4afSHartmut Brandt TAILQ_HEAD(depend_list, depend); 75f06ca4afSHartmut Brandt 76f06ca4afSHartmut Brandt /* 77f06ca4afSHartmut Brandt * Set context 78f06ca4afSHartmut Brandt */ 79f06ca4afSHartmut Brandt struct context { 80f06ca4afSHartmut Brandt struct snmp_context ctx; 81f06ca4afSHartmut Brandt struct depend_list dlist; 82f06ca4afSHartmut Brandt const struct snmp_node *node[SNMP_MAX_BINDINGS]; 83f06ca4afSHartmut Brandt struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; 84f06ca4afSHartmut Brandt struct depend *depend; 85f06ca4afSHartmut Brandt }; 86f06ca4afSHartmut Brandt 87f06ca4afSHartmut Brandt #define TR(W) (snmp_trace & SNMP_TRACE_##W) 88f06ca4afSHartmut Brandt u_int snmp_trace = 0; 89f06ca4afSHartmut Brandt 90f06ca4afSHartmut Brandt static char oidbuf[ASN_OIDSTRLEN]; 91f06ca4afSHartmut Brandt 92f06ca4afSHartmut Brandt /* 93f06ca4afSHartmut Brandt * Allocate a context 94f06ca4afSHartmut Brandt */ 95f06ca4afSHartmut Brandt struct snmp_context * 96f06ca4afSHartmut Brandt snmp_init_context(void) 97f06ca4afSHartmut Brandt { 98f06ca4afSHartmut Brandt struct context *context; 99f06ca4afSHartmut Brandt 100f06ca4afSHartmut Brandt if ((context = malloc(sizeof(*context))) == NULL) 101f06ca4afSHartmut Brandt return (NULL); 102f06ca4afSHartmut Brandt 103f06ca4afSHartmut Brandt memset(context, 0, sizeof(*context)); 104f06ca4afSHartmut Brandt TAILQ_INIT(&context->dlist); 105f06ca4afSHartmut Brandt 106f06ca4afSHartmut Brandt return (&context->ctx); 107f06ca4afSHartmut Brandt } 108f06ca4afSHartmut Brandt 109f06ca4afSHartmut Brandt /* 110f06ca4afSHartmut Brandt * Find a variable for SET/GET and the first GETBULK pass. 111f06ca4afSHartmut Brandt * Return the node pointer. If the search fails, set the errp to 112f06ca4afSHartmut Brandt * the correct SNMPv2 GET exception code. 113f06ca4afSHartmut Brandt */ 114f06ca4afSHartmut Brandt static struct snmp_node * 115f06ca4afSHartmut Brandt find_node(const struct snmp_value *value, enum snmp_syntax *errp) 116f06ca4afSHartmut Brandt { 117f06ca4afSHartmut Brandt struct snmp_node *tp; 118f06ca4afSHartmut Brandt 119f06ca4afSHartmut Brandt if (TR(FIND)) 120f06ca4afSHartmut Brandt snmp_debug("find: searching %s", 121f06ca4afSHartmut Brandt asn_oid2str_r(&value->var, oidbuf)); 122f06ca4afSHartmut Brandt 123f06ca4afSHartmut Brandt /* 124f06ca4afSHartmut Brandt * If we have an exact match (the entry in the table is a 125f06ca4afSHartmut Brandt * sub-oid from the variable) we have found what we are for. 126f06ca4afSHartmut Brandt * If the table oid is higher than the variable, there is no match. 127f06ca4afSHartmut Brandt */ 128f06ca4afSHartmut Brandt for (tp = tree; tp < tree + tree_size; tp++) { 129f06ca4afSHartmut Brandt if (asn_is_suboid(&tp->oid, &value->var)) 130f06ca4afSHartmut Brandt goto found; 131f06ca4afSHartmut Brandt if (asn_compare_oid(&tp->oid, &value->var) >= 0) 132f06ca4afSHartmut Brandt break; 133f06ca4afSHartmut Brandt } 134f06ca4afSHartmut Brandt 135f06ca4afSHartmut Brandt if (TR(FIND)) 136f06ca4afSHartmut Brandt snmp_debug("find: no match"); 137f06ca4afSHartmut Brandt *errp = SNMP_SYNTAX_NOSUCHOBJECT; 138f06ca4afSHartmut Brandt return (NULL); 139f06ca4afSHartmut Brandt 140f06ca4afSHartmut Brandt found: 141f06ca4afSHartmut Brandt /* leafs must have a 0 instance identifier */ 142f06ca4afSHartmut Brandt if (tp->type == SNMP_NODE_LEAF && 143f06ca4afSHartmut Brandt (value->var.len != tp->oid.len + 1 || 144f06ca4afSHartmut Brandt value->var.subs[tp->oid.len] != 0)) { 145f06ca4afSHartmut Brandt if (TR(FIND)) 146f06ca4afSHartmut Brandt snmp_debug("find: bad leaf index"); 147f06ca4afSHartmut Brandt *errp = SNMP_SYNTAX_NOSUCHINSTANCE; 148f06ca4afSHartmut Brandt return (NULL); 149f06ca4afSHartmut Brandt } 150f06ca4afSHartmut Brandt if (TR(FIND)) 151f06ca4afSHartmut Brandt snmp_debug("find: found %s", 152f06ca4afSHartmut Brandt asn_oid2str_r(&value->var, oidbuf)); 153f06ca4afSHartmut Brandt return (tp); 154f06ca4afSHartmut Brandt } 155f06ca4afSHartmut Brandt 156f06ca4afSHartmut Brandt static struct snmp_node * 157f06ca4afSHartmut Brandt find_subnode(const struct snmp_value *value) 158f06ca4afSHartmut Brandt { 159f06ca4afSHartmut Brandt struct snmp_node *tp; 160f06ca4afSHartmut Brandt 161f06ca4afSHartmut Brandt for (tp = tree; tp < tree + tree_size; tp++) { 162f06ca4afSHartmut Brandt if (asn_is_suboid(&value->var, &tp->oid)) 163f06ca4afSHartmut Brandt return (tp); 164f06ca4afSHartmut Brandt } 165f06ca4afSHartmut Brandt return (NULL); 166f06ca4afSHartmut Brandt } 167f06ca4afSHartmut Brandt 168135f7de5SShteryana Shopova static void 16906983448SShteryana Shopova snmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp) 170135f7de5SShteryana Shopova { 171135f7de5SShteryana Shopova memset(resp, 0, sizeof(*resp)); 172135f7de5SShteryana Shopova strcpy(resp->community, pdu->community); 173135f7de5SShteryana Shopova resp->version = pdu->version; 174*3b49535aSShteryana Shopova if (pdu->flags & SNMP_MSG_AUTODISCOVER) 175*3b49535aSShteryana Shopova resp->type = SNMP_PDU_REPORT; /* RFC 3414.4 */ 176*3b49535aSShteryana Shopova else 177135f7de5SShteryana Shopova resp->type = SNMP_PDU_RESPONSE; 178135f7de5SShteryana Shopova resp->request_id = pdu->request_id; 179135f7de5SShteryana Shopova resp->version = pdu->version; 180135f7de5SShteryana Shopova 181135f7de5SShteryana Shopova if (resp->version != SNMP_V3) 182135f7de5SShteryana Shopova return; 183135f7de5SShteryana Shopova 18472cd7a52SShteryana Shopova memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); 18572cd7a52SShteryana Shopova memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); 18672cd7a52SShteryana Shopova snmp_pdu_init_secparams(resp); 187135f7de5SShteryana Shopova resp->identifier = pdu->identifier; 188135f7de5SShteryana Shopova resp->security_model = pdu->security_model; 189135f7de5SShteryana Shopova resp->context_engine_len = pdu->context_engine_len; 190135f7de5SShteryana Shopova memcpy(resp->context_engine, pdu->context_engine, 191135f7de5SShteryana Shopova resp->context_engine_len); 192135f7de5SShteryana Shopova strlcpy(resp->context_name, pdu->context_name, 193135f7de5SShteryana Shopova sizeof(resp->context_name)); 194135f7de5SShteryana Shopova } 195135f7de5SShteryana Shopova 196f06ca4afSHartmut Brandt /* 197f06ca4afSHartmut Brandt * Execute a GET operation. The tree is rooted at the global 'root'. 198f06ca4afSHartmut Brandt * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 199f06ca4afSHartmut Brandt * the pdu error status and index will be set. 200f06ca4afSHartmut Brandt */ 201f06ca4afSHartmut Brandt enum snmp_ret 202f06ca4afSHartmut Brandt snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 203f06ca4afSHartmut Brandt struct snmp_pdu *resp, void *data) 204f06ca4afSHartmut Brandt { 205f06ca4afSHartmut Brandt int ret; 206f06ca4afSHartmut Brandt u_int i; 207f06ca4afSHartmut Brandt struct snmp_node *tp; 208f06ca4afSHartmut Brandt enum snmp_syntax except; 209f06ca4afSHartmut Brandt struct context context; 210f06ca4afSHartmut Brandt enum asn_err err; 211f06ca4afSHartmut Brandt 212f06ca4afSHartmut Brandt memset(&context, 0, sizeof(context)); 213f06ca4afSHartmut Brandt context.ctx.data = data; 214f06ca4afSHartmut Brandt 215135f7de5SShteryana Shopova snmp_pdu_create_response(pdu, resp); 216f06ca4afSHartmut Brandt 217f06ca4afSHartmut Brandt if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 218f06ca4afSHartmut Brandt /* cannot even encode header - very bad */ 219f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 220f06ca4afSHartmut Brandt 221f06ca4afSHartmut Brandt for (i = 0; i < pdu->nbindings; i++) { 222f06ca4afSHartmut Brandt resp->bindings[i].var = pdu->bindings[i].var; 223f06ca4afSHartmut Brandt if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 224f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 225f06ca4afSHartmut Brandt if (TR(GET)) 226f06ca4afSHartmut Brandt snmp_debug("get: nosuchname"); 227f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOSUCHNAME; 228f06ca4afSHartmut Brandt pdu->error_index = i + 1; 229f06ca4afSHartmut Brandt snmp_pdu_free(resp); 230f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 231f06ca4afSHartmut Brandt } 232f06ca4afSHartmut Brandt if (TR(GET)) 233f06ca4afSHartmut Brandt snmp_debug("get: exception %u", except); 234f06ca4afSHartmut Brandt resp->bindings[i].syntax = except; 235f06ca4afSHartmut Brandt 236f06ca4afSHartmut Brandt } else { 237f06ca4afSHartmut Brandt /* call the action to fetch the value. */ 238f06ca4afSHartmut Brandt resp->bindings[i].syntax = tp->syntax; 239f06ca4afSHartmut Brandt ret = (*tp->op)(&context.ctx, &resp->bindings[i], 240f06ca4afSHartmut Brandt tp->oid.len, tp->index, SNMP_OP_GET); 241f06ca4afSHartmut Brandt if (TR(GET)) 242f06ca4afSHartmut Brandt snmp_debug("get: action returns %d", ret); 243f06ca4afSHartmut Brandt 244f06ca4afSHartmut Brandt if (ret == SNMP_ERR_NOSUCHNAME) { 245f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 246f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOSUCHNAME; 247f06ca4afSHartmut Brandt pdu->error_index = i + 1; 248f06ca4afSHartmut Brandt snmp_pdu_free(resp); 249f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 250f06ca4afSHartmut Brandt } 251f06ca4afSHartmut Brandt if (TR(GET)) 252f06ca4afSHartmut Brandt snmp_debug("get: exception noSuchInstance"); 253f06ca4afSHartmut Brandt resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 254f06ca4afSHartmut Brandt 255f06ca4afSHartmut Brandt } else if (ret != SNMP_ERR_NOERROR) { 256f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 257f06ca4afSHartmut Brandt pdu->error_index = i + 1; 258f06ca4afSHartmut Brandt snmp_pdu_free(resp); 259f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 260f06ca4afSHartmut Brandt } 261f06ca4afSHartmut Brandt } 262f06ca4afSHartmut Brandt resp->nbindings++; 263f06ca4afSHartmut Brandt 264f06ca4afSHartmut Brandt err = snmp_binding_encode(resp_b, &resp->bindings[i]); 265f06ca4afSHartmut Brandt 266f06ca4afSHartmut Brandt if (err == ASN_ERR_EOBUF) { 267f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_TOOBIG; 268f06ca4afSHartmut Brandt pdu->error_index = 0; 269f06ca4afSHartmut Brandt snmp_pdu_free(resp); 270f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 271f06ca4afSHartmut Brandt } 272f06ca4afSHartmut Brandt if (err != ASN_ERR_OK) { 273f06ca4afSHartmut Brandt if (TR(GET)) 274f06ca4afSHartmut Brandt snmp_debug("get: binding encoding: %u", err); 275f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 276f06ca4afSHartmut Brandt pdu->error_index = i + 1; 277f06ca4afSHartmut Brandt snmp_pdu_free(resp); 278f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 279f06ca4afSHartmut Brandt } 280f06ca4afSHartmut Brandt } 281f06ca4afSHartmut Brandt 282546401ceSShteryana Shopova if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 283546401ceSShteryana Shopova snmp_debug("get: failed to encode PDU"); 284546401ceSShteryana Shopova return (SNMP_RET_ERR); 285546401ceSShteryana Shopova } 286546401ceSShteryana Shopova 287546401ceSShteryana Shopova return (SNMP_RET_OK); 288f06ca4afSHartmut Brandt } 289f06ca4afSHartmut Brandt 290f06ca4afSHartmut Brandt static struct snmp_node * 291f06ca4afSHartmut Brandt next_node(const struct snmp_value *value, int *pnext) 292f06ca4afSHartmut Brandt { 293f06ca4afSHartmut Brandt struct snmp_node *tp; 294f06ca4afSHartmut Brandt 295f06ca4afSHartmut Brandt if (TR(FIND)) 296f06ca4afSHartmut Brandt snmp_debug("next: searching %s", 297f06ca4afSHartmut Brandt asn_oid2str_r(&value->var, oidbuf)); 298f06ca4afSHartmut Brandt 299f06ca4afSHartmut Brandt *pnext = 0; 300f06ca4afSHartmut Brandt for (tp = tree; tp < tree + tree_size; tp++) { 301f06ca4afSHartmut Brandt if (asn_is_suboid(&tp->oid, &value->var)) { 302f06ca4afSHartmut Brandt /* the tree OID is a sub-oid of the requested OID. */ 303f06ca4afSHartmut Brandt if (tp->type == SNMP_NODE_LEAF) { 304f06ca4afSHartmut Brandt if (tp->oid.len == value->var.len) { 305f06ca4afSHartmut Brandt /* request for scalar type */ 306f06ca4afSHartmut Brandt if (TR(FIND)) 307f06ca4afSHartmut Brandt snmp_debug("next: found scalar %s", 308f06ca4afSHartmut Brandt asn_oid2str_r(&tp->oid, oidbuf)); 309f06ca4afSHartmut Brandt return (tp); 310f06ca4afSHartmut Brandt } 311f06ca4afSHartmut Brandt /* try next */ 312f06ca4afSHartmut Brandt } else { 313f06ca4afSHartmut Brandt if (TR(FIND)) 314f06ca4afSHartmut Brandt snmp_debug("next: found column %s", 315f06ca4afSHartmut Brandt asn_oid2str_r(&tp->oid, oidbuf)); 316f06ca4afSHartmut Brandt return (tp); 317f06ca4afSHartmut Brandt } 318f06ca4afSHartmut Brandt } else if (asn_is_suboid(&value->var, &tp->oid) || 319f06ca4afSHartmut Brandt asn_compare_oid(&tp->oid, &value->var) >= 0) { 320f06ca4afSHartmut Brandt if (TR(FIND)) 321f06ca4afSHartmut Brandt snmp_debug("next: found %s", 322f06ca4afSHartmut Brandt asn_oid2str_r(&tp->oid, oidbuf)); 323f06ca4afSHartmut Brandt *pnext = 1; 324f06ca4afSHartmut Brandt return (tp); 325f06ca4afSHartmut Brandt } 326f06ca4afSHartmut Brandt } 327f06ca4afSHartmut Brandt 328f06ca4afSHartmut Brandt if (TR(FIND)) 329f06ca4afSHartmut Brandt snmp_debug("next: failed"); 330f06ca4afSHartmut Brandt 331f06ca4afSHartmut Brandt return (NULL); 332f06ca4afSHartmut Brandt } 333f06ca4afSHartmut Brandt 334f06ca4afSHartmut Brandt static enum snmp_ret 335f06ca4afSHartmut Brandt do_getnext(struct context *context, const struct snmp_value *inb, 336f06ca4afSHartmut Brandt struct snmp_value *outb, struct snmp_pdu *pdu) 337f06ca4afSHartmut Brandt { 338f06ca4afSHartmut Brandt const struct snmp_node *tp; 339f06ca4afSHartmut Brandt int ret, next; 340f06ca4afSHartmut Brandt 341f06ca4afSHartmut Brandt if ((tp = next_node(inb, &next)) == NULL) 342f06ca4afSHartmut Brandt goto eofMib; 343f06ca4afSHartmut Brandt 344f06ca4afSHartmut Brandt /* retain old variable if we are doing a GETNEXT on an exact 345f06ca4afSHartmut Brandt * matched leaf only */ 346f06ca4afSHartmut Brandt if (tp->type == SNMP_NODE_LEAF || next) 347f06ca4afSHartmut Brandt outb->var = tp->oid; 348f06ca4afSHartmut Brandt else 349f06ca4afSHartmut Brandt outb->var = inb->var; 350f06ca4afSHartmut Brandt 351f06ca4afSHartmut Brandt for (;;) { 352f06ca4afSHartmut Brandt outb->syntax = tp->syntax; 353f06ca4afSHartmut Brandt if (tp->type == SNMP_NODE_LEAF) { 354f06ca4afSHartmut Brandt /* make a GET operation */ 355f06ca4afSHartmut Brandt outb->var.subs[outb->var.len++] = 0; 356f06ca4afSHartmut Brandt ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 357f06ca4afSHartmut Brandt tp->index, SNMP_OP_GET); 358f06ca4afSHartmut Brandt } else { 359f06ca4afSHartmut Brandt /* make a GETNEXT */ 360f06ca4afSHartmut Brandt ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 361f06ca4afSHartmut Brandt tp->index, SNMP_OP_GETNEXT); 362f06ca4afSHartmut Brandt } 363f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOSUCHNAME) { 364f06ca4afSHartmut Brandt /* got something */ 365f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 366f06ca4afSHartmut Brandt snmp_debug("getnext: %s returns %u", 367f06ca4afSHartmut Brandt asn_oid2str(&outb->var), ret); 368f06ca4afSHartmut Brandt break; 369f06ca4afSHartmut Brandt } 370f06ca4afSHartmut Brandt 371f06ca4afSHartmut Brandt /* object has no data - try next */ 372f06ca4afSHartmut Brandt if (++tp == tree + tree_size) 373f06ca4afSHartmut Brandt break; 37494caccb3SHartmut Brandt 37594caccb3SHartmut Brandt if (TR(GETNEXT)) 37694caccb3SHartmut Brandt snmp_debug("getnext: no data - avancing to %s", 37794caccb3SHartmut Brandt asn_oid2str(&tp->oid)); 37894caccb3SHartmut Brandt 379f06ca4afSHartmut Brandt outb->var = tp->oid; 380f06ca4afSHartmut Brandt } 381f06ca4afSHartmut Brandt 382f06ca4afSHartmut Brandt if (ret == SNMP_ERR_NOSUCHNAME) { 383f06ca4afSHartmut Brandt eofMib: 384f06ca4afSHartmut Brandt outb->var = inb->var; 385f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 386f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOSUCHNAME; 387f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 388f06ca4afSHartmut Brandt } 389f06ca4afSHartmut Brandt outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 390f06ca4afSHartmut Brandt 391f06ca4afSHartmut Brandt } else if (ret != SNMP_ERR_NOERROR) { 392f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 393f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 394f06ca4afSHartmut Brandt } 395f06ca4afSHartmut Brandt return (SNMP_RET_OK); 396f06ca4afSHartmut Brandt } 397f06ca4afSHartmut Brandt 398f06ca4afSHartmut Brandt 399f06ca4afSHartmut Brandt /* 400f06ca4afSHartmut Brandt * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 401f06ca4afSHartmut Brandt * Build the response PDU on the fly. The return is: 402f06ca4afSHartmut Brandt */ 403f06ca4afSHartmut Brandt enum snmp_ret 404f06ca4afSHartmut Brandt snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 405f06ca4afSHartmut Brandt struct snmp_pdu *resp, void *data) 406f06ca4afSHartmut Brandt { 407f06ca4afSHartmut Brandt struct context context; 408f06ca4afSHartmut Brandt u_int i; 409f06ca4afSHartmut Brandt enum asn_err err; 410f06ca4afSHartmut Brandt enum snmp_ret result; 411f06ca4afSHartmut Brandt 412f06ca4afSHartmut Brandt memset(&context, 0, sizeof(context)); 413f06ca4afSHartmut Brandt context.ctx.data = data; 414f06ca4afSHartmut Brandt 415135f7de5SShteryana Shopova snmp_pdu_create_response(pdu, resp); 416f06ca4afSHartmut Brandt 417f06ca4afSHartmut Brandt if (snmp_pdu_encode_header(resp_b, resp)) 418f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 419f06ca4afSHartmut Brandt 420f06ca4afSHartmut Brandt for (i = 0; i < pdu->nbindings; i++) { 421f06ca4afSHartmut Brandt result = do_getnext(&context, &pdu->bindings[i], 422f06ca4afSHartmut Brandt &resp->bindings[i], pdu); 423f06ca4afSHartmut Brandt 424f06ca4afSHartmut Brandt if (result != SNMP_RET_OK) { 425f06ca4afSHartmut Brandt pdu->error_index = i + 1; 426f06ca4afSHartmut Brandt snmp_pdu_free(resp); 427f06ca4afSHartmut Brandt return (result); 428f06ca4afSHartmut Brandt } 429f06ca4afSHartmut Brandt 430f06ca4afSHartmut Brandt resp->nbindings++; 431f06ca4afSHartmut Brandt 432f06ca4afSHartmut Brandt err = snmp_binding_encode(resp_b, &resp->bindings[i]); 433f06ca4afSHartmut Brandt 434f06ca4afSHartmut Brandt if (err == ASN_ERR_EOBUF) { 435f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_TOOBIG; 436f06ca4afSHartmut Brandt pdu->error_index = 0; 437f06ca4afSHartmut Brandt snmp_pdu_free(resp); 438f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 439f06ca4afSHartmut Brandt } 440f06ca4afSHartmut Brandt if (err != ASN_ERR_OK) { 441f06ca4afSHartmut Brandt if (TR(GET)) 442f06ca4afSHartmut Brandt snmp_debug("getnext: binding encoding: %u", err); 443f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 444f06ca4afSHartmut Brandt pdu->error_index = i + 1; 445f06ca4afSHartmut Brandt snmp_pdu_free(resp); 446f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 447f06ca4afSHartmut Brandt } 448f06ca4afSHartmut Brandt } 449546401ceSShteryana Shopova 450546401ceSShteryana Shopova if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 451546401ceSShteryana Shopova snmp_debug("getnext: failed to encode PDU"); 452546401ceSShteryana Shopova return (SNMP_RET_ERR); 453546401ceSShteryana Shopova } 454546401ceSShteryana Shopova 455546401ceSShteryana Shopova return (SNMP_RET_OK); 456f06ca4afSHartmut Brandt } 457f06ca4afSHartmut Brandt 458f06ca4afSHartmut Brandt enum snmp_ret 459f06ca4afSHartmut Brandt snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 460f06ca4afSHartmut Brandt struct snmp_pdu *resp, void *data) 461f06ca4afSHartmut Brandt { 462f06ca4afSHartmut Brandt struct context context; 463f06ca4afSHartmut Brandt u_int i; 464f06ca4afSHartmut Brandt int cnt; 465f06ca4afSHartmut Brandt u_int non_rep; 466f06ca4afSHartmut Brandt int eomib; 467f06ca4afSHartmut Brandt enum snmp_ret result; 468f06ca4afSHartmut Brandt enum asn_err err; 469f06ca4afSHartmut Brandt 470f06ca4afSHartmut Brandt memset(&context, 0, sizeof(context)); 471f06ca4afSHartmut Brandt context.ctx.data = data; 472f06ca4afSHartmut Brandt 473135f7de5SShteryana Shopova snmp_pdu_create_response(pdu, resp); 474f06ca4afSHartmut Brandt 475f06ca4afSHartmut Brandt if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 476f06ca4afSHartmut Brandt /* cannot even encode header - very bad */ 477f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 478f06ca4afSHartmut Brandt 479f06ca4afSHartmut Brandt if ((non_rep = pdu->error_status) > pdu->nbindings) 480f06ca4afSHartmut Brandt non_rep = pdu->nbindings; 481f06ca4afSHartmut Brandt 482f06ca4afSHartmut Brandt /* non-repeaters */ 483f06ca4afSHartmut Brandt for (i = 0; i < non_rep; i++) { 484f06ca4afSHartmut Brandt result = do_getnext(&context, &pdu->bindings[i], 485f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings], pdu); 486f06ca4afSHartmut Brandt 487f06ca4afSHartmut Brandt if (result != SNMP_RET_OK) { 488f06ca4afSHartmut Brandt pdu->error_index = i + 1; 489f06ca4afSHartmut Brandt snmp_pdu_free(resp); 490f06ca4afSHartmut Brandt return (result); 491f06ca4afSHartmut Brandt } 492f06ca4afSHartmut Brandt 493f06ca4afSHartmut Brandt err = snmp_binding_encode(resp_b, 494f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings++]); 495f06ca4afSHartmut Brandt 496f06ca4afSHartmut Brandt if (err == ASN_ERR_EOBUF) 497f06ca4afSHartmut Brandt goto done; 498f06ca4afSHartmut Brandt 499f06ca4afSHartmut Brandt if (err != ASN_ERR_OK) { 500f06ca4afSHartmut Brandt if (TR(GET)) 501f06ca4afSHartmut Brandt snmp_debug("getnext: binding encoding: %u", err); 502f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 503f06ca4afSHartmut Brandt pdu->error_index = i + 1; 504f06ca4afSHartmut Brandt snmp_pdu_free(resp); 505f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 506f06ca4afSHartmut Brandt } 507f06ca4afSHartmut Brandt } 508f06ca4afSHartmut Brandt 509f06ca4afSHartmut Brandt if (non_rep == pdu->nbindings) 510f06ca4afSHartmut Brandt goto done; 511f06ca4afSHartmut Brandt 512f06ca4afSHartmut Brandt /* repeates */ 513f06ca4afSHartmut Brandt for (cnt = 0; cnt < pdu->error_index; cnt++) { 514f06ca4afSHartmut Brandt eomib = 1; 515f06ca4afSHartmut Brandt for (i = non_rep; i < pdu->nbindings; i++) { 516ecd241b6SXin LI 517ecd241b6SXin LI if (resp->nbindings == SNMP_MAX_BINDINGS) 518ecd241b6SXin LI /* PDU is full */ 519ecd241b6SXin LI goto done; 520ecd241b6SXin LI 521f06ca4afSHartmut Brandt if (cnt == 0) 522f06ca4afSHartmut Brandt result = do_getnext(&context, &pdu->bindings[i], 523f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings], pdu); 524f06ca4afSHartmut Brandt else 525f06ca4afSHartmut Brandt result = do_getnext(&context, 526f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings - 527f06ca4afSHartmut Brandt (pdu->nbindings - non_rep)], 528f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings], pdu); 529f06ca4afSHartmut Brandt 530f06ca4afSHartmut Brandt if (result != SNMP_RET_OK) { 531f06ca4afSHartmut Brandt pdu->error_index = i + 1; 532f06ca4afSHartmut Brandt snmp_pdu_free(resp); 533f06ca4afSHartmut Brandt return (result); 534f06ca4afSHartmut Brandt } 535f06ca4afSHartmut Brandt if (resp->bindings[resp->nbindings].syntax != 536f06ca4afSHartmut Brandt SNMP_SYNTAX_ENDOFMIBVIEW) 537f06ca4afSHartmut Brandt eomib = 0; 538f06ca4afSHartmut Brandt 539f06ca4afSHartmut Brandt err = snmp_binding_encode(resp_b, 540f06ca4afSHartmut Brandt &resp->bindings[resp->nbindings++]); 541f06ca4afSHartmut Brandt 542f06ca4afSHartmut Brandt if (err == ASN_ERR_EOBUF) 543f06ca4afSHartmut Brandt goto done; 544f06ca4afSHartmut Brandt 545f06ca4afSHartmut Brandt if (err != ASN_ERR_OK) { 546f06ca4afSHartmut Brandt if (TR(GET)) 547f06ca4afSHartmut Brandt snmp_debug("getnext: binding encoding: %u", err); 548f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 549f06ca4afSHartmut Brandt pdu->error_index = i + 1; 550f06ca4afSHartmut Brandt snmp_pdu_free(resp); 551f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 552f06ca4afSHartmut Brandt } 553f06ca4afSHartmut Brandt } 554f06ca4afSHartmut Brandt if (eomib) 555f06ca4afSHartmut Brandt break; 556f06ca4afSHartmut Brandt } 557f06ca4afSHartmut Brandt 558f06ca4afSHartmut Brandt done: 559546401ceSShteryana Shopova if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 560546401ceSShteryana Shopova snmp_debug("getnext: failed to encode PDU"); 561546401ceSShteryana Shopova return (SNMP_RET_ERR); 562546401ceSShteryana Shopova } 563546401ceSShteryana Shopova 564546401ceSShteryana Shopova return (SNMP_RET_OK); 565f06ca4afSHartmut Brandt } 566f06ca4afSHartmut Brandt 567f06ca4afSHartmut Brandt /* 568f06ca4afSHartmut Brandt * Rollback a SET operation. Failed index is 'i'. 569f06ca4afSHartmut Brandt */ 570f06ca4afSHartmut Brandt static void 571f06ca4afSHartmut Brandt rollback(struct context *context, struct snmp_pdu *pdu, u_int i) 572f06ca4afSHartmut Brandt { 573f06ca4afSHartmut Brandt struct snmp_value *b; 574f06ca4afSHartmut Brandt const struct snmp_node *np; 575f06ca4afSHartmut Brandt int ret; 576f06ca4afSHartmut Brandt 577f06ca4afSHartmut Brandt while (i-- > 0) { 578f06ca4afSHartmut Brandt b = &pdu->bindings[i]; 579f06ca4afSHartmut Brandt np = context->node[i]; 580f06ca4afSHartmut Brandt 581f06ca4afSHartmut Brandt context->ctx.scratch = &context->scratch[i]; 582f06ca4afSHartmut Brandt 583f06ca4afSHartmut Brandt ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 584f06ca4afSHartmut Brandt SNMP_OP_ROLLBACK); 585f06ca4afSHartmut Brandt 586f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR) { 587f06ca4afSHartmut Brandt snmp_error("set: rollback failed (%d) on variable %s " 588f06ca4afSHartmut Brandt "index %u", ret, asn_oid2str(&b->var), i); 589f06ca4afSHartmut Brandt if (pdu->version != SNMP_V1) { 590f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_UNDO_FAILED; 591f06ca4afSHartmut Brandt pdu->error_index = 0; 592f06ca4afSHartmut Brandt } 593f06ca4afSHartmut Brandt } 594f06ca4afSHartmut Brandt } 595f06ca4afSHartmut Brandt } 596f06ca4afSHartmut Brandt 597f06ca4afSHartmut Brandt /* 598f06ca4afSHartmut Brandt * Commit dependencies. 599f06ca4afSHartmut Brandt */ 600f06ca4afSHartmut Brandt int 601f06ca4afSHartmut Brandt snmp_dep_commit(struct snmp_context *ctx) 602f06ca4afSHartmut Brandt { 603f06ca4afSHartmut Brandt struct context *context = (struct context *)ctx; 604f06ca4afSHartmut Brandt int ret; 605f06ca4afSHartmut Brandt 606f06ca4afSHartmut Brandt TAILQ_FOREACH(context->depend, &context->dlist, link) { 607f06ca4afSHartmut Brandt ctx->dep = &context->depend->dep; 608f06ca4afSHartmut Brandt 609f06ca4afSHartmut Brandt if (TR(SET)) 610f06ca4afSHartmut Brandt snmp_debug("set: dependency commit %s", 611f06ca4afSHartmut Brandt asn_oid2str(&ctx->dep->obj)); 612f06ca4afSHartmut Brandt 613f06ca4afSHartmut Brandt ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 614f06ca4afSHartmut Brandt 615f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR) { 616f06ca4afSHartmut Brandt if (TR(SET)) 617f06ca4afSHartmut Brandt snmp_debug("set: dependency failed %d", ret); 618f06ca4afSHartmut Brandt return (ret); 619f06ca4afSHartmut Brandt } 620f06ca4afSHartmut Brandt } 621f06ca4afSHartmut Brandt return (SNMP_ERR_NOERROR); 622f06ca4afSHartmut Brandt } 623f06ca4afSHartmut Brandt 624f06ca4afSHartmut Brandt /* 625f06ca4afSHartmut Brandt * Rollback dependencies 626f06ca4afSHartmut Brandt */ 627f06ca4afSHartmut Brandt int 628f06ca4afSHartmut Brandt snmp_dep_rollback(struct snmp_context *ctx) 629f06ca4afSHartmut Brandt { 630f06ca4afSHartmut Brandt struct context *context = (struct context *)ctx; 631f06ca4afSHartmut Brandt int ret, ret1; 632f06ca4afSHartmut Brandt char objbuf[ASN_OIDSTRLEN]; 633f06ca4afSHartmut Brandt char idxbuf[ASN_OIDSTRLEN]; 634f06ca4afSHartmut Brandt 635f06ca4afSHartmut Brandt ret1 = SNMP_ERR_NOERROR; 636f06ca4afSHartmut Brandt while ((context->depend = 637f06ca4afSHartmut Brandt TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 638f06ca4afSHartmut Brandt ctx->dep = &context->depend->dep; 639f06ca4afSHartmut Brandt 640f06ca4afSHartmut Brandt if (TR(SET)) 641f06ca4afSHartmut Brandt snmp_debug("set: dependency rollback %s", 642f06ca4afSHartmut Brandt asn_oid2str(&ctx->dep->obj)); 643f06ca4afSHartmut Brandt 644f06ca4afSHartmut Brandt ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 645f06ca4afSHartmut Brandt 646f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR) { 647f06ca4afSHartmut Brandt snmp_debug("set: dep rollback returns %u: %s %s", ret, 648f06ca4afSHartmut Brandt asn_oid2str_r(&ctx->dep->obj, objbuf), 649f06ca4afSHartmut Brandt asn_oid2str_r(&ctx->dep->idx, idxbuf)); 650f06ca4afSHartmut Brandt if (ret1 == SNMP_ERR_NOERROR) 651f06ca4afSHartmut Brandt ret1 = ret; 652f06ca4afSHartmut Brandt } 653f06ca4afSHartmut Brandt } 654f06ca4afSHartmut Brandt return (ret1); 655f06ca4afSHartmut Brandt } 656f06ca4afSHartmut Brandt 6578eecd77aSHartmut Brandt void 6588eecd77aSHartmut Brandt snmp_dep_finish(struct snmp_context *ctx) 6598eecd77aSHartmut Brandt { 6608eecd77aSHartmut Brandt struct context *context = (struct context *)ctx; 6618eecd77aSHartmut Brandt struct depend *d; 6628eecd77aSHartmut Brandt 6638eecd77aSHartmut Brandt while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 6648eecd77aSHartmut Brandt ctx->dep = &d->dep; 6658eecd77aSHartmut Brandt (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 6668eecd77aSHartmut Brandt TAILQ_REMOVE(&context->dlist, d, link); 6678eecd77aSHartmut Brandt free(d); 6688eecd77aSHartmut Brandt } 6698eecd77aSHartmut Brandt } 6708eecd77aSHartmut Brandt 671f06ca4afSHartmut Brandt /* 672f06ca4afSHartmut Brandt * Do a SET operation. 673f06ca4afSHartmut Brandt */ 674f06ca4afSHartmut Brandt enum snmp_ret 675f06ca4afSHartmut Brandt snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 676f06ca4afSHartmut Brandt struct snmp_pdu *resp, void *data) 677f06ca4afSHartmut Brandt { 678f06ca4afSHartmut Brandt int ret; 679f06ca4afSHartmut Brandt u_int i; 680f06ca4afSHartmut Brandt enum asn_err asnerr; 681f06ca4afSHartmut Brandt struct context context; 682f06ca4afSHartmut Brandt const struct snmp_node *np; 683f06ca4afSHartmut Brandt struct snmp_value *b; 684f06ca4afSHartmut Brandt enum snmp_syntax except; 685f06ca4afSHartmut Brandt 686f06ca4afSHartmut Brandt memset(&context, 0, sizeof(context)); 687f06ca4afSHartmut Brandt TAILQ_INIT(&context.dlist); 688f06ca4afSHartmut Brandt context.ctx.data = data; 689f06ca4afSHartmut Brandt 690135f7de5SShteryana Shopova snmp_pdu_create_response(pdu, resp); 691f06ca4afSHartmut Brandt 692f06ca4afSHartmut Brandt if (snmp_pdu_encode_header(resp_b, resp)) 693f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 694f06ca4afSHartmut Brandt 695f06ca4afSHartmut Brandt /* 696f06ca4afSHartmut Brandt * 1. Find all nodes, check that they are writeable and 697f06ca4afSHartmut Brandt * that the syntax is ok, copy over the binding to the response. 698f06ca4afSHartmut Brandt */ 699f06ca4afSHartmut Brandt for (i = 0; i < pdu->nbindings; i++) { 700f06ca4afSHartmut Brandt b = &pdu->bindings[i]; 701f06ca4afSHartmut Brandt 702f06ca4afSHartmut Brandt if ((np = context.node[i] = find_node(b, &except)) == NULL) { 703f06ca4afSHartmut Brandt /* not found altogether or LEAF with wrong index */ 704f06ca4afSHartmut Brandt if (TR(SET)) 705f06ca4afSHartmut Brandt snmp_debug("set: node not found %s", 706f06ca4afSHartmut Brandt asn_oid2str_r(&b->var, oidbuf)); 707f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 708f06ca4afSHartmut Brandt pdu->error_index = i + 1; 709f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOSUCHNAME; 710f06ca4afSHartmut Brandt } else if ((np = find_subnode(b)) != NULL) { 711f06ca4afSHartmut Brandt /* 2. intermediate object */ 712f06ca4afSHartmut Brandt pdu->error_index = i + 1; 713f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 714f06ca4afSHartmut Brandt } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 715f06ca4afSHartmut Brandt pdu->error_index = i + 1; 716f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NO_ACCESS; 717f06ca4afSHartmut Brandt } else { 718f06ca4afSHartmut Brandt pdu->error_index = i + 1; 719f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NO_CREATION; 720f06ca4afSHartmut Brandt } 721f06ca4afSHartmut Brandt snmp_pdu_free(resp); 722f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 723f06ca4afSHartmut Brandt } 724f06ca4afSHartmut Brandt /* 725f06ca4afSHartmut Brandt * 2. write/createable? 726f06ca4afSHartmut Brandt * Can check this for leafs only, because in v2 we have 727f06ca4afSHartmut Brandt * to differentiate between NOT_WRITEABLE and NO_CREATION 728f06ca4afSHartmut Brandt * and only the action routine for COLUMNS knows, whether 729f06ca4afSHartmut Brandt * a column exists. 730f06ca4afSHartmut Brandt */ 731f06ca4afSHartmut Brandt if (np->type == SNMP_NODE_LEAF && 732f06ca4afSHartmut Brandt !(np->flags & SNMP_NODE_CANSET)) { 733f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 734f06ca4afSHartmut Brandt pdu->error_index = i + 1; 735f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOSUCHNAME; 736f06ca4afSHartmut Brandt } else { 737f06ca4afSHartmut Brandt pdu->error_index = i + 1; 738f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 739f06ca4afSHartmut Brandt } 740f06ca4afSHartmut Brandt snmp_pdu_free(resp); 741f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 742f06ca4afSHartmut Brandt } 743f06ca4afSHartmut Brandt /* 744f06ca4afSHartmut Brandt * 3. Ensure the right syntax 745f06ca4afSHartmut Brandt */ 746f06ca4afSHartmut Brandt if (np->syntax != b->syntax) { 747f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 748f06ca4afSHartmut Brandt pdu->error_index = i + 1; 749f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 750f06ca4afSHartmut Brandt } else { 751f06ca4afSHartmut Brandt pdu->error_index = i + 1; 752f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_WRONG_TYPE; 753f06ca4afSHartmut Brandt } 754f06ca4afSHartmut Brandt snmp_pdu_free(resp); 755f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 756f06ca4afSHartmut Brandt } 757f06ca4afSHartmut Brandt /* 758f06ca4afSHartmut Brandt * 4. Copy binding 759f06ca4afSHartmut Brandt */ 760f06ca4afSHartmut Brandt if (snmp_value_copy(&resp->bindings[i], b)) { 761f06ca4afSHartmut Brandt pdu->error_index = i + 1; 762f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 763f06ca4afSHartmut Brandt snmp_pdu_free(resp); 764f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 765f06ca4afSHartmut Brandt } 766f06ca4afSHartmut Brandt asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 767f06ca4afSHartmut Brandt if (asnerr == ASN_ERR_EOBUF) { 768f06ca4afSHartmut Brandt pdu->error_index = i + 1; 769f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_TOOBIG; 770f06ca4afSHartmut Brandt snmp_pdu_free(resp); 771f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 772f06ca4afSHartmut Brandt } else if (asnerr != ASN_ERR_OK) { 773f06ca4afSHartmut Brandt pdu->error_index = i + 1; 774f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_GENERR; 775f06ca4afSHartmut Brandt snmp_pdu_free(resp); 776f06ca4afSHartmut Brandt return (SNMP_RET_ERR); 777f06ca4afSHartmut Brandt } 778f06ca4afSHartmut Brandt resp->nbindings++; 779f06ca4afSHartmut Brandt } 780f06ca4afSHartmut Brandt 7818eecd77aSHartmut Brandt context.ctx.code = SNMP_RET_OK; 782f06ca4afSHartmut Brandt 783f06ca4afSHartmut Brandt /* 784f06ca4afSHartmut Brandt * 2. Call the SET method for each node. If a SET fails, rollback 785f06ca4afSHartmut Brandt * everything. Map error codes depending on the version. 786f06ca4afSHartmut Brandt */ 787f06ca4afSHartmut Brandt for (i = 0; i < pdu->nbindings; i++) { 788f06ca4afSHartmut Brandt b = &pdu->bindings[i]; 789f06ca4afSHartmut Brandt np = context.node[i]; 790f06ca4afSHartmut Brandt 791f06ca4afSHartmut Brandt context.ctx.var_index = i + 1; 792f06ca4afSHartmut Brandt context.ctx.scratch = &context.scratch[i]; 793f06ca4afSHartmut Brandt 794f06ca4afSHartmut Brandt ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 795f06ca4afSHartmut Brandt SNMP_OP_SET); 796f06ca4afSHartmut Brandt 797f06ca4afSHartmut Brandt if (TR(SET)) 798f06ca4afSHartmut Brandt snmp_debug("set: action %s returns %d", np->name, ret); 799f06ca4afSHartmut Brandt 800f06ca4afSHartmut Brandt if (pdu->version == SNMP_V1) { 801f06ca4afSHartmut Brandt switch (ret) { 802f06ca4afSHartmut Brandt case SNMP_ERR_NO_ACCESS: 803f06ca4afSHartmut Brandt ret = SNMP_ERR_NOSUCHNAME; 804f06ca4afSHartmut Brandt break; 805f06ca4afSHartmut Brandt case SNMP_ERR_WRONG_TYPE: 806f06ca4afSHartmut Brandt /* should no happen */ 807f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 808f06ca4afSHartmut Brandt break; 809f06ca4afSHartmut Brandt case SNMP_ERR_WRONG_LENGTH: 810f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 811f06ca4afSHartmut Brandt break; 812f06ca4afSHartmut Brandt case SNMP_ERR_WRONG_ENCODING: 813f06ca4afSHartmut Brandt /* should not happen */ 814f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 815f06ca4afSHartmut Brandt break; 816f06ca4afSHartmut Brandt case SNMP_ERR_WRONG_VALUE: 817f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 818f06ca4afSHartmut Brandt break; 819f06ca4afSHartmut Brandt case SNMP_ERR_NO_CREATION: 820f06ca4afSHartmut Brandt ret = SNMP_ERR_NOSUCHNAME; 821f06ca4afSHartmut Brandt break; 822f06ca4afSHartmut Brandt case SNMP_ERR_INCONS_VALUE: 823f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 824f06ca4afSHartmut Brandt break; 825f06ca4afSHartmut Brandt case SNMP_ERR_RES_UNAVAIL: 826f06ca4afSHartmut Brandt ret = SNMP_ERR_GENERR; 827f06ca4afSHartmut Brandt break; 828f06ca4afSHartmut Brandt case SNMP_ERR_COMMIT_FAILED: 829f06ca4afSHartmut Brandt ret = SNMP_ERR_GENERR; 830f06ca4afSHartmut Brandt break; 831f06ca4afSHartmut Brandt case SNMP_ERR_UNDO_FAILED: 832f06ca4afSHartmut Brandt ret = SNMP_ERR_GENERR; 833f06ca4afSHartmut Brandt break; 834f06ca4afSHartmut Brandt case SNMP_ERR_AUTH_ERR: 835f06ca4afSHartmut Brandt /* should not happen */ 836f06ca4afSHartmut Brandt ret = SNMP_ERR_GENERR; 837f06ca4afSHartmut Brandt break; 838f06ca4afSHartmut Brandt case SNMP_ERR_NOT_WRITEABLE: 839f06ca4afSHartmut Brandt ret = SNMP_ERR_NOSUCHNAME; 840f06ca4afSHartmut Brandt break; 841f06ca4afSHartmut Brandt case SNMP_ERR_INCONS_NAME: 842f06ca4afSHartmut Brandt ret = SNMP_ERR_BADVALUE; 843f06ca4afSHartmut Brandt break; 844f06ca4afSHartmut Brandt } 845f06ca4afSHartmut Brandt } 846f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR) { 847f06ca4afSHartmut Brandt pdu->error_index = i + 1; 848f06ca4afSHartmut Brandt pdu->error_status = ret; 849f06ca4afSHartmut Brandt 850f06ca4afSHartmut Brandt rollback(&context, pdu, i); 851f06ca4afSHartmut Brandt snmp_pdu_free(resp); 852f06ca4afSHartmut Brandt 8538eecd77aSHartmut Brandt context.ctx.code = SNMP_RET_ERR; 854f06ca4afSHartmut Brandt 855f06ca4afSHartmut Brandt goto errout; 856f06ca4afSHartmut Brandt } 857f06ca4afSHartmut Brandt } 858f06ca4afSHartmut Brandt 859f06ca4afSHartmut Brandt /* 860f06ca4afSHartmut Brandt * 3. Call dependencies 861f06ca4afSHartmut Brandt */ 862f06ca4afSHartmut Brandt if (TR(SET)) 863f06ca4afSHartmut Brandt snmp_debug("set: set operations ok"); 864f06ca4afSHartmut Brandt 865f06ca4afSHartmut Brandt if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 866f06ca4afSHartmut Brandt pdu->error_status = ret; 867f06ca4afSHartmut Brandt pdu->error_index = context.ctx.var_index; 868f06ca4afSHartmut Brandt 869f06ca4afSHartmut Brandt if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 870f06ca4afSHartmut Brandt if (pdu->version != SNMP_V1) { 871f06ca4afSHartmut Brandt pdu->error_status = SNMP_ERR_UNDO_FAILED; 872f06ca4afSHartmut Brandt pdu->error_index = 0; 873f06ca4afSHartmut Brandt } 874f06ca4afSHartmut Brandt } 875f06ca4afSHartmut Brandt rollback(&context, pdu, i); 876f06ca4afSHartmut Brandt snmp_pdu_free(resp); 877f06ca4afSHartmut Brandt 8788eecd77aSHartmut Brandt context.ctx.code = SNMP_RET_ERR; 879f06ca4afSHartmut Brandt 880f06ca4afSHartmut Brandt goto errout; 881f06ca4afSHartmut Brandt } 882f06ca4afSHartmut Brandt 883f06ca4afSHartmut Brandt /* 884f06ca4afSHartmut Brandt * 4. Commit and copy values from the original packet to the response. 885f06ca4afSHartmut Brandt * This is not the commit operation from RFC 1905 but rather an 886f06ca4afSHartmut Brandt * 'FREE RESOURCES' operation. It shouldn't fail. 887f06ca4afSHartmut Brandt */ 888f06ca4afSHartmut Brandt if (TR(SET)) 889f06ca4afSHartmut Brandt snmp_debug("set: commiting"); 890f06ca4afSHartmut Brandt 891f06ca4afSHartmut Brandt for (i = 0; i < pdu->nbindings; i++) { 892f06ca4afSHartmut Brandt b = &resp->bindings[i]; 893f06ca4afSHartmut Brandt np = context.node[i]; 894f06ca4afSHartmut Brandt 895f06ca4afSHartmut Brandt context.ctx.var_index = i + 1; 896f06ca4afSHartmut Brandt context.ctx.scratch = &context.scratch[i]; 897f06ca4afSHartmut Brandt 898f06ca4afSHartmut Brandt ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 899f06ca4afSHartmut Brandt SNMP_OP_COMMIT); 900f06ca4afSHartmut Brandt 901f06ca4afSHartmut Brandt if (ret != SNMP_ERR_NOERROR) 902f06ca4afSHartmut Brandt snmp_error("set: commit failed (%d) on" 903f06ca4afSHartmut Brandt " variable %s index %u", ret, 904f06ca4afSHartmut Brandt asn_oid2str_r(&b->var, oidbuf), i); 905f06ca4afSHartmut Brandt } 906f06ca4afSHartmut Brandt 907f06ca4afSHartmut Brandt if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 908f06ca4afSHartmut Brandt snmp_error("set: fix_encoding failed"); 909f06ca4afSHartmut Brandt snmp_pdu_free(resp); 9108eecd77aSHartmut Brandt context.ctx.code = SNMP_RET_IGN; 911f06ca4afSHartmut Brandt } 912f06ca4afSHartmut Brandt 913f06ca4afSHartmut Brandt /* 914f06ca4afSHartmut Brandt * Done 915f06ca4afSHartmut Brandt */ 916f06ca4afSHartmut Brandt errout: 9178eecd77aSHartmut Brandt snmp_dep_finish(&context.ctx); 918f06ca4afSHartmut Brandt 919f06ca4afSHartmut Brandt if (TR(SET)) 9208eecd77aSHartmut Brandt snmp_debug("set: returning %d", context.ctx.code); 921f06ca4afSHartmut Brandt 9228eecd77aSHartmut Brandt return (context.ctx.code); 923f06ca4afSHartmut Brandt } 924f06ca4afSHartmut Brandt /* 925f06ca4afSHartmut Brandt * Lookup a dependency. If it doesn't exist, create one 926f06ca4afSHartmut Brandt */ 927f06ca4afSHartmut Brandt struct snmp_dependency * 928f06ca4afSHartmut Brandt snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 929f06ca4afSHartmut Brandt const struct asn_oid *idx, size_t len, snmp_depop_t func) 930f06ca4afSHartmut Brandt { 931f06ca4afSHartmut Brandt struct context *context; 932f06ca4afSHartmut Brandt struct depend *d; 933f06ca4afSHartmut Brandt 934f06ca4afSHartmut Brandt context = (struct context *)(void *) 935f06ca4afSHartmut Brandt ((char *)ctx - offsetof(struct context, ctx)); 936f06ca4afSHartmut Brandt if (TR(DEPEND)) { 937f06ca4afSHartmut Brandt snmp_debug("depend: looking for %s", asn_oid2str(obj)); 938f06ca4afSHartmut Brandt if (idx) 939f06ca4afSHartmut Brandt snmp_debug("depend: index is %s", asn_oid2str(idx)); 940f06ca4afSHartmut Brandt } 941f06ca4afSHartmut Brandt TAILQ_FOREACH(d, &context->dlist, link) 942f06ca4afSHartmut Brandt if (asn_compare_oid(obj, &d->dep.obj) == 0 && 943f06ca4afSHartmut Brandt ((idx == NULL && d->dep.idx.len == 0) || 944f06ca4afSHartmut Brandt (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 945f06ca4afSHartmut Brandt if(TR(DEPEND)) 946f06ca4afSHartmut Brandt snmp_debug("depend: found"); 947f06ca4afSHartmut Brandt return (&d->dep); 948f06ca4afSHartmut Brandt } 949f06ca4afSHartmut Brandt 950f06ca4afSHartmut Brandt if(TR(DEPEND)) 951f06ca4afSHartmut Brandt snmp_debug("depend: creating"); 952f06ca4afSHartmut Brandt 953f06ca4afSHartmut Brandt if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 954f06ca4afSHartmut Brandt return (NULL); 955f06ca4afSHartmut Brandt memset(&d->dep, 0, len); 956f06ca4afSHartmut Brandt 957f06ca4afSHartmut Brandt d->dep.obj = *obj; 958f06ca4afSHartmut Brandt if (idx == NULL) 959f06ca4afSHartmut Brandt d->dep.idx.len = 0; 960f06ca4afSHartmut Brandt else 961f06ca4afSHartmut Brandt d->dep.idx = *idx; 962f06ca4afSHartmut Brandt d->len = len; 963f06ca4afSHartmut Brandt d->func = func; 964f06ca4afSHartmut Brandt 965f06ca4afSHartmut Brandt TAILQ_INSERT_TAIL(&context->dlist, d, link); 966f06ca4afSHartmut Brandt 967f06ca4afSHartmut Brandt return (&d->dep); 968f06ca4afSHartmut Brandt } 969f06ca4afSHartmut Brandt 970f06ca4afSHartmut Brandt /* 971f06ca4afSHartmut Brandt * Make an error response from a PDU. We do this without decoding the 972f06ca4afSHartmut Brandt * variable bindings. This means we can sent the junk back to a caller 973f06ca4afSHartmut Brandt * that has sent us junk in the first place. 974f06ca4afSHartmut Brandt */ 975f06ca4afSHartmut Brandt enum snmp_ret 976f06ca4afSHartmut Brandt snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 977f06ca4afSHartmut Brandt struct asn_buf *resp_b) 978f06ca4afSHartmut Brandt { 97906983448SShteryana Shopova u_char type; 980f06ca4afSHartmut Brandt asn_len_t len; 981f06ca4afSHartmut Brandt struct snmp_pdu resp; 982f06ca4afSHartmut Brandt enum asn_err err; 983f06ca4afSHartmut Brandt enum snmp_code code; 984f06ca4afSHartmut Brandt 98506983448SShteryana Shopova snmp_pdu_create_response(pdu, &resp); 98606983448SShteryana Shopova 987135f7de5SShteryana Shopova if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 988f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 989f06ca4afSHartmut Brandt 99006983448SShteryana Shopova if (pdu->version == SNMP_V3) { 99106983448SShteryana Shopova if (resp.user.priv_proto != SNMP_PRIV_NOPRIV && 99206983448SShteryana Shopova (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK 99306983448SShteryana Shopova || type != ASN_TYPE_OCTETSTRING)) { 99406983448SShteryana Shopova snmp_error("cannot decode encrypted pdu"); 995f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 99606983448SShteryana Shopova } 99706983448SShteryana Shopova 99806983448SShteryana Shopova if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) { 99906983448SShteryana Shopova snmp_error("cannot decode scoped pdu header"); 100006983448SShteryana Shopova return (SNMP_RET_IGN); 100106983448SShteryana Shopova } 100206983448SShteryana Shopova 100306983448SShteryana Shopova len = SNMP_ENGINE_ID_SIZ; 100406983448SShteryana Shopova if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine, 100506983448SShteryana Shopova &len) != ASN_ERR_OK) { 100606983448SShteryana Shopova snmp_error("cannot decode msg context engine"); 100706983448SShteryana Shopova return (SNMP_RET_IGN); 100806983448SShteryana Shopova } 100906983448SShteryana Shopova resp.context_engine_len = len; 101006983448SShteryana Shopova len = SNMP_CONTEXT_NAME_SIZ; 101106983448SShteryana Shopova if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name, 101206983448SShteryana Shopova &len) != ASN_ERR_OK) { 101306983448SShteryana Shopova snmp_error("cannot decode msg context name"); 101406983448SShteryana Shopova return (SNMP_RET_IGN); 101506983448SShteryana Shopova } 101606983448SShteryana Shopova resp.context_name[len] = '\0'; 101706983448SShteryana Shopova } 101806983448SShteryana Shopova 101906983448SShteryana Shopova 102006983448SShteryana Shopova if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) { 102106983448SShteryana Shopova snmp_error("cannot get pdu header"); 102206983448SShteryana Shopova return (SNMP_RET_IGN); 102306983448SShteryana Shopova } 102406983448SShteryana Shopova 102506983448SShteryana Shopova if ((type & ~ASN_TYPE_MASK) != 102606983448SShteryana Shopova (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { 102706983448SShteryana Shopova snmp_error("bad pdu header tag"); 102806983448SShteryana Shopova return (SNMP_RET_IGN); 102906983448SShteryana Shopova } 1030f06ca4afSHartmut Brandt 1031f06ca4afSHartmut Brandt err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 1032f06ca4afSHartmut Brandt if (ASN_ERR_STOPPED(err)) 1033f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 1034f06ca4afSHartmut Brandt if (pdu_b->asn_len < len) 1035f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 1036f06ca4afSHartmut Brandt pdu_b->asn_len = len; 1037f06ca4afSHartmut Brandt 1038f06ca4afSHartmut Brandt /* now we have the bindings left - construct new message */ 1039f06ca4afSHartmut Brandt resp.error_status = pdu->error_status; 1040f06ca4afSHartmut Brandt resp.error_index = pdu->error_index; 1041f06ca4afSHartmut Brandt resp.type = SNMP_PDU_RESPONSE; 1042f06ca4afSHartmut Brandt 1043f06ca4afSHartmut Brandt code = snmp_pdu_encode_header(resp_b, &resp); 1044f06ca4afSHartmut Brandt if (code != SNMP_CODE_OK) 1045f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 1046f06ca4afSHartmut Brandt 1047f06ca4afSHartmut Brandt if (pdu_b->asn_len > resp_b->asn_len) 1048f06ca4afSHartmut Brandt /* too short */ 1049f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 1050f06ca4afSHartmut Brandt (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 1051f06ca4afSHartmut Brandt resp_b->asn_len -= pdu_b->asn_len; 1052f06ca4afSHartmut Brandt resp_b->asn_ptr += pdu_b->asn_len; 1053f06ca4afSHartmut Brandt 1054f06ca4afSHartmut Brandt code = snmp_fix_encoding(resp_b, &resp); 1055f06ca4afSHartmut Brandt if (code != SNMP_CODE_OK) 1056f06ca4afSHartmut Brandt return (SNMP_RET_IGN); 1057f06ca4afSHartmut Brandt 1058f06ca4afSHartmut Brandt return (SNMP_RET_OK); 1059f06ca4afSHartmut Brandt } 1060f06ca4afSHartmut Brandt 1061f06ca4afSHartmut Brandt static void 1062f06ca4afSHartmut Brandt snmp_debug_func(const char *fmt, ...) 1063f06ca4afSHartmut Brandt { 1064f06ca4afSHartmut Brandt va_list ap; 1065f06ca4afSHartmut Brandt 1066f06ca4afSHartmut Brandt va_start(ap, fmt); 1067f06ca4afSHartmut Brandt vfprintf(stderr, fmt, ap); 1068f06ca4afSHartmut Brandt va_end(ap); 1069f06ca4afSHartmut Brandt fprintf(stderr, "\n"); 1070f06ca4afSHartmut Brandt } 1071