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