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