1 /** 2 * @file 3 * Abstract Syntax Notation One (ISO 8824, 8825) encoding 4 * 5 * @todo not optimised (yet), favor correctness over speed, favor speed over size 6 */ 7 8 /* 9 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without modification, 13 * are permitted provided that the following conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright notice, 16 * this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright notice, 18 * this list of conditions and the following disclaimer in the documentation 19 * and/or other materials provided with the distribution. 20 * 3. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 26 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 28 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 32 * OF SUCH DAMAGE. 33 * 34 * Author: Christiaan Simons <christiaan.simons (at) axon.tv> 35 */ 36 37 #include "lwip/opt.h" 38 39 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ 40 41 #include "lwip/snmp_asn1.h" 42 43 /** 44 * Returns octet count for length. 45 * 46 * @param length 47 * @param octets_needed points to the return value 48 */ 49 void 50 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) 51 { 52 if (length < 0x80U) 53 { 54 *octets_needed = 1; 55 } 56 else if (length < 0x100U) 57 { 58 *octets_needed = 2; 59 } 60 else 61 { 62 *octets_needed = 3; 63 } 64 } 65 66 /** 67 * Returns octet count for an u32_t. 68 * 69 * @param value 70 * @param octets_needed points to the return value 71 * 72 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 73 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 74 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 75 */ 76 void 77 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) 78 { 79 if (value < 0x80UL) 80 { 81 *octets_needed = 1; 82 } 83 else if (value < 0x8000UL) 84 { 85 *octets_needed = 2; 86 } 87 else if (value < 0x800000UL) 88 { 89 *octets_needed = 3; 90 } 91 else if (value < 0x80000000UL) 92 { 93 *octets_needed = 4; 94 } 95 else 96 { 97 *octets_needed = 5; 98 } 99 } 100 101 /** 102 * Returns octet count for an s32_t. 103 * 104 * @param value 105 * @param octets_needed points to the return value 106 * 107 * @note ASN coded integers are _always_ signed. 108 */ 109 void 110 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) 111 { 112 if (value < 0) 113 { 114 value = ~value; 115 } 116 if (value < 0x80L) 117 { 118 *octets_needed = 1; 119 } 120 else if (value < 0x8000L) 121 { 122 *octets_needed = 2; 123 } 124 else if (value < 0x800000L) 125 { 126 *octets_needed = 3; 127 } 128 else 129 { 130 *octets_needed = 4; 131 } 132 } 133 134 /** 135 * Returns octet count for an object identifier. 136 * 137 * @param ident_len object identifier array length 138 * @param ident points to object identifier array 139 * @param octets_needed points to the return value 140 */ 141 void 142 snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) 143 { 144 s32_t sub_id; 145 u8_t cnt; 146 147 cnt = 0; 148 if (ident_len > 1) 149 { 150 /* compressed prefix in one octet */ 151 cnt++; 152 ident_len -= 2; 153 ident += 2; 154 } 155 while(ident_len > 0) 156 { 157 ident_len--; 158 sub_id = *ident; 159 160 sub_id >>= 7; 161 cnt++; 162 while(sub_id > 0) 163 { 164 sub_id >>= 7; 165 cnt++; 166 } 167 ident++; 168 } 169 *octets_needed = cnt; 170 } 171 172 /** 173 * Encodes ASN type field into a pbuf chained ASN1 msg. 174 * 175 * @param p points to output pbuf to encode value into 176 * @param ofs points to the offset within the pbuf chain 177 * @param type input ASN1 type 178 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 179 */ 180 err_t 181 snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) 182 { 183 u16_t plen, base; 184 u8_t *msg_ptr; 185 186 plen = 0; 187 while (p != NULL) 188 { 189 base = plen; 190 plen += p->len; 191 if (ofs < plen) 192 { 193 msg_ptr = (u8_t*)p->payload; 194 msg_ptr += ofs - base; 195 *msg_ptr = type; 196 return ERR_OK; 197 } 198 p = p->next; 199 } 200 /* p == NULL, ofs >= plen */ 201 return ERR_ARG; 202 } 203 204 /** 205 * Encodes host order length field into a pbuf chained ASN1 msg. 206 * 207 * @param p points to output pbuf to encode length into 208 * @param ofs points to the offset within the pbuf chain 209 * @param length is the host order length to be encoded 210 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 211 */ 212 err_t 213 snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) 214 { 215 u16_t plen, base; 216 u8_t *msg_ptr; 217 218 plen = 0; 219 while (p != NULL) 220 { 221 base = plen; 222 plen += p->len; 223 if (ofs < plen) 224 { 225 msg_ptr = (u8_t*)p->payload; 226 msg_ptr += ofs - base; 227 228 if (length < 0x80) 229 { 230 *msg_ptr = (u8_t)length; 231 return ERR_OK; 232 } 233 else if (length < 0x100) 234 { 235 *msg_ptr = 0x81; 236 ofs += 1; 237 if (ofs >= plen) 238 { 239 /* next octet in next pbuf */ 240 p = p->next; 241 if (p == NULL) { return ERR_ARG; } 242 msg_ptr = (u8_t*)p->payload; 243 } 244 else 245 { 246 /* next octet in same pbuf */ 247 msg_ptr++; 248 } 249 *msg_ptr = (u8_t)length; 250 return ERR_OK; 251 } 252 else 253 { 254 u8_t i; 255 256 /* length >= 0x100 && length <= 0xFFFF */ 257 *msg_ptr = 0x82; 258 i = 2; 259 while (i > 0) 260 { 261 i--; 262 ofs += 1; 263 if (ofs >= plen) 264 { 265 /* next octet in next pbuf */ 266 p = p->next; 267 if (p == NULL) { return ERR_ARG; } 268 msg_ptr = (u8_t*)p->payload; 269 plen += p->len; 270 } 271 else 272 { 273 /* next octet in same pbuf */ 274 msg_ptr++; 275 } 276 if (i == 0) 277 { 278 /* least significant length octet */ 279 *msg_ptr = (u8_t)length; 280 } 281 else 282 { 283 /* most significant length octet */ 284 *msg_ptr = (u8_t)(length >> 8); 285 } 286 } 287 return ERR_OK; 288 } 289 } 290 p = p->next; 291 } 292 /* p == NULL, ofs >= plen */ 293 return ERR_ARG; 294 } 295 296 /** 297 * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. 298 * 299 * @param p points to output pbuf to encode value into 300 * @param ofs points to the offset within the pbuf chain 301 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) 302 * @param value is the host order u32_t value to be encoded 303 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 304 * 305 * @see snmp_asn1_enc_u32t_cnt() 306 */ 307 err_t 308 snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value) 309 { 310 u16_t plen, base; 311 u8_t *msg_ptr; 312 313 plen = 0; 314 while (p != NULL) 315 { 316 base = plen; 317 plen += p->len; 318 if (ofs < plen) 319 { 320 msg_ptr = (u8_t*)p->payload; 321 msg_ptr += ofs - base; 322 323 if (octets_needed == 5) 324 { 325 /* not enough bits in 'value' add leading 0x00 */ 326 octets_needed--; 327 *msg_ptr = 0x00; 328 ofs += 1; 329 if (ofs >= plen) 330 { 331 /* next octet in next pbuf */ 332 p = p->next; 333 if (p == NULL) { return ERR_ARG; } 334 msg_ptr = (u8_t*)p->payload; 335 plen += p->len; 336 } 337 else 338 { 339 /* next octet in same pbuf */ 340 msg_ptr++; 341 } 342 } 343 while (octets_needed > 1) 344 { 345 octets_needed--; 346 *msg_ptr = (u8_t)(value >> (octets_needed << 3)); 347 ofs += 1; 348 if (ofs >= plen) 349 { 350 /* next octet in next pbuf */ 351 p = p->next; 352 if (p == NULL) { return ERR_ARG; } 353 msg_ptr = (u8_t*)p->payload; 354 plen += p->len; 355 } 356 else 357 { 358 /* next octet in same pbuf */ 359 msg_ptr++; 360 } 361 } 362 /* (only) one least significant octet */ 363 *msg_ptr = (u8_t)value; 364 return ERR_OK; 365 } 366 p = p->next; 367 } 368 /* p == NULL, ofs >= plen */ 369 return ERR_ARG; 370 } 371 372 /** 373 * Encodes s32_t integer into a pbuf chained ASN1 msg. 374 * 375 * @param p points to output pbuf to encode value into 376 * @param ofs points to the offset within the pbuf chain 377 * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) 378 * @param value is the host order s32_t value to be encoded 379 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 380 * 381 * @see snmp_asn1_enc_s32t_cnt() 382 */ 383 err_t 384 snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value) 385 { 386 u16_t plen, base; 387 u8_t *msg_ptr; 388 389 plen = 0; 390 while (p != NULL) 391 { 392 base = plen; 393 plen += p->len; 394 if (ofs < plen) 395 { 396 msg_ptr = (u8_t*)p->payload; 397 msg_ptr += ofs - base; 398 399 while (octets_needed > 1) 400 { 401 octets_needed--; 402 *msg_ptr = (u8_t)(value >> (octets_needed << 3)); 403 ofs += 1; 404 if (ofs >= plen) 405 { 406 /* next octet in next pbuf */ 407 p = p->next; 408 if (p == NULL) { return ERR_ARG; } 409 msg_ptr = (u8_t*)p->payload; 410 plen += p->len; 411 } 412 else 413 { 414 /* next octet in same pbuf */ 415 msg_ptr++; 416 } 417 } 418 /* (only) one least significant octet */ 419 *msg_ptr = (u8_t)value; 420 return ERR_OK; 421 } 422 p = p->next; 423 } 424 /* p == NULL, ofs >= plen */ 425 return ERR_ARG; 426 } 427 428 /** 429 * Encodes object identifier into a pbuf chained ASN1 msg. 430 * 431 * @param p points to output pbuf to encode oid into 432 * @param ofs points to the offset within the pbuf chain 433 * @param ident_len object identifier array length 434 * @param ident points to object identifier array 435 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 436 */ 437 err_t 438 snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) 439 { 440 u16_t plen, base; 441 u8_t *msg_ptr; 442 443 plen = 0; 444 while (p != NULL) 445 { 446 base = plen; 447 plen += p->len; 448 if (ofs < plen) 449 { 450 msg_ptr = (u8_t*)p->payload; 451 msg_ptr += ofs - base; 452 453 if (ident_len > 1) 454 { 455 if ((ident[0] == 1) && (ident[1] == 3)) 456 { 457 /* compressed (most common) prefix .iso.org */ 458 *msg_ptr = 0x2b; 459 } 460 else 461 { 462 /* calculate prefix */ 463 *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]); 464 } 465 ofs += 1; 466 if (ofs >= plen) 467 { 468 /* next octet in next pbuf */ 469 p = p->next; 470 if (p == NULL) { return ERR_ARG; } 471 msg_ptr = (u8_t*)p->payload; 472 plen += p->len; 473 } 474 else 475 { 476 /* next octet in same pbuf */ 477 msg_ptr++; 478 } 479 ident_len -= 2; 480 ident += 2; 481 } 482 else 483 { 484 /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ 485 /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ 486 return ERR_ARG; 487 } 488 while (ident_len > 0) 489 { 490 s32_t sub_id; 491 u8_t shift, tail; 492 493 ident_len--; 494 sub_id = *ident; 495 tail = 0; 496 shift = 28; 497 while(shift > 0) 498 { 499 u8_t code; 500 501 code = (u8_t)(sub_id >> shift); 502 if ((code != 0) || (tail != 0)) 503 { 504 tail = 1; 505 *msg_ptr = code | 0x80; 506 ofs += 1; 507 if (ofs >= plen) 508 { 509 /* next octet in next pbuf */ 510 p = p->next; 511 if (p == NULL) { return ERR_ARG; } 512 msg_ptr = (u8_t*)p->payload; 513 plen += p->len; 514 } 515 else 516 { 517 /* next octet in same pbuf */ 518 msg_ptr++; 519 } 520 } 521 shift -= 7; 522 } 523 *msg_ptr = (u8_t)sub_id & 0x7F; 524 if (ident_len > 0) 525 { 526 ofs += 1; 527 if (ofs >= plen) 528 { 529 /* next octet in next pbuf */ 530 p = p->next; 531 if (p == NULL) { return ERR_ARG; } 532 msg_ptr = (u8_t*)p->payload; 533 plen += p->len; 534 } 535 else 536 { 537 /* next octet in same pbuf */ 538 msg_ptr++; 539 } 540 } 541 /* proceed to next sub-identifier */ 542 ident++; 543 } 544 return ERR_OK; 545 } 546 p = p->next; 547 } 548 /* p == NULL, ofs >= plen */ 549 return ERR_ARG; 550 } 551 552 /** 553 * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. 554 * 555 * @param p points to output pbuf to encode raw data into 556 * @param ofs points to the offset within the pbuf chain 557 * @param raw_len raw data length 558 * @param raw points raw data 559 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode 560 */ 561 err_t 562 snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw) 563 { 564 u16_t plen, base; 565 u8_t *msg_ptr; 566 567 plen = 0; 568 while (p != NULL) 569 { 570 base = plen; 571 plen += p->len; 572 if (ofs < plen) 573 { 574 msg_ptr = (u8_t*)p->payload; 575 msg_ptr += ofs - base; 576 577 while (raw_len > 1) 578 { 579 /* copy raw_len - 1 octets */ 580 raw_len--; 581 *msg_ptr = *raw; 582 raw++; 583 ofs += 1; 584 if (ofs >= plen) 585 { 586 /* next octet in next pbuf */ 587 p = p->next; 588 if (p == NULL) { return ERR_ARG; } 589 msg_ptr = (u8_t*)p->payload; 590 plen += p->len; 591 } 592 else 593 { 594 /* next octet in same pbuf */ 595 msg_ptr++; 596 } 597 } 598 if (raw_len > 0) 599 { 600 /* copy last or single octet */ 601 *msg_ptr = *raw; 602 } 603 return ERR_OK; 604 } 605 p = p->next; 606 } 607 /* p == NULL, ofs >= plen */ 608 return ERR_ARG; 609 } 610 611 #endif /* LWIP_SNMP */ 612