1 /* $OpenBSD: ax.c,v 1.3 2023/10/24 08:54:52 martijn Exp $ */ 2 /* 3 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/socket.h> 18 19 #include <arpa/inet.h> 20 21 #include <ctype.h> 22 #include <endian.h> 23 #include <errno.h> 24 #include <inttypes.h> 25 #include <stdlib.h> 26 #include <stdint.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <strings.h> 30 #include <unistd.h> 31 32 #include "ax.h" 33 34 #define AX_PDU_HEADER 20 35 36 static int ax_pdu_need(struct ax *, size_t); 37 static int ax_pdu_header(struct ax *, 38 enum ax_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t, 39 struct ax_ostring *); 40 static uint32_t ax_packetid(struct ax *); 41 static uint32_t ax_pdu_queue(struct ax *); 42 static int ax_pdu_add_uint16(struct ax *, uint16_t); 43 static int ax_pdu_add_uint32(struct ax *, uint32_t); 44 static int ax_pdu_add_uint64(struct ax *, uint64_t); 45 static int ax_pdu_add_oid(struct ax *, struct ax_oid *); 46 static int ax_pdu_add_str(struct ax *, struct ax_ostring *); 47 static int ax_pdu_add_varbindlist(struct ax *, struct ax_varbind *, 48 size_t); 49 static int ax_pdu_add_searchrange(struct ax *, struct ax_searchrange *); 50 static uint16_t ax_pdutoh16(struct ax_pdu_header *, uint8_t *); 51 static uint32_t ax_pdutoh32(struct ax_pdu_header *, uint8_t *); 52 static uint64_t ax_pdutoh64(struct ax_pdu_header *, uint8_t *); 53 static ssize_t ax_pdutooid(struct ax_pdu_header *, struct ax_oid *, 54 uint8_t *, size_t); 55 static ssize_t ax_pdutoostring(struct ax_pdu_header *, 56 struct ax_ostring *, uint8_t *, size_t); 57 static ssize_t ax_pdutovarbind(struct ax_pdu_header *, 58 struct ax_varbind *, uint8_t *, size_t); 59 60 struct ax * 61 ax_new(int fd) 62 { 63 struct ax *ax; 64 65 if (fd == -1) { 66 errno = EINVAL; 67 return NULL; 68 } 69 70 if ((ax = calloc(1, sizeof(*ax))) == NULL) 71 return NULL; 72 ax->ax_fd = fd; 73 ax->ax_rbsize = 512; 74 if ((ax->ax_rbuf = malloc(ax->ax_rbsize)) == NULL) 75 goto fail; 76 ax->ax_byteorder = AX_BYTE_ORDER_NATIVE; 77 78 return ax; 79 80 fail: 81 free(ax); 82 return NULL; 83 } 84 85 void 86 ax_free(struct ax *ax) 87 { 88 if (ax == NULL) 89 return; 90 close(ax->ax_fd); 91 free(ax->ax_rbuf); 92 free(ax->ax_wbuf); 93 free(ax->ax_packetids); 94 free(ax); 95 } 96 97 struct ax_pdu * 98 ax_recv(struct ax *ax) 99 { 100 struct ax_pdu *pdu; 101 struct ax_pdu_header header; 102 struct ax_pdu_response *response; 103 struct ax_varbind *varbind; 104 struct ax_pdu_searchrangelist *srl = NULL; 105 struct ax_pdu_varbindlist *vbl; 106 struct ax_searchrange *sr; 107 size_t rbsize, packetidx = 0, i, rawlen; 108 ssize_t nread; 109 uint8_t *u8; 110 uint8_t *rbuf; 111 int found; 112 113 /* Only read a single packet at a time to make sure libevent triggers */ 114 if (ax->ax_rblen < AX_PDU_HEADER) { 115 if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, 116 AX_PDU_HEADER - ax->ax_rblen)) == 0) { 117 errno = ECONNRESET; 118 return NULL; 119 } 120 if (nread == -1) 121 return NULL; 122 ax->ax_rblen += nread; 123 if (ax->ax_rblen < AX_PDU_HEADER) { 124 errno = EAGAIN; 125 return NULL; 126 } 127 } 128 u8 = ax->ax_rbuf; 129 header.aph_version = *u8++; 130 header.aph_type = *u8++; 131 header.aph_flags = *u8++; 132 u8++; 133 header.aph_sessionid = ax_pdutoh32(&header, u8); 134 u8 += 4; 135 header.aph_transactionid = ax_pdutoh32(&header, u8); 136 u8 += 4; 137 header.aph_packetid = ax_pdutoh32(&header, u8); 138 u8 += 4; 139 header.aph_plength = ax_pdutoh32(&header, u8); 140 141 if (header.aph_version != 1) { 142 errno = EPROTO; 143 return NULL; 144 } 145 if (ax->ax_rblen < AX_PDU_HEADER + header.aph_plength) { 146 if (AX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) { 147 rbsize = (((AX_PDU_HEADER + header.aph_plength) 148 / 512) + 1) * 512; 149 if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize, 150 rbsize, sizeof(*rbuf))) == NULL) 151 return NULL; 152 ax->ax_rbsize = rbsize; 153 ax->ax_rbuf = rbuf; 154 } 155 nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, 156 header.aph_plength - (ax->ax_rblen - AX_PDU_HEADER)); 157 if (nread == 0) 158 errno = ECONNRESET; 159 if (nread <= 0) 160 return NULL; 161 ax->ax_rblen += nread; 162 if (ax->ax_rblen < AX_PDU_HEADER + header.aph_plength) { 163 errno = EAGAIN; 164 return NULL; 165 } 166 } 167 168 if ((pdu = calloc(1, sizeof(*pdu))) == NULL) 169 return NULL; 170 171 memcpy(&(pdu->ap_header), &header, sizeof(header)); 172 173 #if defined(AX_DEBUG) && defined(AX_DEBUG_VERBOSE) 174 { 175 char chars[4]; 176 int print = 1; 177 178 fprintf(stderr, "received packet:\n"); 179 for (i = 0; i < pdu->ap_header.aph_plength + AX_PDU_HEADER; 180 i++) { 181 fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]); 182 chars[i % 4] = ax->ax_rbuf[i]; 183 if (!isprint(ax->ax_rbuf[i])) 184 print = 0; 185 if (i % 4 == 3) { 186 if (print) 187 fprintf(stderr, "%.4s", chars); 188 fprintf(stderr, "\n"); 189 print = 1; 190 } 191 } 192 } 193 #endif 194 195 u8 = (ax->ax_rbuf) + AX_PDU_HEADER; 196 rawlen = pdu->ap_header.aph_plength; 197 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) { 198 nread = ax_pdutoostring(&header, &(pdu->ap_context), u8, 199 rawlen); 200 if (nread == -1) 201 goto fail; 202 rawlen -= nread; 203 u8 += nread; 204 } 205 206 switch (pdu->ap_header.aph_type) { 207 case AX_PDU_TYPE_OPEN: 208 if (rawlen < 12) { 209 errno = EPROTO; 210 goto fail; 211 } 212 pdu->ap_payload.ap_open.ap_timeout = *u8; 213 rawlen -= 4; 214 u8 += 4; 215 if ((nread = ax_pdutooid(&header, 216 &(pdu->ap_payload.ap_open.ap_oid), u8, rawlen)) == -1) 217 goto fail; 218 rawlen -= nread; 219 u8 += nread; 220 if ((nread = ax_pdutoostring(&header, 221 &(pdu->ap_payload.ap_open.ap_descr), u8, rawlen)) == -1) 222 goto fail; 223 if (rawlen - nread != 0) { 224 errno = EPROTO; 225 goto fail; 226 } 227 break; 228 case AX_PDU_TYPE_CLOSE: 229 if (rawlen != 4) { 230 errno = EPROTO; 231 goto fail; 232 } 233 if (u8[0] != AX_CLOSE_OTHER && 234 u8[0] != AX_CLOSEN_PARSEERROR && 235 u8[0] != AX_CLOSE_PROTOCOLERROR && 236 u8[0] != AX_CLOSE_TIMEOUTS && 237 u8[0] != AX_CLOSE_SHUTDOWN && 238 u8[0] != AX_CLOSE_BYMANAGER) { 239 errno = EPROTO; 240 goto fail; 241 } 242 pdu->ap_payload.ap_close.ap_reason = u8[0]; 243 break; 244 case AX_PDU_TYPE_REGISTER: 245 if (rawlen < 8) { 246 errno = EPROTO; 247 goto fail; 248 } 249 pdu->ap_payload.ap_register.ap_timeout = *u8++; 250 pdu->ap_payload.ap_register.ap_priority = *u8++; 251 pdu->ap_payload.ap_register.ap_range_subid = *u8++; 252 u8++; 253 rawlen -= 4; 254 if ((nread = ax_pdutooid(&header, 255 &(pdu->ap_payload.ap_register.ap_subtree), 256 u8, rawlen)) == -1) 257 goto fail; 258 rawlen -= nread; 259 u8 += nread; 260 if (pdu->ap_payload.ap_register.ap_range_subid) { 261 if (rawlen != 4) { 262 errno = EPROTO; 263 goto fail; 264 } 265 pdu->ap_payload.ap_register.ap_upper_bound = 266 ax_pdutoh32(&header, u8); 267 rawlen -= 4; 268 } 269 if (rawlen != 0) { 270 errno = EPROTO; 271 goto fail; 272 } 273 break; 274 case AX_PDU_TYPE_UNREGISTER: 275 if (rawlen < 8) { 276 errno = EPROTO; 277 goto fail; 278 } 279 u8++; 280 pdu->ap_payload.ap_unregister.ap_priority = *u8++; 281 pdu->ap_payload.ap_unregister.ap_range_subid = *u8++; 282 u8++; 283 rawlen -= 4; 284 if ((nread = ax_pdutooid(&header, 285 &(pdu->ap_payload.ap_unregister.ap_subtree), 286 u8, rawlen)) == -1) 287 goto fail; 288 rawlen -= nread; 289 u8 += nread; 290 if (pdu->ap_payload.ap_unregister.ap_range_subid) { 291 if (rawlen != 4) { 292 errno = EPROTO; 293 goto fail; 294 } 295 pdu->ap_payload.ap_unregister.ap_upper_bound = 296 ax_pdutoh32(&header, u8); 297 rawlen -= 4; 298 } 299 if (rawlen != 0) { 300 errno = EPROTO; 301 goto fail; 302 } 303 break; 304 case AX_PDU_TYPE_GETBULK: 305 if (rawlen < 4) { 306 errno = EPROTO; 307 goto fail; 308 } 309 pdu->ap_payload.ap_getbulk.ap_nonrep = 310 ax_pdutoh16(&header, u8); 311 u8 += 2; 312 pdu->ap_payload.ap_getbulk.ap_maxrep = 313 ax_pdutoh16(&header, u8); 314 u8 += 2; 315 srl = &(pdu->ap_payload.ap_getbulk.ap_srl); 316 rawlen -= 4; 317 /* FALLTHROUGH */ 318 case AX_PDU_TYPE_GET: 319 case AX_PDU_TYPE_GETNEXT: 320 if (pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK) 321 srl = &(pdu->ap_payload.ap_srl); 322 while (rawlen > 0 ) { 323 srl->ap_nsr++; 324 sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr)); 325 if (sr == NULL) 326 goto fail; 327 srl->ap_sr = sr; 328 sr += (srl->ap_nsr - 1); 329 if ((nread = ax_pdutooid(&header, &(sr->asr_start), 330 u8, rawlen)) == -1) 331 goto fail; 332 rawlen -= nread; 333 u8 += nread; 334 if ((nread = ax_pdutooid(&header, &(sr->asr_stop), 335 u8, rawlen)) == -1) 336 goto fail; 337 rawlen -= nread; 338 u8 += nread; 339 } 340 break; 341 case AX_PDU_TYPE_TESTSET: 342 case AX_PDU_TYPE_INDEXALLOCATE: 343 case AX_PDU_TYPE_INDEXDEALLOCATE: 344 case AX_PDU_TYPE_NOTIFY: 345 vbl = &(pdu->ap_payload.ap_vbl); 346 while (rawlen > 0) { 347 varbind = recallocarray(vbl->ap_varbind, 348 vbl->ap_nvarbind, vbl->ap_nvarbind + 1, 349 sizeof(*(vbl->ap_varbind))); 350 if (varbind == NULL) 351 goto fail; 352 vbl->ap_varbind = varbind; 353 nread = ax_pdutovarbind(&header, 354 &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen); 355 if (nread == -1) 356 goto fail; 357 vbl->ap_nvarbind++; 358 u8 += nread; 359 rawlen -= nread; 360 } 361 break; 362 case AX_PDU_TYPE_COMMITSET: 363 case AX_PDU_TYPE_UNDOSET: 364 case AX_PDU_TYPE_CLEANUPSET: 365 case AX_PDU_TYPE_PING: 366 if (rawlen != 0) { 367 errno = EPROTO; 368 goto fail; 369 } 370 break; 371 case AX_PDU_TYPE_ADDAGENTCAPS: 372 nread = ax_pdutooid(&header, 373 &(pdu->ap_payload.ap_addagentcaps.ap_oid), u8, rawlen); 374 if (nread == -1) 375 goto fail; 376 rawlen -= nread; 377 u8 += nread; 378 nread = ax_pdutoostring(&header, 379 &(pdu->ap_payload.ap_addagentcaps.ap_descr), u8, rawlen); 380 if (nread == -1) 381 goto fail; 382 if (rawlen - nread != 0) { 383 errno = EPROTO; 384 goto fail; 385 } 386 break; 387 case AX_PDU_TYPE_REMOVEAGENTCAPS: 388 nread = ax_pdutooid(&header, 389 &(pdu->ap_payload.ap_removeagentcaps.ap_oid), u8, rawlen); 390 if (nread == -1) 391 goto fail; 392 if (rawlen - nread != 0) { 393 errno = EPROTO; 394 goto fail; 395 } 396 break; 397 case AX_PDU_TYPE_RESPONSE: 398 if (ax->ax_packetids != NULL) { 399 found = 0; 400 for (i = 0; ax->ax_packetids[i] != 0; i++) { 401 if (ax->ax_packetids[i] == 402 pdu->ap_header.aph_packetid) { 403 packetidx = i; 404 found = 1; 405 } 406 } 407 if (found) { 408 ax->ax_packetids[packetidx] = 409 ax->ax_packetids[i - 1]; 410 ax->ax_packetids[i - 1] = 0; 411 } else { 412 errno = EPROTO; 413 goto fail; 414 } 415 } 416 if (rawlen < 8) { 417 errno = EPROTO; 418 goto fail; 419 } 420 response = &(pdu->ap_payload.ap_response); 421 response->ap_uptime = ax_pdutoh32(&header, u8); 422 u8 += 4; 423 response->ap_error = ax_pdutoh16(&header, u8); 424 u8 += 2; 425 response->ap_index = ax_pdutoh16(&header, u8); 426 u8 += 2; 427 rawlen -= 8; 428 while (rawlen > 0) { 429 varbind = recallocarray(response->ap_varbindlist, 430 response->ap_nvarbind, response->ap_nvarbind + 1, 431 sizeof(*(response->ap_varbindlist))); 432 if (varbind == NULL) 433 goto fail; 434 response->ap_varbindlist = varbind; 435 nread = ax_pdutovarbind(&header, 436 &(response->ap_varbindlist[response->ap_nvarbind]), 437 u8, rawlen); 438 if (nread == -1) 439 goto fail; 440 response->ap_nvarbind++; 441 u8 += nread; 442 rawlen -= nread; 443 } 444 break; 445 default: 446 errno = EPROTO; 447 goto fail; 448 } 449 450 ax->ax_rblen = 0; 451 452 return pdu; 453 fail: 454 ax_pdu_free(pdu); 455 return NULL; 456 } 457 458 static int 459 ax_pdu_need(struct ax *ax, size_t need) 460 { 461 uint8_t *wbuf; 462 size_t wbsize; 463 464 if (ax->ax_wbtlen + need >= ax->ax_wbsize) { 465 wbsize = (((ax->ax_wbtlen + need) / 512) + 1) * 512; 466 wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1); 467 if (wbuf == NULL) { 468 ax->ax_wbtlen = ax->ax_wblen; 469 return -1; 470 } 471 ax->ax_wbsize = wbsize; 472 ax->ax_wbuf = wbuf; 473 } 474 475 return 0; 476 } 477 478 ssize_t 479 ax_send(struct ax *ax) 480 { 481 ssize_t nwrite; 482 483 if (ax->ax_wblen != ax->ax_wbtlen) { 484 errno = EALREADY; 485 return -1; 486 } 487 488 if (ax->ax_wblen == 0) 489 return 0; 490 491 #if defined(AX_DEBUG) && defined(AX_DEBUG_VERBOSE) 492 { 493 size_t i; 494 char chars[4]; 495 int print = 1; 496 497 fprintf(stderr, "sending packet:\n"); 498 for (i = 0; i < ax->ax_wblen; i++) { 499 fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]); 500 chars[i % 4] = ax->ax_wbuf[i]; 501 if (!isprint(ax->ax_wbuf[i])) 502 print = 0; 503 if (i % 4 == 3) { 504 if (print) 505 fprintf(stderr, "%.4s", chars); 506 fprintf(stderr, "\n"); 507 print = 1; 508 } 509 } 510 } 511 #endif 512 513 if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen, 514 MSG_NOSIGNAL | MSG_DONTWAIT)) == -1) 515 return -1; 516 517 memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite); 518 ax->ax_wblen -= nwrite; 519 ax->ax_wbtlen = ax->ax_wblen; 520 521 return ax->ax_wblen; 522 } 523 524 uint32_t 525 ax_open(struct ax *ax, uint8_t timeout, struct ax_oid *oid, 526 struct ax_ostring *descr) 527 { 528 if (ax_pdu_header(ax, AX_PDU_TYPE_OPEN, 0, 0, 0, 0, 529 NULL) == -1) 530 return 0; 531 ax_pdu_need(ax, 4); 532 ax->ax_wbuf[ax->ax_wbtlen++] = timeout; 533 memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); 534 ax->ax_wbtlen += 3; 535 if (ax_pdu_add_oid(ax, oid) == -1) 536 return 0; 537 if (ax_pdu_add_str(ax, descr) == -1) 538 return 0; 539 540 return ax_pdu_queue(ax); 541 } 542 543 uint32_t 544 ax_close(struct ax *ax, uint32_t sessionid, 545 enum ax_close_reason reason) 546 { 547 if (ax_pdu_header(ax, AX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0, 548 NULL) == -1) 549 return 0; 550 551 if (ax_pdu_need(ax, 4) == -1) 552 return 0; 553 ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason; 554 memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); 555 ax->ax_wbtlen += 3; 556 557 return ax_pdu_queue(ax); 558 } 559 560 int 561 ax_get(struct ax *ax, uint32_t sessionid, uint32_t transactionid, 562 uint32_t packetid, struct ax_ostring *context, struct ax_searchrange *srl, 563 size_t nsr) 564 { 565 size_t i; 566 567 if (ax_pdu_header(ax, AX_PDU_TYPE_GET, 0, sessionid, transactionid, 568 packetid, context) == -1) 569 return -1; 570 571 for (i = 0; i < nsr; i++) { 572 if (ax_pdu_add_searchrange(ax, &(srl[i])) == -1) 573 return 0; 574 } 575 576 return ax_pdu_queue(ax); 577 } 578 579 int 580 ax_getnext(struct ax *ax, uint32_t sessionid, uint32_t transactionid, 581 uint32_t packetid, struct ax_ostring *context, struct ax_searchrange *srl, 582 size_t nsr) 583 { 584 size_t i; 585 586 if (ax_pdu_header(ax, AX_PDU_TYPE_GETNEXT, 0, sessionid, transactionid, 587 packetid, context) == -1) 588 return -1; 589 590 for (i = 0; i < nsr; i++) { 591 if (ax_pdu_add_searchrange(ax, &(srl[i])) == -1) 592 return 0; 593 } 594 595 return ax_pdu_queue(ax); 596 } 597 598 uint32_t 599 ax_indexallocate(struct ax *ax, uint8_t flags, uint32_t sessionid, 600 struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) 601 { 602 if (flags & ~(AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)) { 603 errno = EINVAL; 604 return 0; 605 } 606 607 if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXALLOCATE, flags, 608 sessionid, 0, 0, context) == -1) 609 return 0; 610 611 if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) 612 return 0; 613 614 return ax_pdu_queue(ax); 615 } 616 617 uint32_t 618 ax_indexdeallocate(struct ax *ax, uint32_t sessionid, 619 struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) 620 { 621 if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXDEALLOCATE, 0, 622 sessionid, 0, 0, context) == -1) 623 return 0; 624 625 if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) 626 return 0; 627 628 return ax_pdu_queue(ax); 629 } 630 631 uint32_t 632 ax_addagentcaps(struct ax *ax, uint32_t sessionid, 633 struct ax_ostring *context, struct ax_oid *id, 634 struct ax_ostring *descr) 635 { 636 if (ax_pdu_header(ax, AX_PDU_TYPE_ADDAGENTCAPS, 0, 637 sessionid, 0, 0, context) == -1) 638 return 0; 639 if (ax_pdu_add_oid(ax, id) == -1) 640 return 0; 641 if (ax_pdu_add_str(ax, descr) == -1) 642 return 0; 643 644 return ax_pdu_queue(ax); 645 } 646 647 uint32_t 648 ax_removeagentcaps(struct ax *ax, uint32_t sessionid, 649 struct ax_ostring *context, struct ax_oid *id) 650 { 651 if (ax_pdu_header(ax, AX_PDU_TYPE_REMOVEAGENTCAPS, 0, 652 sessionid, 0, 0, context) == -1) 653 return 0; 654 if (ax_pdu_add_oid(ax, id) == -1) 655 return 0; 656 657 return ax_pdu_queue(ax); 658 659 } 660 661 uint32_t 662 ax_register(struct ax *ax, uint8_t flags, uint32_t sessionid, 663 struct ax_ostring *context, uint8_t timeout, uint8_t priority, 664 uint8_t range_subid, struct ax_oid *subtree, uint32_t upperbound) 665 { 666 if (flags & ~(AX_PDU_FLAG_INSTANCE_REGISTRATION)) { 667 errno = EINVAL; 668 return 0; 669 } 670 671 if (ax_pdu_header(ax, AX_PDU_TYPE_REGISTER, flags, 672 sessionid, 0, 0, context) == -1) 673 return 0; 674 675 if (ax_pdu_need(ax, 4) == -1) 676 return 0; 677 ax->ax_wbuf[ax->ax_wbtlen++] = timeout; 678 ax->ax_wbuf[ax->ax_wbtlen++] = priority; 679 ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; 680 ax->ax_wbuf[ax->ax_wbtlen++] = 0; 681 if (ax_pdu_add_oid(ax, subtree) == -1) 682 return 0; 683 if (range_subid != 0) { 684 if (ax_pdu_add_uint32(ax, upperbound) == -1) 685 return 0; 686 } 687 688 return ax_pdu_queue(ax); 689 } 690 691 uint32_t 692 ax_unregister(struct ax *ax, uint32_t sessionid, 693 struct ax_ostring *context, uint8_t priority, uint8_t range_subid, 694 struct ax_oid *subtree, uint32_t upperbound) 695 { 696 if (ax_pdu_header(ax, AX_PDU_TYPE_UNREGISTER, 0, 697 sessionid, 0, 0, context) == -1) 698 return 0; 699 700 if (ax_pdu_need(ax, 4) == -1) 701 return 0; 702 ax->ax_wbuf[ax->ax_wbtlen++] = 0; 703 ax->ax_wbuf[ax->ax_wbtlen++] = priority; 704 ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; 705 ax->ax_wbuf[ax->ax_wbtlen++] = 0; 706 if (ax_pdu_add_oid(ax, subtree) == -1) 707 return 0; 708 if (range_subid != 0) { 709 if (ax_pdu_add_uint32(ax, upperbound) == -1) 710 return 0; 711 } 712 713 return ax_pdu_queue(ax); 714 } 715 716 int 717 ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid, 718 uint32_t packetid, uint32_t sysuptime, uint16_t error, uint16_t index, 719 struct ax_varbind *vblist, size_t nvb) 720 { 721 if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid, 722 transactionid, packetid, NULL) == -1) 723 return -1; 724 725 if (ax_pdu_add_uint32(ax, sysuptime) == -1 || 726 ax_pdu_add_uint16(ax, error) == -1 || 727 ax_pdu_add_uint16(ax, index) == -1) 728 return -1; 729 730 if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) 731 return -1; 732 if (ax_pdu_queue(ax) == 0) 733 return -1; 734 return 0; 735 } 736 737 void 738 ax_pdu_free(struct ax_pdu *pdu) 739 { 740 size_t i; 741 struct ax_pdu_response *response; 742 struct ax_pdu_varbindlist *vblist; 743 744 if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) 745 free(pdu->ap_context.aos_string); 746 747 switch (pdu->ap_header.aph_type) { 748 case AX_PDU_TYPE_OPEN: 749 free(pdu->ap_payload.ap_open.ap_descr.aos_string); 750 break; 751 case AX_PDU_TYPE_GET: 752 case AX_PDU_TYPE_GETNEXT: 753 case AX_PDU_TYPE_GETBULK: 754 free(pdu->ap_payload.ap_srl.ap_sr); 755 break; 756 case AX_PDU_TYPE_TESTSET: 757 case AX_PDU_TYPE_INDEXALLOCATE: 758 case AX_PDU_TYPE_INDEXDEALLOCATE: 759 vblist = &(pdu->ap_payload.ap_vbl); 760 for (i = 0; i < vblist->ap_nvarbind; i++) 761 ax_varbind_free(&(vblist->ap_varbind[i])); 762 free(vblist->ap_varbind); 763 break; 764 case AX_PDU_TYPE_RESPONSE: 765 response = &(pdu->ap_payload.ap_response); 766 for (i = 0; i < response->ap_nvarbind; i++) 767 ax_varbind_free(&(response->ap_varbindlist[i])); 768 free(response->ap_varbindlist); 769 break; 770 default: 771 break; 772 } 773 free(pdu); 774 } 775 776 void 777 ax_varbind_free(struct ax_varbind *varbind) 778 { 779 switch (varbind->avb_type) { 780 case AX_DATA_TYPE_OCTETSTRING: 781 case AX_DATA_TYPE_IPADDRESS: 782 case AX_DATA_TYPE_OPAQUE: 783 free(varbind->avb_data.avb_ostring.aos_string); 784 break; 785 default: 786 break; 787 } 788 } 789 790 const char * 791 ax_error2string(enum ax_pdu_error error) 792 { 793 static char buffer[64]; 794 switch (error) { 795 case AX_PDU_ERROR_NOERROR: 796 return "No error"; 797 case AX_PDU_ERROR_GENERR: 798 return "Generic error"; 799 case AX_PDU_ERROR_NOACCESS: 800 return "No access"; 801 case AX_PDU_ERROR_WRONGTYPE: 802 return "Wrong type"; 803 case AX_PDU_ERROR_WRONGLENGTH: 804 return "Wrong length"; 805 case AX_PDU_ERROR_WRONGENCODING: 806 return "Wrong encoding"; 807 case AX_PDU_ERROR_WRONGVALUE: 808 return "Wrong value"; 809 case AX_PDU_ERROR_NOCREATION: 810 return "No creation"; 811 case AX_PDU_ERROR_INCONSISTENTVALUE: 812 return "Inconsistent value"; 813 case AX_PDU_ERROR_RESOURCEUNAVAILABLE: 814 return "Resource unavailable"; 815 case AX_PDU_ERROR_COMMITFAILED: 816 return "Commit failed"; 817 case AX_PDU_ERROR_UNDOFAILED: 818 return "Undo failed"; 819 case AX_PDU_ERROR_NOTWRITABLE: 820 return "Not writable"; 821 case AX_PDU_ERROR_INCONSISTENTNAME: 822 return "Inconsistent name"; 823 case AX_PDU_ERROR_OPENFAILED: 824 return "Open Failed"; 825 case AX_PDU_ERROR_NOTOPEN: 826 return "Not open"; 827 case AX_PDU_ERROR_INDEXWRONGTYPE: 828 return "Index wrong type"; 829 case AX_PDU_ERROR_INDEXALREADYALLOCATED: 830 return "Index already allocated"; 831 case AX_PDU_ERROR_INDEXNONEAVAILABLE: 832 return "Index none available"; 833 case AX_PDU_ERROR_INDEXNOTALLOCATED: 834 return "Index not allocated"; 835 case AX_PDU_ERROR_UNSUPPORTEDCONETXT: 836 return "Unsupported context"; 837 case AX_PDU_ERROR_DUPLICATEREGISTRATION: 838 return "Duplicate registration"; 839 case AX_PDU_ERROR_UNKNOWNREGISTRATION: 840 return "Unkown registration"; 841 case AX_PDU_ERROR_UNKNOWNAGENTCAPS: 842 return "Unknown agent capabilities"; 843 case AX_PDU_ERROR_PARSEERROR: 844 return "Parse error"; 845 case AX_PDU_ERROR_REQUESTDENIED: 846 return "Request denied"; 847 case AX_PDU_ERROR_PROCESSINGERROR: 848 return "Processing error"; 849 } 850 snprintf(buffer, sizeof(buffer), "Unknown error: %d", error); 851 return buffer; 852 } 853 854 const char * 855 ax_pdutype2string(enum ax_pdu_type type) 856 { 857 static char buffer[64]; 858 switch(type) { 859 case AX_PDU_TYPE_OPEN: 860 return "agentx-Open-PDU"; 861 case AX_PDU_TYPE_CLOSE: 862 return "agentx-Close-PDU"; 863 case AX_PDU_TYPE_REGISTER: 864 return "agentx-Register-PDU"; 865 case AX_PDU_TYPE_UNREGISTER: 866 return "agentx-Unregister-PDU"; 867 case AX_PDU_TYPE_GET: 868 return "agentx-Get-PDU"; 869 case AX_PDU_TYPE_GETNEXT: 870 return "agentx-GetNext-PDU"; 871 case AX_PDU_TYPE_GETBULK: 872 return "agentx-GetBulk-PDU"; 873 case AX_PDU_TYPE_TESTSET: 874 return "agentx-TestSet-PDU"; 875 case AX_PDU_TYPE_COMMITSET: 876 return "agentx-CommitSet-PDU"; 877 case AX_PDU_TYPE_UNDOSET: 878 return "agentx-UndoSet-PDU"; 879 case AX_PDU_TYPE_CLEANUPSET: 880 return "agentx-CleanupSet-PDU"; 881 case AX_PDU_TYPE_NOTIFY: 882 return "agentx-Notify-PDU"; 883 case AX_PDU_TYPE_PING: 884 return "agentx-Ping-PDU"; 885 case AX_PDU_TYPE_INDEXALLOCATE: 886 return "agentx-IndexAllocate-PDU"; 887 case AX_PDU_TYPE_INDEXDEALLOCATE: 888 return "agentx-IndexDeallocate-PDU"; 889 case AX_PDU_TYPE_ADDAGENTCAPS: 890 return "agentx-AddAgentCaps-PDU"; 891 case AX_PDU_TYPE_REMOVEAGENTCAPS: 892 return "agentx-RemoveAgentCaps-PDU"; 893 case AX_PDU_TYPE_RESPONSE: 894 return "agentx-Response-PDU"; 895 } 896 snprintf(buffer, sizeof(buffer), "Unknown type: %d", type); 897 return buffer; 898 } 899 900 const char * 901 ax_closereason2string(enum ax_close_reason reason) 902 { 903 static char buffer[64]; 904 905 switch (reason) { 906 case AX_CLOSE_OTHER: 907 return "Undefined reason"; 908 case AX_CLOSEN_PARSEERROR: 909 return "Too many AgentX parse errors from peer"; 910 case AX_CLOSE_PROTOCOLERROR: 911 return "Too many AgentX protocol errors from peer"; 912 case AX_CLOSE_TIMEOUTS: 913 return "Too many timeouts waiting for peer"; 914 case AX_CLOSE_SHUTDOWN: 915 return "shutting down"; 916 case AX_CLOSE_BYMANAGER: 917 return "Manager shuts down"; 918 } 919 snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason); 920 return buffer; 921 } 922 923 const char * 924 ax_oid2string(struct ax_oid *oid) 925 { 926 return ax_oidrange2string(oid, 0, 0); 927 } 928 929 const char * 930 ax_oidrange2string(struct ax_oid *oid, uint8_t range_subid, 931 uint32_t upperbound) 932 { 933 static char buf[1024]; 934 char *p; 935 size_t i, rest; 936 int ret; 937 938 rest = sizeof(buf); 939 p = buf; 940 for (i = 0; i < oid->aoi_idlen; i++) { 941 if (range_subid != 0 && range_subid - 1 == (uint8_t)i) 942 ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i], 943 upperbound); 944 else 945 ret = snprintf(p, rest, ".%u", oid->aoi_id[i]); 946 if ((size_t) ret >= rest) { 947 snprintf(buf, sizeof(buf), "Couldn't parse oid"); 948 return buf; 949 } 950 p += ret; 951 rest -= (size_t) ret; 952 } 953 return buf; 954 } 955 956 const char * 957 ax_varbind2string(struct ax_varbind *vb) 958 { 959 static char buf[1024]; 960 char tmpbuf[1024]; 961 size_t i, bufleft; 962 int ishex = 0; 963 char *p; 964 int ret; 965 966 switch (vb->avb_type) { 967 case AX_DATA_TYPE_INTEGER: 968 snprintf(buf, sizeof(buf), "%s: (int)%d", 969 ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_int32); 970 break; 971 case AX_DATA_TYPE_OCTETSTRING: 972 for (i = 0; 973 i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) { 974 if (!isprint(vb->avb_data.avb_ostring.aos_string[i])) 975 ishex = 1; 976 } 977 if (ishex) { 978 p = tmpbuf; 979 bufleft = sizeof(tmpbuf); 980 for (i = 0; 981 i < vb->avb_data.avb_ostring.aos_slen; i++) { 982 ret = snprintf(p, bufleft, " %02hhX", 983 vb->avb_data.avb_ostring.aos_string[i]); 984 if (ret >= (int) bufleft) { 985 p = strrchr(p, ' '); 986 strlcpy(p, "...", 4); 987 break; 988 } 989 p += 3; 990 bufleft -= 3; 991 } 992 ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s", 993 ax_oid2string(&(vb->avb_oid)), tmpbuf); 994 if (ret >= (int) sizeof(buf)) { 995 p = strrchr(buf, ' '); 996 strlcpy(p, "...", 4); 997 } 998 } else { 999 ret = snprintf(buf, sizeof(buf), "%s: (string)", 1000 ax_oid2string(&(vb->avb_oid))); 1001 if (ret >= (int) sizeof(buf)) { 1002 snprintf(buf, sizeof(buf), "<too large OID>: " 1003 "(string)<too large string>"); 1004 break; 1005 } 1006 p = buf + ret; 1007 bufleft = (int) sizeof(buf) - ret; 1008 if (snprintf(p, bufleft, "%.*s", 1009 vb->avb_data.avb_ostring.aos_slen, 1010 vb->avb_data.avb_ostring.aos_string) >= 1011 (int) bufleft) { 1012 p = buf + sizeof(buf) - 4; 1013 strlcpy(p, "...", 4); 1014 } 1015 } 1016 break; 1017 case AX_DATA_TYPE_NULL: 1018 snprintf(buf, sizeof(buf), "%s: <null>", 1019 ax_oid2string(&(vb->avb_oid))); 1020 break; 1021 case AX_DATA_TYPE_OID: 1022 strlcpy(tmpbuf, 1023 ax_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf)); 1024 snprintf(buf, sizeof(buf), "%s: (oid)%s", 1025 ax_oid2string(&(vb->avb_oid)), tmpbuf); 1026 break; 1027 case AX_DATA_TYPE_IPADDRESS: 1028 if (vb->avb_data.avb_ostring.aos_slen != 4) { 1029 snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>", 1030 ax_oid2string(&(vb->avb_oid))); 1031 break; 1032 } 1033 if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string, 1034 tmpbuf, sizeof(tmpbuf)) == NULL) { 1035 snprintf(buf, sizeof(buf), "%s: (ipaddress)" 1036 "<unparseable>: %s", 1037 ax_oid2string(&(vb->avb_oid)), 1038 strerror(errno)); 1039 break; 1040 } 1041 snprintf(buf, sizeof(buf), "%s: (ipaddress)%s", 1042 ax_oid2string(&(vb->avb_oid)), tmpbuf); 1043 break; 1044 case AX_DATA_TYPE_COUNTER32: 1045 snprintf(buf, sizeof(buf), "%s: (counter32)%u", 1046 ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); 1047 break; 1048 case AX_DATA_TYPE_GAUGE32: 1049 snprintf(buf, sizeof(buf), "%s: (gauge32)%u", 1050 ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); 1051 break; 1052 case AX_DATA_TYPE_TIMETICKS: 1053 snprintf(buf, sizeof(buf), "%s: (timeticks)%u", 1054 ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); 1055 break; 1056 case AX_DATA_TYPE_OPAQUE: 1057 p = tmpbuf; 1058 bufleft = sizeof(tmpbuf); 1059 for (i = 0; 1060 i < vb->avb_data.avb_ostring.aos_slen; i++) { 1061 ret = snprintf(p, bufleft, " %02hhX", 1062 vb->avb_data.avb_ostring.aos_string[i]); 1063 if (ret >= (int) bufleft) { 1064 p = strrchr(p, ' '); 1065 strlcpy(p, "...", 4); 1066 break; 1067 } 1068 p += 3; 1069 bufleft -= 3; 1070 } 1071 ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s", 1072 ax_oid2string(&(vb->avb_oid)), tmpbuf); 1073 if (ret >= (int) sizeof(buf)) { 1074 p = strrchr(buf, ' '); 1075 strlcpy(p, "...", 4); 1076 } 1077 break; 1078 case AX_DATA_TYPE_COUNTER64: 1079 snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64, 1080 ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64); 1081 break; 1082 case AX_DATA_TYPE_NOSUCHOBJECT: 1083 snprintf(buf, sizeof(buf), "%s: <noSuchObject>", 1084 ax_oid2string(&(vb->avb_oid))); 1085 break; 1086 case AX_DATA_TYPE_NOSUCHINSTANCE: 1087 snprintf(buf, sizeof(buf), "%s: <noSuchInstance>", 1088 ax_oid2string(&(vb->avb_oid))); 1089 break; 1090 case AX_DATA_TYPE_ENDOFMIBVIEW: 1091 snprintf(buf, sizeof(buf), "%s: <endOfMibView>", 1092 ax_oid2string(&(vb->avb_oid))); 1093 break; 1094 } 1095 return buf; 1096 } 1097 1098 int 1099 ax_oid_cmp(struct ax_oid *o1, struct ax_oid *o2) 1100 { 1101 size_t i, min; 1102 1103 min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen; 1104 for (i = 0; i < min; i++) { 1105 if (o1->aoi_id[i] < o2->aoi_id[i]) 1106 return -1; 1107 if (o1->aoi_id[i] > o2->aoi_id[i]) 1108 return 1; 1109 } 1110 /* o1 is parent of o2 */ 1111 if (o1->aoi_idlen < o2->aoi_idlen) 1112 return -2; 1113 /* o1 is child of o2 */ 1114 if (o1->aoi_idlen > o2->aoi_idlen) 1115 return 2; 1116 return 0; 1117 } 1118 1119 int 1120 ax_oid_add(struct ax_oid *oid, uint32_t value) 1121 { 1122 if (oid->aoi_idlen == AX_OID_MAX_LEN) 1123 return -1; 1124 oid->aoi_id[oid->aoi_idlen++] = value; 1125 return 0; 1126 } 1127 1128 static uint32_t 1129 ax_pdu_queue(struct ax *ax) 1130 { 1131 struct ax_pdu_header header; 1132 uint32_t packetid, plength; 1133 size_t wbtlen = ax->ax_wbtlen; 1134 1135 header.aph_flags = ax->ax_byteorder == AX_BYTE_ORDER_BE ? 1136 AX_PDU_FLAG_NETWORK_BYTE_ORDER : 0; 1137 packetid = ax_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12])); 1138 plength = (ax->ax_wbtlen - ax->ax_wblen) - AX_PDU_HEADER; 1139 ax->ax_wbtlen = ax->ax_wblen + 16; 1140 (void)ax_pdu_add_uint32(ax, plength); 1141 1142 ax->ax_wblen = ax->ax_wbtlen = wbtlen; 1143 1144 return packetid; 1145 } 1146 1147 static int 1148 ax_pdu_header(struct ax *ax, enum ax_pdu_type type, uint8_t flags, 1149 uint32_t sessionid, uint32_t transactionid, uint32_t packetid, 1150 struct ax_ostring *context) 1151 { 1152 if (ax->ax_wblen != ax->ax_wbtlen) { 1153 errno = EALREADY; 1154 return -1; 1155 } 1156 1157 if (ax_pdu_need(ax, 4) == -1) 1158 return -1; 1159 ax->ax_wbuf[ax->ax_wbtlen++] = 1; 1160 ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type; 1161 if (context != NULL) 1162 flags |= AX_PDU_FLAG_NON_DEFAULT_CONTEXT; 1163 if (ax->ax_byteorder == AX_BYTE_ORDER_BE) 1164 flags |= AX_PDU_FLAG_NETWORK_BYTE_ORDER; 1165 ax->ax_wbuf[ax->ax_wbtlen++] = flags; 1166 ax->ax_wbuf[ax->ax_wbtlen++] = 0; 1167 if (packetid == 0) 1168 packetid = ax_packetid(ax); 1169 if (ax_pdu_add_uint32(ax, sessionid) == -1 || 1170 ax_pdu_add_uint32(ax, transactionid) == -1 || 1171 ax_pdu_add_uint32(ax, packetid) == -1 || 1172 ax_pdu_need(ax, 4) == -1) 1173 return -1; 1174 ax->ax_wbtlen += 4; 1175 if (context != NULL) { 1176 if (ax_pdu_add_str(ax, context) == -1) 1177 return -1; 1178 } 1179 1180 return 0; 1181 } 1182 1183 static uint32_t 1184 ax_packetid(struct ax *ax) 1185 { 1186 uint32_t packetid, *packetids; 1187 size_t npackets = 0, i; 1188 int found; 1189 1190 if (ax->ax_packetids != NULL) { 1191 for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++) 1192 continue; 1193 } 1194 if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) { 1195 packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize, 1196 ax->ax_packetidsize + 25, sizeof(*packetids)); 1197 if (packetids == NULL) 1198 return 0; 1199 ax->ax_packetidsize += 25; 1200 ax->ax_packetids = packetids; 1201 } 1202 do { 1203 found = 0; 1204 packetid = arc4random(); 1205 for (i = 0; ax->ax_packetids[i] != 0; i++) { 1206 if (ax->ax_packetids[i] == packetid) { 1207 found = 1; 1208 break; 1209 } 1210 } 1211 } while (packetid == 0 || found); 1212 ax->ax_packetids[npackets] = packetid; 1213 1214 return packetid; 1215 } 1216 1217 static int 1218 ax_pdu_add_uint16(struct ax *ax, uint16_t value) 1219 { 1220 if (ax_pdu_need(ax, sizeof(value)) == -1) 1221 return -1; 1222 1223 if (ax->ax_byteorder == AX_BYTE_ORDER_BE) 1224 value = htobe16(value); 1225 else 1226 value = htole16(value); 1227 memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); 1228 ax->ax_wbtlen += sizeof(value); 1229 return 0; 1230 } 1231 1232 static int 1233 ax_pdu_add_uint32(struct ax *ax, uint32_t value) 1234 { 1235 if (ax_pdu_need(ax, sizeof(value)) == -1) 1236 return -1; 1237 1238 if (ax->ax_byteorder == AX_BYTE_ORDER_BE) 1239 value = htobe32(value); 1240 else 1241 value = htole32(value); 1242 memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); 1243 ax->ax_wbtlen += sizeof(value); 1244 return 0; 1245 } 1246 1247 static int 1248 ax_pdu_add_uint64(struct ax *ax, uint64_t value) 1249 { 1250 if (ax_pdu_need(ax, sizeof(value)) == -1) 1251 return -1; 1252 1253 if (ax->ax_byteorder == AX_BYTE_ORDER_BE) 1254 value = htobe64(value); 1255 else 1256 value = htole64(value); 1257 memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); 1258 ax->ax_wbtlen += sizeof(value); 1259 return 0; 1260 } 1261 1262 1263 static int 1264 ax_pdu_add_oid(struct ax *ax, struct ax_oid *oid) 1265 { 1266 static struct ax_oid nulloid = {0}; 1267 uint8_t prefix = 0, n_subid, i = 0; 1268 1269 n_subid = oid->aoi_idlen; 1270 1271 if (oid == NULL) 1272 oid = &nulloid; 1273 1274 if (oid->aoi_idlen > 4 && 1275 oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 && 1276 oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 && 1277 oid->aoi_id[4] <= UINT8_MAX) { 1278 prefix = oid->aoi_id[4]; 1279 i = 5; 1280 } 1281 1282 if (ax_pdu_need(ax, 4) == -1) 1283 return -1; 1284 ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i; 1285 ax->ax_wbuf[ax->ax_wbtlen++] = prefix; 1286 ax->ax_wbuf[ax->ax_wbtlen++] = oid->aoi_include; 1287 ax->ax_wbuf[ax->ax_wbtlen++] = 0; 1288 1289 for (; i < n_subid; i++) { 1290 if (ax_pdu_add_uint32(ax, oid->aoi_id[i]) == -1) 1291 return -1; 1292 } 1293 1294 return 0; 1295 } 1296 1297 static int 1298 ax_pdu_add_str(struct ax *ax, struct ax_ostring *str) 1299 { 1300 size_t length, zeroes; 1301 1302 if (ax_pdu_add_uint32(ax, str->aos_slen) == -1) 1303 return -1; 1304 1305 if ((zeroes = (4 - (str->aos_slen % 4))) == 4) 1306 zeroes = 0; 1307 length = str->aos_slen + zeroes; 1308 if (ax_pdu_need(ax, length) == -1) 1309 return -1; 1310 1311 memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen); 1312 ax->ax_wbtlen += str->aos_slen; 1313 memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes); 1314 ax->ax_wbtlen += zeroes; 1315 return 0; 1316 } 1317 1318 static int 1319 ax_pdu_add_varbindlist(struct ax *ax, 1320 struct ax_varbind *vblist, size_t nvb) 1321 { 1322 size_t i; 1323 uint16_t temp; 1324 1325 for (i = 0; i < nvb; i++) { 1326 temp = (uint16_t) vblist[i].avb_type; 1327 if (ax_pdu_add_uint16(ax, temp) == -1 || 1328 ax_pdu_need(ax, 2)) 1329 return -1; 1330 memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2); 1331 ax->ax_wbtlen += 2; 1332 if (ax_pdu_add_oid(ax, &(vblist[i].avb_oid)) == -1) 1333 return -1; 1334 switch (vblist[i].avb_type) { 1335 case AX_DATA_TYPE_INTEGER: 1336 if (ax_pdu_add_uint32(ax, 1337 vblist[i].avb_data.avb_int32) == -1) 1338 return -1; 1339 break; 1340 case AX_DATA_TYPE_COUNTER32: 1341 case AX_DATA_TYPE_GAUGE32: 1342 case AX_DATA_TYPE_TIMETICKS: 1343 if (ax_pdu_add_uint32(ax, 1344 vblist[i].avb_data.avb_uint32) == -1) 1345 return -1; 1346 break; 1347 case AX_DATA_TYPE_COUNTER64: 1348 if (ax_pdu_add_uint64(ax, 1349 vblist[i].avb_data.avb_uint64) == -1) 1350 return -1; 1351 break; 1352 case AX_DATA_TYPE_OCTETSTRING: 1353 case AX_DATA_TYPE_IPADDRESS: 1354 case AX_DATA_TYPE_OPAQUE: 1355 if (ax_pdu_add_str(ax, 1356 &(vblist[i].avb_data.avb_ostring)) == -1) 1357 return -1; 1358 break; 1359 case AX_DATA_TYPE_OID: 1360 if (ax_pdu_add_oid(ax, 1361 &(vblist[i].avb_data.avb_oid)) == -1) 1362 return -1; 1363 break; 1364 case AX_DATA_TYPE_NULL: 1365 case AX_DATA_TYPE_NOSUCHOBJECT: 1366 case AX_DATA_TYPE_NOSUCHINSTANCE: 1367 case AX_DATA_TYPE_ENDOFMIBVIEW: 1368 break; 1369 default: 1370 errno = EINVAL; 1371 return -1; 1372 } 1373 } 1374 return 0; 1375 } 1376 1377 static int 1378 ax_pdu_add_searchrange(struct ax *ax, struct ax_searchrange *sr) 1379 { 1380 if (ax_pdu_add_oid(ax, &(sr->asr_start)) == -1 || 1381 ax_pdu_add_oid(ax, &(sr->asr_stop)) == -1) 1382 return -1; 1383 return 0; 1384 } 1385 1386 static uint16_t 1387 ax_pdutoh16(struct ax_pdu_header *header, uint8_t *buf) 1388 { 1389 uint16_t value; 1390 1391 memcpy(&value, buf, sizeof(value)); 1392 if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) 1393 return be16toh(value); 1394 return le16toh(value); 1395 } 1396 1397 static uint32_t 1398 ax_pdutoh32(struct ax_pdu_header *header, uint8_t *buf) 1399 { 1400 uint32_t value; 1401 1402 memcpy(&value, buf, sizeof(value)); 1403 if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) 1404 return be32toh(value); 1405 return le32toh(value); 1406 } 1407 1408 static uint64_t 1409 ax_pdutoh64(struct ax_pdu_header *header, uint8_t *buf) 1410 { 1411 uint64_t value; 1412 1413 memcpy(&value, buf, sizeof(value)); 1414 if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER) 1415 return be64toh(value); 1416 return le64toh(value); 1417 } 1418 1419 static ssize_t 1420 ax_pdutooid(struct ax_pdu_header *header, struct ax_oid *oid, 1421 uint8_t *buf, size_t rawlen) 1422 { 1423 size_t i = 0; 1424 ssize_t nread; 1425 1426 if (rawlen < 4) 1427 goto fail; 1428 rawlen -= 4; 1429 nread = 4; 1430 oid->aoi_idlen = *buf++; 1431 if (rawlen < (oid->aoi_idlen * 4)) 1432 goto fail; 1433 nread += oid->aoi_idlen * 4; 1434 if (*buf != 0) { 1435 oid->aoi_id[0] = 1; 1436 oid->aoi_id[1] = 3; 1437 oid->aoi_id[2] = 6; 1438 oid->aoi_id[3] = 1; 1439 oid->aoi_id[4] = *buf; 1440 oid->aoi_idlen += 5; 1441 i = 5; 1442 } 1443 buf++; 1444 oid->aoi_include = *buf; 1445 if (oid->aoi_idlen > AX_OID_MAX_LEN) 1446 goto fail; 1447 for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) 1448 oid->aoi_id[i] = ax_pdutoh32(header, buf); 1449 1450 return nread; 1451 1452 fail: 1453 errno = EPROTO; 1454 return -1; 1455 } 1456 1457 static ssize_t 1458 ax_pdutoostring(struct ax_pdu_header *header, 1459 struct ax_ostring *ostring, uint8_t *buf, size_t rawlen) 1460 { 1461 ssize_t nread; 1462 1463 if (rawlen < 4) 1464 goto fail; 1465 1466 ostring->aos_slen = ax_pdutoh32(header, buf); 1467 rawlen -= 4; 1468 buf += 4; 1469 if (ostring->aos_slen > rawlen) 1470 goto fail; 1471 if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL) 1472 return -1; 1473 memcpy(ostring->aos_string, buf, ostring->aos_slen); 1474 ostring->aos_string[ostring->aos_slen] = '\0'; 1475 1476 nread = 4 + ostring->aos_slen; 1477 if (ostring->aos_slen % 4 != 0) 1478 nread += 4 - (ostring->aos_slen % 4); 1479 1480 return nread; 1481 1482 fail: 1483 errno = EPROTO; 1484 return -1; 1485 } 1486 1487 static ssize_t 1488 ax_pdutovarbind(struct ax_pdu_header *header, 1489 struct ax_varbind *varbind, uint8_t *buf, size_t rawlen) 1490 { 1491 ssize_t nread, rread = 4; 1492 1493 if (rawlen == 0) 1494 return 0; 1495 1496 if (rawlen < 8) 1497 goto fail; 1498 varbind->avb_type = ax_pdutoh16(header, buf); 1499 1500 buf += 4; 1501 rawlen -= 4; 1502 nread = ax_pdutooid(header, &(varbind->avb_oid), buf, rawlen); 1503 if (nread == -1) 1504 return -1; 1505 rread += nread; 1506 buf += nread; 1507 rawlen -= nread; 1508 1509 switch(varbind->avb_type) { 1510 case AX_DATA_TYPE_INTEGER: 1511 if (rawlen < 4) 1512 goto fail; 1513 varbind->avb_data.avb_int32 = ax_pdutoh32(header, buf); 1514 return rread + 4; 1515 case AX_DATA_TYPE_COUNTER32: 1516 case AX_DATA_TYPE_GAUGE32: 1517 case AX_DATA_TYPE_TIMETICKS: 1518 if (rawlen < 4) 1519 goto fail; 1520 varbind->avb_data.avb_uint32 = ax_pdutoh32(header, buf); 1521 return rread + 4; 1522 case AX_DATA_TYPE_COUNTER64: 1523 if (rawlen < 8) 1524 goto fail; 1525 varbind->avb_data.avb_uint64 = ax_pdutoh64(header, buf); 1526 return rread + 8; 1527 case AX_DATA_TYPE_OCTETSTRING: 1528 case AX_DATA_TYPE_IPADDRESS: 1529 case AX_DATA_TYPE_OPAQUE: 1530 nread = ax_pdutoostring(header, 1531 &(varbind->avb_data.avb_ostring), buf, rawlen); 1532 if (nread == -1) 1533 return -1; 1534 return nread + rread; 1535 case AX_DATA_TYPE_OID: 1536 nread = ax_pdutooid(header, &(varbind->avb_data.avb_oid), 1537 buf, rawlen); 1538 if (nread == -1) 1539 return -1; 1540 return nread + rread; 1541 case AX_DATA_TYPE_NULL: 1542 case AX_DATA_TYPE_NOSUCHOBJECT: 1543 case AX_DATA_TYPE_NOSUCHINSTANCE: 1544 case AX_DATA_TYPE_ENDOFMIBVIEW: 1545 return rread; 1546 } 1547 1548 fail: 1549 errno = EPROTO; 1550 return -1; 1551 } 1552