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