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