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