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