xref: /openbsd-src/usr.sbin/snmpd/ax.c (revision 68dd5bb1859285b71cb62a10bf107b8ad54064d9)
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