snmp.c revision 122394
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 of this software and documentation and use in source and 9 * binary forms, with or without modification, are permitted provided that 10 * the following conditions are met: 11 * 12 * 1. Redistributions of source code or documentation must retain the above 13 * copyright notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS 22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 25 * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $Begemot: bsnmp/lib/snmp.c,v 1.34 2003/01/28 13:44:34 hbb Exp $ 34 * 35 * SNMP 36 */ 37#include <sys/types.h> 38#include <sys/socket.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <stddef.h> 42#include <stdarg.h> 43#include <string.h> 44#include <ctype.h> 45#include <netdb.h> 46#include <errno.h> 47 48#include "asn1.h" 49#include "snmp.h" 50#include "snmppriv.h" 51 52static void snmp_error_func(const char *, ...); 53static void snmp_printf_func(const char *, ...); 54 55void (*snmp_error)(const char *, ...) = snmp_error_func; 56void (*snmp_printf)(const char *, ...) = snmp_printf_func; 57 58 59/* 60 * Get the next variable binding from the list. 61 * ASN errors on the sequence or the OID are always fatal. 62 */ 63static enum asn_err 64get_var_binding(struct asn_buf *b, struct snmp_value *binding) 65{ 66 u_char type; 67 asn_len_t len, trailer; 68 enum asn_err err; 69 70 if (asn_get_sequence(b, &len) != ASN_ERR_OK) { 71 snmp_error("cannot parse varbind header"); 72 return (ASN_ERR_FAILED); 73 } 74 75 /* temporary truncate the length so that the parser does not 76 * eat up bytes behind the sequence in the case the encoding is 77 * wrong of inner elements. */ 78 trailer = b->asn_len - len; 79 b->asn_len = len; 80 81 if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) { 82 snmp_error("cannot parse binding objid"); 83 return (ASN_ERR_FAILED); 84 } 85 if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { 86 snmp_error("cannot parse binding value header"); 87 return (ASN_ERR_FAILED); 88 } 89 90 switch (type) { 91 92 case ASN_TYPE_NULL: 93 binding->syntax = SNMP_SYNTAX_NULL; 94 err = asn_get_null_raw(b, len); 95 break; 96 97 case ASN_TYPE_INTEGER: 98 binding->syntax = SNMP_SYNTAX_INTEGER; 99 err = asn_get_integer_raw(b, len, &binding->v.integer); 100 break; 101 102 case ASN_TYPE_OCTETSTRING: 103 binding->syntax = SNMP_SYNTAX_OCTETSTRING; 104 binding->v.octetstring.octets = malloc(len); 105 if (binding->v.octetstring.octets == NULL) { 106 snmp_error("%s", strerror(errno)); 107 return (ASN_ERR_FAILED); 108 } 109 binding->v.octetstring.len = len; 110 err = asn_get_octetstring_raw(b, len, 111 binding->v.octetstring.octets, 112 &binding->v.octetstring.len); 113 if (ASN_ERR_STOPPED(err)) { 114 free(binding->v.octetstring.octets); 115 binding->v.octetstring.octets = NULL; 116 } 117 break; 118 119 case ASN_TYPE_OBJID: 120 binding->syntax = SNMP_SYNTAX_OID; 121 err = asn_get_objid_raw(b, len, &binding->v.oid); 122 break; 123 124 case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS: 125 binding->syntax = SNMP_SYNTAX_IPADDRESS; 126 err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress); 127 break; 128 129 case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS: 130 binding->syntax = SNMP_SYNTAX_TIMETICKS; 131 err = asn_get_uint32_raw(b, len, &binding->v.uint32); 132 break; 133 134 case ASN_CLASS_APPLICATION|ASN_APP_COUNTER: 135 binding->syntax = SNMP_SYNTAX_COUNTER; 136 err = asn_get_uint32_raw(b, len, &binding->v.uint32); 137 break; 138 139 case ASN_CLASS_APPLICATION|ASN_APP_GAUGE: 140 binding->syntax = SNMP_SYNTAX_GAUGE; 141 err = asn_get_uint32_raw(b, len, &binding->v.uint32); 142 break; 143 144 case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64: 145 binding->syntax = SNMP_SYNTAX_COUNTER64; 146 err = asn_get_counter64_raw(b, len, &binding->v.counter64); 147 break; 148 149 case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT: 150 binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT; 151 err = asn_get_null_raw(b, len); 152 break; 153 154 case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE: 155 binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 156 err = asn_get_null_raw(b, len); 157 break; 158 159 case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW: 160 binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 161 err = asn_get_null_raw(b, len); 162 break; 163 164 default: 165 if ((err = asn_skip(b, len)) == ASN_ERR_OK) 166 err = ASN_ERR_TAG; 167 snmp_error("bad binding value type 0x%x", type); 168 break; 169 } 170 171 if (ASN_ERR_STOPPED(err)) { 172 snmp_error("cannot parse binding value"); 173 return (err); 174 } 175 176 if (b->asn_len != 0) 177 snmp_error("ignoring junk at end of binding"); 178 179 b->asn_len = trailer; 180 181 return (err); 182} 183 184/* 185 * Parse the different PDUs contents. Any ASN error in the outer components 186 * are fatal. Only errors in variable values may be tolerated. If all 187 * components can be parsed it returns either ASN_ERR_OK or the first 188 * error that was found. 189 */ 190enum asn_err 191snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) 192{ 193 if (pdu->type == SNMP_PDU_TRAP) { 194 if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) { 195 snmp_error("cannot parse trap enterprise"); 196 return (ASN_ERR_FAILED); 197 } 198 if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) { 199 snmp_error("cannot parse trap agent address"); 200 return (ASN_ERR_FAILED); 201 } 202 if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) { 203 snmp_error("cannot parse 'generic-trap'"); 204 return (ASN_ERR_FAILED); 205 } 206 if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) { 207 snmp_error("cannot parse 'specific-trap'"); 208 return (ASN_ERR_FAILED); 209 } 210 if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) { 211 snmp_error("cannot parse trap 'time-stamp'"); 212 return (ASN_ERR_FAILED); 213 } 214 } else { 215 if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) { 216 snmp_error("cannot parse 'request-id'"); 217 return (ASN_ERR_FAILED); 218 } 219 if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) { 220 snmp_error("cannot parse 'error_status'"); 221 return (ASN_ERR_FAILED); 222 } 223 if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) { 224 snmp_error("cannot parse 'error_index'"); 225 return (ASN_ERR_FAILED); 226 } 227 } 228 229 if (asn_get_sequence(b, lenp) != ASN_ERR_OK) { 230 snmp_error("cannot get varlist header"); 231 return (ASN_ERR_FAILED); 232 } 233 234 return (ASN_ERR_OK); 235} 236 237static enum asn_err 238parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) 239{ 240 asn_len_t len, trailer; 241 struct snmp_value *v; 242 enum asn_err err, err1; 243 244 err = snmp_parse_pdus_hdr(b, pdu, &len); 245 if (ASN_ERR_STOPPED(err)) 246 return (err); 247 248 trailer = b->asn_len - len; 249 250 v = pdu->bindings; 251 err = ASN_ERR_OK; 252 while (b->asn_len != 0) { 253 if (pdu->nbindings == SNMP_MAX_BINDINGS) { 254 snmp_error("too many bindings (> %u) in PDU", 255 SNMP_MAX_BINDINGS); 256 return (ASN_ERR_FAILED); 257 } 258 err1 = get_var_binding(b, v); 259 if (ASN_ERR_STOPPED(err1)) 260 return (ASN_ERR_FAILED); 261 if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) { 262 err = err1; 263 *ip = pdu->nbindings + 1; 264 } 265 pdu->nbindings++; 266 v++; 267 } 268 269 b->asn_len = trailer; 270 271 return (err); 272} 273 274/* 275 * Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'. 276 */ 277enum asn_err 278snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) 279{ 280 int32_t version; 281 u_char type; 282 u_int comm_len; 283 284 if (asn_get_integer(b, &version) != ASN_ERR_OK) { 285 snmp_error("cannot decode version"); 286 return (ASN_ERR_FAILED); 287 } 288 289 if (version == 0) { 290 pdu->version = SNMP_V1; 291 } else if (version == 1) { 292 pdu->version = SNMP_V2c; 293 } else { 294 pdu->version = SNMP_Verr; 295 snmp_error("unsupported SNMP version"); 296 return (ASN_ERR_TAG); 297 } 298 299 comm_len = SNMP_COMMUNITY_MAXLEN; 300 if (asn_get_octetstring(b, (u_char *)pdu->community, 301 &comm_len) != ASN_ERR_OK) { 302 snmp_error("cannot decode community"); 303 return (ASN_ERR_FAILED); 304 } 305 pdu->community[comm_len] = '\0'; 306 307 if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) { 308 snmp_error("cannot get pdu header"); 309 return (ASN_ERR_FAILED); 310 } 311 if ((type & ~ASN_TYPE_MASK) != 312 (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { 313 snmp_error("bad pdu header tag"); 314 return (ASN_ERR_FAILED); 315 } 316 pdu->type = type & ASN_TYPE_MASK; 317 318 switch (pdu->type) { 319 320 case SNMP_PDU_GET: 321 case SNMP_PDU_GETNEXT: 322 case SNMP_PDU_RESPONSE: 323 case SNMP_PDU_SET: 324 break; 325 326 case SNMP_PDU_TRAP: 327 if (pdu->version != SNMP_V1) { 328 snmp_error("bad pdu type %u", pdu->type); 329 return (ASN_ERR_FAILED); 330 } 331 break; 332 333 case SNMP_PDU_GETBULK: 334 case SNMP_PDU_INFORM: 335 case SNMP_PDU_TRAP2: 336 case SNMP_PDU_REPORT: 337 if (pdu->version == SNMP_V1) { 338 snmp_error("bad pdu type %u", pdu->type); 339 return (ASN_ERR_FAILED); 340 } 341 break; 342 343 default: 344 snmp_error("bad pdu type %u", pdu->type); 345 return (ASN_ERR_FAILED); 346 } 347 348 349 if (*lenp > b->asn_len) { 350 snmp_error("pdu length too long"); 351 return (ASN_ERR_FAILED); 352 } 353 354 return (ASN_ERR_OK); 355} 356 357static enum asn_err 358parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) 359{ 360 enum asn_err err; 361 asn_len_t len, trailer; 362 363 err = snmp_parse_message_hdr(b, pdu, &len); 364 if (ASN_ERR_STOPPED(err)) 365 return (err); 366 367 trailer = b->asn_len - len; 368 b->asn_len = len; 369 370 err = parse_pdus(b, pdu, ip); 371 if (ASN_ERR_STOPPED(err)) 372 return (ASN_ERR_FAILED); 373 374 if (b->asn_len != 0) 375 snmp_error("ignoring trailing junk after pdu"); 376 377 b->asn_len = trailer; 378 379 return (err); 380} 381 382/* 383 * Decode the PDU except for the variable bindings itself. 384 * If decoding fails because of a bad binding, but the rest can be 385 * decoded, ip points to the index of the failed variable (errors 386 * OORANGE, BADLEN or BADVERS). 387 */ 388enum snmp_code 389snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) 390{ 391 asn_len_t len; 392 393 memset(pdu, 0, sizeof(*pdu)); 394 395 if (asn_get_sequence(b, &len) != ASN_ERR_OK) { 396 snmp_error("cannot decode pdu header"); 397 return (SNMP_CODE_FAILED); 398 } 399 if (b->asn_len < len) { 400 snmp_error("outer sequence value too short"); 401 return (SNMP_CODE_FAILED); 402 } 403 if (b->asn_len != len) { 404 snmp_error("ignoring trailing junk in message"); 405 b->asn_len = len; 406 } 407 408 switch (parse_message(b, pdu, ip)) { 409 410 case ASN_ERR_OK: 411 return (SNMP_CODE_OK); 412 413 case ASN_ERR_FAILED: 414 case ASN_ERR_EOBUF: 415 snmp_pdu_free(pdu); 416 return (SNMP_CODE_FAILED); 417 418 case ASN_ERR_BADLEN: 419 return (SNMP_CODE_BADLEN); 420 421 case ASN_ERR_RANGE: 422 return (SNMP_CODE_OORANGE); 423 424 case ASN_ERR_TAG: 425 if (pdu->version == SNMP_Verr) 426 return (SNMP_CODE_BADVERS); 427 else 428 return (SNMP_CODE_BADENC); 429 } 430 431 return (SNMP_CODE_OK); 432} 433 434/* 435 * Encode the SNMP PDU without the variable bindings field. 436 * We do this the rather uneffective way by 437 * moving things around and assuming that the length field will never 438 * use more than 2 bytes. 439 * We need a number of pointers to apply the fixes afterwards. 440 */ 441enum snmp_code 442snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu) 443{ 444 enum asn_err err; 445 446 if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), 447 &pdu->outer_ptr) != ASN_ERR_OK) 448 return (SNMP_CODE_FAILED); 449 450 if (pdu->version == SNMP_V1) 451 err = asn_put_integer(b, 0); 452 else if (pdu->version == SNMP_V2c) 453 err = asn_put_integer(b, 1); 454 else 455 return (SNMP_CODE_BADVERS); 456 if (err != ASN_ERR_OK) 457 return (SNMP_CODE_FAILED); 458 459 if (asn_put_octetstring(b, (u_char *)pdu->community, 460 strlen(pdu->community)) != ASN_ERR_OK) 461 return (SNMP_CODE_FAILED); 462 463 if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT | 464 pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK) 465 return (SNMP_CODE_FAILED); 466 467 if (pdu->type == SNMP_PDU_TRAP) { 468 if (pdu->version != SNMP_V1 || 469 asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK || 470 asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK || 471 asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK || 472 asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK || 473 asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK) 474 return (SNMP_CODE_FAILED); 475 } else { 476 if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK || 477 pdu->type == SNMP_PDU_INFORM || 478 pdu->type == SNMP_PDU_TRAP2 || 479 pdu->type == SNMP_PDU_REPORT)) 480 return (SNMP_CODE_FAILED); 481 482 if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK || 483 asn_put_integer(b, pdu->error_status) != ASN_ERR_OK || 484 asn_put_integer(b, pdu->error_index) != ASN_ERR_OK) 485 return (SNMP_CODE_FAILED); 486 } 487 488 if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), 489 &pdu->vars_ptr) != ASN_ERR_OK) 490 return (SNMP_CODE_FAILED); 491 492 return (SNMP_CODE_OK); 493} 494 495enum snmp_code 496snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu) 497{ 498 if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK || 499 asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK || 500 asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK) 501 return (SNMP_CODE_FAILED); 502 return (SNMP_CODE_OK); 503} 504 505/* 506 * Encode a binding. Caller must ensure, that the syntax is ok for that version. 507 * Be sure not to cobber b, when something fails. 508 */ 509enum asn_err 510snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding) 511{ 512 u_char *ptr; 513 enum asn_err err; 514 struct asn_buf save = *b; 515 516 if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | 517 ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) { 518 *b = save; 519 return (err); 520 } 521 522 if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) { 523 *b = save; 524 return (err); 525 } 526 527 switch (binding->syntax) { 528 529 case SNMP_SYNTAX_NULL: 530 err = asn_put_null(b); 531 break; 532 533 case SNMP_SYNTAX_INTEGER: 534 err = asn_put_integer(b, binding->v.integer); 535 break; 536 537 case SNMP_SYNTAX_OCTETSTRING: 538 err = asn_put_octetstring(b, binding->v.octetstring.octets, 539 binding->v.octetstring.len); 540 break; 541 542 case SNMP_SYNTAX_OID: 543 err = asn_put_objid(b, &binding->v.oid); 544 break; 545 546 case SNMP_SYNTAX_IPADDRESS: 547 err = asn_put_ipaddress(b, binding->v.ipaddress); 548 break; 549 550 case SNMP_SYNTAX_TIMETICKS: 551 err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32); 552 break; 553 554 case SNMP_SYNTAX_COUNTER: 555 err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32); 556 break; 557 558 case SNMP_SYNTAX_GAUGE: 559 err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32); 560 break; 561 562 case SNMP_SYNTAX_COUNTER64: 563 err = asn_put_counter64(b, binding->v.counter64); 564 break; 565 566 case SNMP_SYNTAX_NOSUCHOBJECT: 567 err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT); 568 break; 569 570 case SNMP_SYNTAX_NOSUCHINSTANCE: 571 err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE); 572 break; 573 574 case SNMP_SYNTAX_ENDOFMIBVIEW: 575 err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW); 576 break; 577 } 578 579 if (err != ASN_ERR_OK) { 580 *b = save; 581 return (err); 582 } 583 584 err = asn_commit_header(b, ptr); 585 if (err != ASN_ERR_OK) { 586 *b = save; 587 return (err); 588 } 589 590 return (ASN_ERR_OK); 591} 592 593/* 594 * Encode an PDU. 595 */ 596enum snmp_code 597snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b) 598{ 599 u_int idx; 600 enum snmp_code err; 601 602 if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK) 603 return (err); 604 for (idx = 0; idx < pdu->nbindings; idx++) 605 if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx])) 606 != ASN_ERR_OK) 607 return (SNMP_CODE_FAILED); 608 609 return (snmp_fix_encoding(resp_b, pdu)); 610} 611 612static void 613dump_binding(const struct snmp_value *b) 614{ 615 u_int i; 616 char buf[ASN_OIDSTRLEN]; 617 618 snmp_printf("%s=", asn_oid2str_r(&b->var, buf)); 619 switch (b->syntax) { 620 621 case SNMP_SYNTAX_NULL: 622 snmp_printf("NULL"); 623 break; 624 625 case SNMP_SYNTAX_INTEGER: 626 snmp_printf("INTEGER %d", b->v.integer); 627 break; 628 629 case SNMP_SYNTAX_OCTETSTRING: 630 snmp_printf("OCTET STRING %lu:", b->v.octetstring.len); 631 for (i = 0; i < b->v.octetstring.len; i++) 632 snmp_printf(" %02x", b->v.octetstring.octets[i]); 633 break; 634 635 case SNMP_SYNTAX_OID: 636 snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf)); 637 break; 638 639 case SNMP_SYNTAX_IPADDRESS: 640 snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0], 641 b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]); 642 break; 643 644 case SNMP_SYNTAX_COUNTER: 645 snmp_printf("COUNTER %u", b->v.uint32); 646 break; 647 648 case SNMP_SYNTAX_GAUGE: 649 snmp_printf("GAUGE %u", b->v.uint32); 650 break; 651 652 case SNMP_SYNTAX_TIMETICKS: 653 snmp_printf("TIMETICKS %u", b->v.uint32); 654 break; 655 656 case SNMP_SYNTAX_COUNTER64: 657 snmp_printf("COUNTER64 %lld", b->v.counter64); 658 break; 659 660 case SNMP_SYNTAX_NOSUCHOBJECT: 661 snmp_printf("NoSuchObject"); 662 break; 663 664 case SNMP_SYNTAX_NOSUCHINSTANCE: 665 snmp_printf("NoSuchInstance"); 666 break; 667 668 case SNMP_SYNTAX_ENDOFMIBVIEW: 669 snmp_printf("EndOfMibView"); 670 break; 671 672 default: 673 snmp_printf("UNKNOWN SYNTAX %u", b->syntax); 674 break; 675 } 676} 677 678static __inline void 679dump_bindings(const struct snmp_pdu *pdu) 680{ 681 u_int i; 682 683 for (i = 0; i < pdu->nbindings; i++) { 684 snmp_printf(" [%u]: ", i); 685 dump_binding(&pdu->bindings[i]); 686 snmp_printf("\n"); 687 } 688} 689 690static __inline void 691dump_notrap(const struct snmp_pdu *pdu) 692{ 693 snmp_printf(" request_id=%d", pdu->request_id); 694 snmp_printf(" error_status=%d", pdu->error_status); 695 snmp_printf(" error_index=%d\n", pdu->error_index); 696 dump_bindings(pdu); 697} 698 699void 700snmp_pdu_dump(const struct snmp_pdu *pdu) 701{ 702 char buf[ASN_OIDSTRLEN]; 703 const char *vers; 704 static const char *types[] = { 705 [SNMP_PDU_GET] = "GET", 706 [SNMP_PDU_GETNEXT] = "GETNEXT", 707 [SNMP_PDU_RESPONSE] = "RESPONSE", 708 [SNMP_PDU_SET] = "SET", 709 [SNMP_PDU_TRAP] = "TRAPv1", 710 [SNMP_PDU_GETBULK] = "GETBULK", 711 [SNMP_PDU_INFORM] = "INFORM", 712 [SNMP_PDU_TRAP2] = "TRAPv2", 713 [SNMP_PDU_REPORT] = "REPORT", 714 }; 715 716 if (pdu->version == SNMP_V1) 717 vers = "SNMPv1"; 718 else if (pdu->version == SNMP_V2c) 719 vers = "SNMPv2c"; 720 else 721 vers = "v?"; 722 723 switch (pdu->type) { 724 case SNMP_PDU_TRAP: 725 snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); 726 snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf)); 727 snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0], 728 pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]); 729 snmp_printf(" generic_trap=%d", pdu->generic_trap); 730 snmp_printf(" specific_trap=%d", pdu->specific_trap); 731 snmp_printf(" time-stamp=%u\n", pdu->time_stamp); 732 dump_bindings(pdu); 733 break; 734 735 case SNMP_PDU_GET: 736 case SNMP_PDU_GETNEXT: 737 case SNMP_PDU_RESPONSE: 738 case SNMP_PDU_SET: 739 case SNMP_PDU_GETBULK: 740 case SNMP_PDU_INFORM: 741 case SNMP_PDU_TRAP2: 742 case SNMP_PDU_REPORT: 743 snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); 744 dump_notrap(pdu); 745 break; 746 747 default: 748 snmp_printf("bad pdu type %u\n", pdu->type); 749 break; 750 } 751} 752 753void 754snmp_value_free(struct snmp_value *value) 755{ 756 if (value->syntax == SNMP_SYNTAX_OCTETSTRING) 757 free(value->v.octetstring.octets); 758 value->syntax = SNMP_SYNTAX_NULL; 759} 760 761int 762snmp_value_copy(struct snmp_value *to, const struct snmp_value *from) 763{ 764 to->var = from->var; 765 to->syntax = from->syntax; 766 767 if (from->syntax == SNMP_SYNTAX_OCTETSTRING) { 768 if ((to->v.octetstring.len = from->v.octetstring.len) == 0) 769 to->v.octetstring.octets = NULL; 770 else { 771 to->v.octetstring.octets = malloc(to->v.octetstring.len); 772 if (to->v.octetstring.octets == NULL) 773 return (-1); 774 (void)memcpy(to->v.octetstring.octets, 775 from->v.octetstring.octets, to->v.octetstring.len); 776 } 777 } else 778 to->v = from->v; 779 return (0); 780} 781 782void 783snmp_pdu_free(struct snmp_pdu *pdu) 784{ 785 u_int i; 786 787 for (i = 0; i < pdu->nbindings; i++) 788 snmp_value_free(&pdu->bindings[i]); 789} 790 791/* 792 * Parse an ASCII SNMP value into the binary form 793 */ 794int 795snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v) 796{ 797 char *end; 798 799 switch (syntax) { 800 801 case SNMP_SYNTAX_NULL: 802 case SNMP_SYNTAX_NOSUCHOBJECT: 803 case SNMP_SYNTAX_NOSUCHINSTANCE: 804 case SNMP_SYNTAX_ENDOFMIBVIEW: 805 if (*str != '\0') 806 return (-1); 807 return (0); 808 809 case SNMP_SYNTAX_INTEGER: 810 v->integer = strtoll(str, &end, 0); 811 if (*end != '\0') 812 return (-1); 813 return (0); 814 815 case SNMP_SYNTAX_OCTETSTRING: 816 { 817 u_long len; /* actual length of string */ 818 u_long alloc; /* allocate length of string */ 819 u_char *octs; /* actual octets */ 820 u_long oct; /* actual octet */ 821 u_char *nocts; /* to avoid memory leak */ 822 u_char c; /* actual character */ 823 824# define STUFFC(C) \ 825 if (alloc == len) { \ 826 alloc += 100; \ 827 if ((nocts = realloc(octs, alloc)) == NULL) { \ 828 free(octs); \ 829 return (-1); \ 830 } \ 831 octs = nocts; \ 832 } \ 833 octs[len++] = (C); 834 835 len = alloc = 0; 836 octs = NULL; 837 838 if (*str == '"') { 839 str++; 840 while((c = *str++) != '\0') { 841 if (c == '"') { 842 if (*str != '\0') { 843 free(octs); 844 return (-1); 845 } 846 break; 847 } 848 if (c == '\\') { 849 switch (c = *str++) { 850 851 case '\\': 852 break; 853 case 'a': 854 c = '\a'; 855 break; 856 case 'b': 857 c = '\b'; 858 break; 859 case 'f': 860 c = '\f'; 861 break; 862 case 'n': 863 c = '\n'; 864 break; 865 case 'r': 866 c = '\r'; 867 break; 868 case 't': 869 c = '\t'; 870 break; 871 case 'v': 872 c = '\v'; 873 break; 874 case 'x': 875 c = 0; 876 if (!isxdigit(*str)) 877 break; 878 if (isdigit(*str)) 879 c = *str++ - '0'; 880 else if (isupper(*str)) 881 c = *str++ - 'A' + 10; 882 else 883 c = *str++ - 'a' + 10; 884 if (!isxdigit(*str)) 885 break; 886 if (isdigit(*str)) 887 c += *str++ - '0'; 888 else if (isupper(*str)) 889 c += *str++ - 'A' + 10; 890 else 891 c += *str++ - 'a' + 10; 892 break; 893 case '0': case '1': case '2': 894 case '3': case '4': case '5': 895 case '6': case '7': 896 c = *str++ - '0'; 897 if (*str < '0' || *str > '7') 898 break; 899 c = *str++ - '0'; 900 if (*str < '0' || *str > '7') 901 break; 902 c = *str++ - '0'; 903 break; 904 default: 905 break; 906 } 907 } 908 STUFFC(c); 909 } 910 } else { 911 while (*str != '\0') { 912 oct = strtoul(str, &end, 16); 913 str = end; 914 if (oct > 0xff) { 915 free(octs); 916 return (-1); 917 } 918 STUFFC(oct); 919 if (*str == ':') 920 str++; 921 else if(*str != '\0') { 922 free(octs); 923 return (-1); 924 } 925 } 926 } 927 v->octetstring.octets = octs; 928 v->octetstring.len = len; 929 return (0); 930# undef STUFFC 931 } 932 933 case SNMP_SYNTAX_OID: 934 { 935 u_long subid; 936 937 v->oid.len = 0; 938 939 for (;;) { 940 if (v->oid.len == ASN_MAXOIDLEN) 941 return (-1); 942 subid = strtoul(str, &end, 10); 943 str = end; 944 if (subid > ASN_MAXID) 945 return (-1); 946 v->oid.subs[v->oid.len++] = (asn_subid_t)subid; 947 if (*str == '\0') 948 break; 949 if (*str != '.') 950 return (-1); 951 str++; 952 } 953 return (0); 954 } 955 956 case SNMP_SYNTAX_IPADDRESS: 957 { 958 struct hostent *he; 959 u_long ip[4]; 960 int n; 961 962 if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2], 963 &ip[3], &n) == 4 && (size_t)n == strlen(str) && 964 ip[0] <= 0xff && ip[1] <= 0xff && 965 ip[2] <= 0xff && ip[3] <= 0xff) { 966 v->ipaddress[0] = (u_char)ip[0]; 967 v->ipaddress[1] = (u_char)ip[1]; 968 v->ipaddress[2] = (u_char)ip[2]; 969 v->ipaddress[3] = (u_char)ip[3]; 970 return (0); 971 } 972 973 if ((he = gethostbyname(str)) == NULL) 974 return (-1); 975 if (he->h_addrtype != AF_INET) 976 return (-1); 977 978 v->ipaddress[0] = he->h_addr[0]; 979 v->ipaddress[1] = he->h_addr[1]; 980 v->ipaddress[2] = he->h_addr[2]; 981 v->ipaddress[3] = he->h_addr[3]; 982 return (0); 983 } 984 985 case SNMP_SYNTAX_COUNTER: 986 case SNMP_SYNTAX_GAUGE: 987 case SNMP_SYNTAX_TIMETICKS: 988 { 989 u_int64_t sub; 990 991 sub = strtoull(str, &end, 0); 992 if (*end != '\0' || sub > 0xffffffff) 993 return (-1); 994 v->uint32 = (u_int32_t)sub; 995 return (0); 996 } 997 998 case SNMP_SYNTAX_COUNTER64: 999 v->counter64 = strtoull(str, &end, 0); 1000 if (*end != '\0') 1001 return (-1); 1002 return (0); 1003 } 1004 abort(); 1005} 1006 1007static void 1008snmp_error_func(const char *fmt, ...) 1009{ 1010 va_list ap; 1011 1012 va_start(ap, fmt); 1013 fprintf(stderr, "SNMP: "); 1014 vfprintf(stderr, fmt, ap); 1015 fprintf(stderr, "\n"); 1016 va_end(ap); 1017} 1018 1019static void 1020snmp_printf_func(const char *fmt, ...) 1021{ 1022 va_list ap; 1023 1024 va_start(ap, fmt); 1025 vfprintf(stderr, fmt, ap); 1026 va_end(ap); 1027} 1028