xref: /openbsd-src/lib/libagentx/agentx.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*
2  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <sys/socket.h>
17 
18 #include <arpa/inet.h>
19 
20 #include <ctype.h>
21 #include <endian.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <unistd.h>
30 
31 #include "agentx.h"
32 
33 #define AGENTX_PDU_HEADER 20
34 
35 static int agentx_pdu_need(struct agentx *, size_t);
36 static int agentx_pdu_header(struct agentx *,
37     enum agentx_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t,
38     struct agentx_ostring *);
39 static uint32_t agentx_packetid(struct agentx *);
40 static uint32_t agentx_pdu_queue(struct agentx *);
41 static int agentx_pdu_add_uint16(struct agentx *, uint16_t);
42 static int agentx_pdu_add_uint32(struct agentx *, uint32_t);
43 static int agentx_pdu_add_uint64(struct agentx *, uint64_t);
44 static int agentx_pdu_add_oid(struct agentx *, struct agentx_oid *, int);
45 static int agentx_pdu_add_str(struct agentx *, struct agentx_ostring *);
46 static int agentx_pdu_add_varbindlist( struct agentx *, struct agentx_varbind *,
47     size_t);
48 static uint16_t agentx_pdutoh16(struct agentx_pdu_header *, uint8_t *);
49 static uint32_t agentx_pdutoh32(struct agentx_pdu_header *, uint8_t *);
50 static uint64_t agentx_pdutoh64(struct agentx_pdu_header *, uint8_t *);
51 static ssize_t agentx_pdutooid(struct agentx_pdu_header *, struct agentx_oid *,
52     uint8_t *, size_t);
53 static ssize_t agentx_pdutoostring(struct agentx_pdu_header *,
54     struct agentx_ostring *, uint8_t *, size_t);
55 static ssize_t agentx_pdutovarbind(struct agentx_pdu_header *,
56     struct agentx_varbind *, uint8_t *, size_t);
57 
58 struct agentx *
59 agentx_new(int fd)
60 {
61 	struct agentx *ax;
62 
63 	if (fd == -1) {
64 		errno = EINVAL;
65 		return NULL;
66 	}
67 
68 	if ((ax = calloc(1, sizeof(*ax))) == NULL)
69 		return NULL;
70 	ax->ax_fd = fd;
71 	if ((ax->ax_rbuf = malloc(512)) == NULL)
72 		goto fail;
73 	ax->ax_rbsize = 512;
74 	ax->ax_byteorder = AGENTX_BYTE_ORDER_NATIVE;
75 
76 	return ax;
77 
78 fail:
79 	free(ax);
80 	return NULL;
81 }
82 
83 void
84 agentx_free(struct agentx *ax)
85 {
86 	if (ax == NULL)
87 		return;
88 	close(ax->ax_fd);
89 	free(ax->ax_rbuf);
90 	free(ax->ax_wbuf);
91 	free(ax->ax_packetids);
92 	free(ax);
93 }
94 
95 struct agentx_pdu *
96 agentx_recv(struct agentx *ax)
97 {
98 	struct agentx_pdu *pdu;
99 	struct agentx_pdu_header header;
100 	struct agentx_pdu_response *response;
101 	struct agentx_varbind *varbind;
102 	struct agentx_pdu_searchrangelist *srl;
103 	struct agentx_pdu_varbindlist *vbl;
104 	struct agentx_searchrange *sr;
105 	size_t rbsize, packetidx = 0, i, rawlen;
106 	ssize_t nread;
107 	uint8_t *u8;
108 	uint8_t *rbuf;
109 	int found;
110 
111 	/* Only read a single packet at a time to make sure libevent triggers */
112 	if (ax->ax_rblen < AGENTX_PDU_HEADER) {
113 		if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen,
114 		    AGENTX_PDU_HEADER - ax->ax_rblen)) == 0) {
115 			errno = ECONNRESET;
116 			return NULL;
117 		}
118 		if (nread == -1)
119 			return NULL;
120 		ax->ax_rblen += nread;
121 		if (ax->ax_rblen < AGENTX_PDU_HEADER) {
122 			errno = EAGAIN;
123 			return NULL;
124 		}
125 	}
126 	u8 = ax->ax_rbuf;
127 	header.aph_version = *u8++;
128 	header.aph_type = *u8++;
129 	header.aph_flags = *u8++;
130 	u8++;
131 	header.aph_sessionid = agentx_pdutoh32(&header, u8);
132 	u8 += 4;
133 	header.aph_transactionid = agentx_pdutoh32(&header, u8);
134 	u8 += 4;
135 	header.aph_packetid = agentx_pdutoh32(&header, u8);
136 	u8 += 4;
137 	header.aph_plength = agentx_pdutoh32(&header, u8);
138 
139 	if (header.aph_version != 1) {
140 		errno = EPROTO;
141 		return NULL;
142 	}
143 	if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) {
144 		if (AGENTX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) {
145 			rbsize = (((AGENTX_PDU_HEADER + header.aph_plength)
146 			    / 512) + 1) * 512;
147 			if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize,
148 			    rbsize, sizeof(*rbuf))) == NULL)
149 				return NULL;
150 			ax->ax_rbsize = rbsize;
151 			ax->ax_rbuf = rbuf;
152 		}
153 		nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen,
154 		    header.aph_plength - (ax->ax_rblen - AGENTX_PDU_HEADER));
155 		if (nread == 0)
156 			errno = ECONNRESET;
157 		if (nread <= 0)
158 			return NULL;
159 		ax->ax_rblen += nread;
160 		if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) {
161 			errno = EAGAIN;
162 			return NULL;
163 		}
164 	}
165 
166 	if ((pdu = calloc(1, sizeof(*pdu))) == NULL)
167 		return NULL;
168 
169 	memcpy(&(pdu->ap_header), &header, sizeof(header));
170 
171 #if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE)
172 	{
173 		char chars[4];
174 		int print = 1;
175 
176 		fprintf(stderr, "received packet:\n");
177 		for (i = 0; i < pdu->ap_header.aph_plength + AGENTX_PDU_HEADER;
178 		    i++) {
179 			fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]);
180 			chars[i % 4] = ax->ax_rbuf[i];
181 			if (!isprint(ax->ax_rbuf[i]))
182 				print = 0;
183 			if (i % 4 == 3) {
184 				if (print)
185 					fprintf(stderr, "%.4s", chars);
186 				fprintf(stderr, "\n");
187 				print = 1;
188 			}
189 		}
190 	}
191 #endif
192 
193 	u8 = (ax->ax_rbuf) + AGENTX_PDU_HEADER;
194 	rawlen = pdu->ap_header.aph_plength;
195 	if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
196 		nread = agentx_pdutoostring(&header, &(pdu->ap_context), u8,
197 		    rawlen);
198 		if (nread == -1)
199 			goto fail;
200 		rawlen -= nread;
201 		u8 += nread;
202 	}
203 
204 	switch (pdu->ap_header.aph_type) {
205 	case AGENTX_PDU_TYPE_GETBULK:
206 		if (rawlen < 4) {
207 			errno = EPROTO;
208 			goto fail;
209 		}
210 		pdu->ap_payload.ap_getbulk.ap_nonrep =
211 		    agentx_pdutoh16(&header, u8);
212 		u8 += 2;
213 		pdu->ap_payload.ap_getbulk.ap_maxrep =
214 		    agentx_pdutoh16(&header, u8);
215 		u8 += 2;
216 		srl = &(pdu->ap_payload.ap_getbulk.ap_srl);
217 		rawlen -= 4;
218 		/* FALLTHROUGH */
219 	case AGENTX_PDU_TYPE_GET:
220 	case AGENTX_PDU_TYPE_GETNEXT:
221 		if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_GETBULK)
222 			srl = &(pdu->ap_payload.ap_srl);
223 		while (rawlen > 0 ) {
224 			srl->ap_nsr++;
225 			sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr));
226 			if (sr == NULL)
227 				goto fail;
228 			srl->ap_sr = sr;
229 			sr += (srl->ap_nsr - 1);
230 			if ((nread = agentx_pdutooid(&header, &(sr->asr_start),
231 			    u8, rawlen)) == -1)
232 				goto fail;
233 			rawlen -= nread;
234 			u8 += nread;
235 			if ((nread = agentx_pdutooid(&header, &(sr->asr_stop),
236 			    u8, rawlen)) == -1)
237 				goto fail;
238 			rawlen -= nread;
239 			u8 += nread;
240 		}
241 		break;
242 	case AGENTX_PDU_TYPE_TESTSET:
243 		vbl = &(pdu->ap_payload.ap_vbl);
244 		while (rawlen > 0) {
245 			varbind = recallocarray(vbl->ap_varbind,
246 			    vbl->ap_nvarbind, vbl->ap_nvarbind + 1,
247 			    sizeof(*(vbl->ap_varbind)));
248 			if (varbind == NULL)
249 				goto fail;
250 			vbl->ap_varbind = varbind;
251 			nread = agentx_pdutovarbind(&header,
252 			    &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen);
253 			if (nread == -1)
254 				goto fail;
255 			vbl->ap_nvarbind++;
256 			u8 += nread;
257 			rawlen -= nread;
258 		}
259 		break;
260 	case AGENTX_PDU_TYPE_COMMITSET:
261 	case AGENTX_PDU_TYPE_UNDOSET:
262 	case AGENTX_PDU_TYPE_CLEANUPSET:
263 		if (rawlen != 0) {
264 			errno = EPROTO;
265 			goto fail;
266 		}
267 		break;
268 	case AGENTX_PDU_TYPE_RESPONSE:
269 		if (ax->ax_packetids != NULL) {
270 			found = 0;
271 			for (i = 0; ax->ax_packetids[i] != 0; i++) {
272 				if (ax->ax_packetids[i] ==
273 				    pdu->ap_header.aph_packetid) {
274 					packetidx = i;
275 					found = 1;
276 				}
277 			}
278 			if (found) {
279 				ax->ax_packetids[packetidx] =
280 				    ax->ax_packetids[i - 1];
281 				ax->ax_packetids[i - 1] = 0;
282 			} else {
283 				errno = EPROTO;
284 				goto fail;
285 			}
286 		}
287 		if (rawlen < 8) {
288 			errno = EPROTO;
289 			goto fail;
290 		}
291 		response = &(pdu->ap_payload.ap_response);
292 		response->ap_uptime = agentx_pdutoh32(&header, u8);
293 		u8 += 4;
294 		response->ap_error = agentx_pdutoh16(&header, u8);
295 		u8 += 2;
296 		response->ap_index = agentx_pdutoh16(&header, u8);
297 		u8 += 2;
298 		rawlen -= 8;
299 		while (rawlen > 0) {
300 			varbind = recallocarray(response->ap_varbindlist,
301 			    response->ap_nvarbind, response->ap_nvarbind + 1,
302 			    sizeof(*(response->ap_varbindlist)));
303 			if (varbind == NULL)
304 				goto fail;
305 			response->ap_varbindlist = varbind;
306 			nread = agentx_pdutovarbind(&header,
307 			    &(response->ap_varbindlist[response->ap_nvarbind]),
308 			    u8, rawlen);
309 			if (nread == -1)
310 				goto fail;
311 			response->ap_nvarbind++;
312 			u8 += nread;
313 			rawlen -= nread;
314 		}
315 		break;
316 	default:
317 		pdu->ap_payload.ap_raw = malloc(pdu->ap_header.aph_plength);
318 		if (pdu->ap_payload.ap_raw == NULL)
319 			goto fail;
320 		memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AGENTX_PDU_HEADER,
321 		    pdu->ap_header.aph_plength);
322 		break;
323 	}
324 
325 	ax->ax_rblen = 0;
326 
327 	return pdu;
328 fail:
329 	agentx_pdu_free(pdu);
330 	return NULL;
331 }
332 
333 static int
334 agentx_pdu_need(struct agentx *ax, size_t need)
335 {
336 	uint8_t *wbuf;
337 	size_t wbsize;
338 
339 	if (ax->ax_wbtlen >= ax->ax_wbsize) {
340 		wbsize = ((ax->ax_wbtlen / 512) + 1) * 512;
341 		wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1);
342 		if (wbuf == NULL) {
343 			ax->ax_wbtlen = ax->ax_wblen;
344 			return -1;
345 		}
346 		ax->ax_wbsize = wbsize;
347 		ax->ax_wbuf = wbuf;
348 	}
349 
350 	return 0;
351 }
352 
353 ssize_t
354 agentx_send(struct agentx *ax)
355 {
356 	ssize_t nwrite;
357 
358 	if (ax->ax_wblen != ax->ax_wbtlen) {
359 		errno = EALREADY;
360 		return -1;
361 	}
362 
363 	if (ax->ax_wblen == 0)
364 		return 0;
365 
366 #if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE)
367 	{
368 		size_t i;
369 		char chars[4];
370 		int print = 1;
371 
372 		fprintf(stderr, "sending packet:\n");
373 		for (i = 0; i < ax->ax_wblen; i++) {
374 			fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]);
375 			chars[i % 4] = ax->ax_wbuf[i];
376 			if (!isprint(ax->ax_wbuf[i]))
377 				print = 0;
378 			if (i % 4 == 3) {
379 				if (print)
380 					fprintf(stderr, "%.4s", chars);
381 				fprintf(stderr, "\n");
382 				print = 1;
383 			}
384 		}
385 	}
386 #endif
387 
388 	if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen,
389 	    MSG_NOSIGNAL | MSG_DONTWAIT)) == -1)
390 		return -1;
391 
392 	memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite);
393 	ax->ax_wblen -= nwrite;
394 	ax->ax_wbtlen = ax->ax_wblen;
395 
396 	return ax->ax_wblen;
397 }
398 
399 uint32_t
400 agentx_open(struct agentx *ax, uint8_t timeout, struct agentx_oid *oid,
401     struct agentx_ostring *descr)
402 {
403 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_OPEN, 0, 0, 0, 0,
404 	    NULL) == -1)
405 		return 0;
406 	agentx_pdu_need(ax, 4);
407 	ax->ax_wbuf[ax->ax_wbtlen++] = timeout;
408 	memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3);
409 	ax->ax_wbtlen += 3;
410 	if (agentx_pdu_add_oid(ax, oid, 0) == -1)
411 		return 0;
412 	if (agentx_pdu_add_str(ax, descr) == -1)
413 		return 0;
414 
415 	return agentx_pdu_queue(ax);
416 }
417 
418 uint32_t
419 agentx_close(struct agentx *ax, uint32_t sessionid,
420     enum agentx_close_reason reason)
421 {
422 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0,
423 	    NULL) == -1)
424 		return 0;
425 
426 	if (agentx_pdu_need(ax, 4) == -1)
427 		return 0;
428 	ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason;
429 	memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3);
430 	ax->ax_wbtlen += 3;
431 
432 	return agentx_pdu_queue(ax);
433 }
434 
435 uint32_t
436 agentx_indexallocate(struct agentx *ax, uint8_t flags, uint32_t sessionid,
437     struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb)
438 {
439 	if (flags & ~(AGENTX_PDU_FLAG_NEW_INDEX | AGENTX_PDU_FLAG_ANY_INDEX)) {
440 		errno = EINVAL;
441 		return 0;
442 	}
443 
444 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXALLOCATE, flags,
445 	    sessionid, 0, 0, context) == -1)
446 		return 0;
447 
448 	if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
449 		return 0;
450 
451 	return agentx_pdu_queue(ax);
452 }
453 
454 uint32_t
455 agentx_indexdeallocate(struct agentx *ax, uint32_t sessionid,
456     struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb)
457 {
458 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXDEALLOCATE, 0,
459 	    sessionid, 0, 0, context) == -1)
460 		return 0;
461 
462 	if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
463 		return 0;
464 
465 	return agentx_pdu_queue(ax);
466 }
467 
468 uint32_t
469 agentx_addagentcaps(struct agentx *ax, uint32_t sessionid,
470     struct agentx_ostring *context, struct agentx_oid *id,
471     struct agentx_ostring *descr)
472 {
473 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_ADDAGENTCAPS, 0,
474 	    sessionid, 0, 0, context) == -1)
475 		return 0;
476 	if (agentx_pdu_add_oid(ax, id, 0) == -1)
477 		return 0;
478 	if (agentx_pdu_add_str(ax, descr) == -1)
479 		return 0;
480 
481 	return agentx_pdu_queue(ax);
482 }
483 
484 uint32_t
485 agentx_removeagentcaps(struct agentx *ax, uint32_t sessionid,
486     struct agentx_ostring *context, struct agentx_oid *id)
487 {
488 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REMOVEAGENTCAPS, 0,
489 	    sessionid, 0, 0, context) == -1)
490 		return 0;
491 	if (agentx_pdu_add_oid(ax, id, 0) == -1)
492 		return 0;
493 
494 	return agentx_pdu_queue(ax);
495 
496 }
497 
498 uint32_t
499 agentx_register(struct agentx *ax, uint8_t flags, uint32_t sessionid,
500     struct agentx_ostring *context, uint8_t timeout, uint8_t priority,
501     uint8_t range_subid, struct agentx_oid *subtree, uint32_t upperbound)
502 {
503 	if (flags & ~(AGENTX_PDU_FLAG_INSTANCE_REGISTRATION)) {
504 		errno = EINVAL;
505 		return 0;
506 	}
507 
508 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REGISTER, flags,
509 	    sessionid, 0, 0, context) == -1)
510 		return 0;
511 
512 	if (agentx_pdu_need(ax, 4) == -1)
513 		return 0;
514 	ax->ax_wbuf[ax->ax_wbtlen++] = timeout;
515 	ax->ax_wbuf[ax->ax_wbtlen++] = priority;
516 	ax->ax_wbuf[ax->ax_wbtlen++] = range_subid;
517 	ax->ax_wbuf[ax->ax_wbtlen++] = 0;
518 	if (agentx_pdu_add_oid(ax, subtree, 0) == -1)
519 		return 0;
520 	if (range_subid != 0) {
521 		if (agentx_pdu_add_uint32(ax, upperbound) == -1)
522 			return 0;
523 	}
524 
525 	return agentx_pdu_queue(ax);
526 }
527 
528 uint32_t
529 agentx_unregister(struct agentx *ax, uint32_t sessionid,
530     struct agentx_ostring *context, uint8_t priority, uint8_t range_subid,
531     struct agentx_oid *subtree, uint32_t upperbound)
532 {
533 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_UNREGISTER, 0,
534 	    sessionid, 0, 0, context) == -1)
535 		return 0;
536 
537 	if (agentx_pdu_need(ax, 4) == -1)
538 		return 0;
539 	ax->ax_wbuf[ax->ax_wbtlen++] = 0;
540 	ax->ax_wbuf[ax->ax_wbtlen++] = priority;
541 	ax->ax_wbuf[ax->ax_wbtlen++] = range_subid;
542 	ax->ax_wbuf[ax->ax_wbtlen++] = 0;
543 	if (agentx_pdu_add_oid(ax, subtree, 0) == -1)
544 		return 0;
545 	if (range_subid != 0) {
546 		if (agentx_pdu_add_uint32(ax, upperbound) == -1)
547 			return 0;
548 	}
549 
550 	return agentx_pdu_queue(ax);
551 }
552 
553 int
554 agentx_response(struct agentx *ax, uint32_t sessionid, uint32_t transactionid,
555     uint32_t packetid, struct agentx_ostring *context, uint32_t sysuptime,
556     uint16_t error, uint16_t index, struct agentx_varbind *vblist, size_t nvb)
557 {
558 	if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_RESPONSE, 0, sessionid,
559 	    transactionid, packetid, context) == -1)
560 		return -1;
561 
562 	if (agentx_pdu_add_uint32(ax, sysuptime) == -1 ||
563 	    agentx_pdu_add_uint16(ax, error) == -1 ||
564 	    agentx_pdu_add_uint16(ax, index) == -1)
565 		return -1;
566 
567 	if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
568 		return -1;
569 	if (agentx_pdu_queue(ax) == 0)
570 		return -1;
571 	return 0;
572 }
573 
574 void
575 agentx_pdu_free(struct agentx_pdu *pdu)
576 {
577 	size_t i;
578 	struct agentx_pdu_response *response;
579 
580 	if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT)
581 		free(pdu->ap_context.aos_string);
582 
583 	switch (pdu->ap_header.aph_type) {
584 	case AGENTX_PDU_TYPE_GET:
585 	case AGENTX_PDU_TYPE_GETNEXT:
586 	case AGENTX_PDU_TYPE_GETBULK:
587 		free(pdu->ap_payload.ap_srl.ap_sr);
588 		break;
589 	case AGENTX_PDU_TYPE_RESPONSE:
590 		response = &(pdu->ap_payload.ap_response);
591 		for (i = 0; i < response->ap_nvarbind; i++)
592 			agentx_varbind_free(&(response->ap_varbindlist[i]));
593 		free(response->ap_varbindlist);
594 		break;
595 	default:
596 		free(pdu->ap_payload.ap_raw);
597 		break;
598 	}
599 	free(pdu);
600 }
601 
602 void
603 agentx_varbind_free(struct agentx_varbind *varbind)
604 {
605 	switch (varbind->avb_type) {
606 	case AGENTX_DATA_TYPE_OCTETSTRING:
607 	case AGENTX_DATA_TYPE_IPADDRESS:
608 	case AGENTX_DATA_TYPE_OPAQUE:
609 		free(varbind->avb_data.avb_ostring.aos_string);
610 		break;
611 	default:
612 		break;
613 	}
614 }
615 
616 const char *
617 agentx_error2string(enum agentx_pdu_error error)
618 {
619 	static char buffer[64];
620 	switch (error) {
621 	case AGENTX_PDU_ERROR_NOERROR:
622 		return "No error";
623 	case AGENTX_PDU_ERROR_GENERR:
624 		return "Generic error";
625 	case AGENTX_PDU_ERROR_NOACCESS:
626 		return "No access";
627 	case AGENTX_PDU_ERROR_WRONGTYPE:
628 		return "Wrong type";
629 	case AGENTX_PDU_ERROR_WRONGLENGTH:
630 		return "Wrong length";
631 	case AGENTX_PDU_ERROR_WRONGENCODING:
632 		return "Wrong encoding";
633 	case AGENTX_PDU_ERROR_WRONGVALUE:
634 		return "Wrong value";
635 	case AGENTX_PDU_ERROR_NOCREATION:
636 		return "No creation";
637 	case AGENTX_PDU_ERROR_INCONSISTENTVALUE:
638 		return "Inconsistent value";
639 	case AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE:
640 		return "Resource unavailable";
641 	case AGENTX_PDU_ERROR_COMMITFAILED:
642 		return "Commit failed";
643 	case AGENTX_PDU_ERROR_UNDOFAILED:
644 		return "Undo failed";
645 	case AGENTX_PDU_ERROR_NOTWRITABLE:
646 		return "Not writable";
647 	case AGENTX_PDU_ERROR_INCONSISTENTNAME:
648 		return "Inconsistent name";
649 	case AGENTX_PDU_ERROR_OPENFAILED:
650 		return "Open Failed";
651 	case AGENTX_PDU_ERROR_NOTOPEN:
652 		return "Not open";
653 	case AGENTX_PDU_ERROR_INDEXWRONGTYPE:
654 		return "Index wrong type";
655 	case AGENTX_PDU_ERROR_INDEXALREADYALLOCATED:
656 		return "Index already allocated";
657 	case AGENTX_PDU_ERROR_INDEXNONEAVAILABLE:
658 		return "Index none available";
659 	case AGENTX_PDU_ERROR_INDEXNOTALLOCATED:
660 		return "Index not allocated";
661 	case AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT:
662 		return "Unsupported context";
663 	case AGENTX_PDU_ERROR_DUPLICATEREGISTRATION:
664 		return "Duplicate registration";
665 	case AGENTX_PDU_ERROR_UNKNOWNREGISTRATION:
666 		return "Unkown registration";
667 	case AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS:
668 		return "Unknown agent capabilities";
669 	case AGENTX_PDU_ERROR_PARSEERROR:
670 		return "Parse error";
671 	case AGENTX_PDU_ERROR_REQUESTDENIED:
672 		return "Request denied";
673 	case AGENTX_PDU_ERROR_PROCESSINGERROR:
674 		return "Processing error";
675 	}
676 	snprintf(buffer, sizeof(buffer), "Unknown error: %d", error);
677 	return buffer;
678 }
679 
680 const char *
681 agentx_pdutype2string(enum agentx_pdu_type type)
682 {
683 	static char buffer[64];
684 	switch(type) {
685 	case AGENTX_PDU_TYPE_OPEN:
686 		return "agentx-Open-PDU";
687 	case AGENTX_PDU_TYPE_CLOSE:
688 		return "agentx-Close-PDU";
689 	case AGENTX_PDU_TYPE_REGISTER:
690 		return "agentx-Register-PDU";
691 	case AGENTX_PDU_TYPE_UNREGISTER:
692 		return "agentx-Unregister-PDU";
693 	case AGENTX_PDU_TYPE_GET:
694 		return "agentx-Get-PDU";
695 	case AGENTX_PDU_TYPE_GETNEXT:
696 		return "agentx-GetNext-PDU";
697 	case AGENTX_PDU_TYPE_GETBULK:
698 		return "agentx-GetBulk-PDU";
699 	case AGENTX_PDU_TYPE_TESTSET:
700 		return "agentx-TestSet-PDU";
701 	case AGENTX_PDU_TYPE_COMMITSET:
702 		return "agentx-CommitSet-PDU";
703 	case AGENTX_PDU_TYPE_UNDOSET:
704 		return "agentx-UndoSet-PDU";
705 	case AGENTX_PDU_TYPE_CLEANUPSET:
706 		return "agentx-CleanupSet-PDU";
707 	case AGENTX_PDU_TYPE_NOTIFY:
708 		return "agentx-Notify-PDU";
709 	case AGENTX_PDU_TYPE_PING:
710 		return "agentx-Ping-PDU";
711 	case AGENTX_PDU_TYPE_INDEXALLOCATE:
712 		return "agentx-IndexAllocate-PDU";
713 	case AGENTX_PDU_TYPE_INDEXDEALLOCATE:
714 		return "agentx-IndexDeallocate-PDU";
715 	case AGENTX_PDU_TYPE_ADDAGENTCAPS:
716 		return "agentx-AddAgentCaps-PDU";
717 	case AGENTX_PDU_TYPE_REMOVEAGENTCAPS:
718 		return "agentx-RemoveAgentCaps-PDU";
719 	case AGENTX_PDU_TYPE_RESPONSE:
720 		return "agentx-Response-PDU";
721 	}
722 	snprintf(buffer, sizeof(buffer), "Unknown type: %d", type);
723 	return buffer;
724 }
725 
726 const char *
727 agentx_closereason2string(enum agentx_close_reason reason)
728 {
729 	static char buffer[64];
730 
731 	switch (reason) {
732 	case AGENTX_CLOSE_OTHER:
733 		return "Undefined reason";
734 	case AGENTX_CLOSEN_PARSEERROR:
735 		return "Too many AgentX parse errors from peer";
736 	case AGENTX_CLOSE_PROTOCOLERROR:
737 		return "Too many AgentX protocol errors from peer";
738 	case AGENTX_CLOSE_TIMEOUTS:
739 		return "Too many timeouts waiting for peer";
740 	case AGENTX_CLOSE_SHUTDOWN:
741 		return "shutting down";
742 	case AGENTX_CLOSE_BYMANAGER:
743 		return "Manager shuts down";
744 	}
745 	snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason);
746 	return buffer;
747 }
748 
749 const char *
750 agentx_oid2string(struct agentx_oid *oid)
751 {
752 	return agentx_oidrange2string(oid, 0, 0);
753 }
754 
755 const char *
756 agentx_oidrange2string(struct agentx_oid *oid, uint8_t range_subid,
757     uint32_t upperbound)
758 {
759 	static char buf[1024];
760 	char *p;
761 	size_t i, rest;
762 	int ret;
763 
764 	rest = sizeof(buf);
765 	p = buf;
766 	for (i = 0; i < oid->aoi_idlen; i++) {
767 		if (range_subid != 0 && range_subid - 1 == (uint8_t)i)
768 			ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i],
769 			    upperbound);
770 		else
771 			ret = snprintf(p, rest, ".%u", oid->aoi_id[i]);
772 		if ((size_t) ret >= rest) {
773 			snprintf(buf, sizeof(buf), "Couldn't parse oid");
774 			return buf;
775 		}
776 		p += ret;
777 		rest -= (size_t) ret;
778 	}
779 	return buf;
780 }
781 
782 const char *
783 agentx_varbind2string(struct agentx_varbind *vb)
784 {
785 	static char buf[1024];
786 	char tmpbuf[1024];
787 	size_t i, bufleft;
788 	int ishex = 0;
789 	char *p;
790 	int ret;
791 
792 	switch (vb->avb_type) {
793 	case AGENTX_DATA_TYPE_INTEGER:
794 		snprintf(buf, sizeof(buf), "%s: (int)%u",
795 		    agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
796 		break;
797 	case AGENTX_DATA_TYPE_OCTETSTRING:
798 		for (i = 0;
799 		    i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) {
800 			if (!isprint(vb->avb_data.avb_ostring.aos_string[i]))
801 				ishex = 1;
802 		}
803 		if (ishex) {
804 			p = tmpbuf;
805 			bufleft = sizeof(tmpbuf);
806 			for (i = 0;
807 			    i < vb->avb_data.avb_ostring.aos_slen; i++) {
808 				ret = snprintf(p, bufleft, " %02hhX",
809 				    vb->avb_data.avb_ostring.aos_string[i]);
810 				if (ret >= (int) bufleft) {
811 					p = strrchr(p, ' ');
812 					strlcpy(p, "...", 4);
813 					break;
814 				}
815 				p += 3;
816 				bufleft -= 3;
817 			}
818 			ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s",
819 			    agentx_oid2string(&(vb->avb_oid)), tmpbuf);
820 			if (ret >= (int) sizeof(buf)) {
821 				p  = strrchr(buf, ' ');
822 				strlcpy(p, "...", 4);
823 			}
824 		} else {
825 			ret = snprintf(buf, sizeof(buf), "%s: (string)",
826 			    agentx_oid2string(&(vb->avb_oid)));
827 			if (ret >= (int) sizeof(buf)) {
828 				snprintf(buf, sizeof(buf), "<too large OID>: "
829 				    "(string)<too large string>");
830 				break;
831 			}
832 			p = buf + ret;
833 			bufleft = (int) sizeof(buf) - ret;
834 			if (snprintf(p, bufleft, "%.*s",
835 			    vb->avb_data.avb_ostring.aos_slen,
836 			    vb->avb_data.avb_ostring.aos_string) >=
837 			    (int) bufleft) {
838 				p = buf + sizeof(buf) - 4;
839 				strlcpy(p, "...", 4);
840 			}
841 		}
842 		break;
843 	case AGENTX_DATA_TYPE_NULL:
844 		snprintf(buf, sizeof(buf), "%s: <null>",
845 		    agentx_oid2string(&(vb->avb_oid)));
846 		break;
847 	case AGENTX_DATA_TYPE_OID:
848 		strlcpy(tmpbuf,
849 		    agentx_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf));
850 		snprintf(buf, sizeof(buf), "%s: (oid)%s",
851 		    agentx_oid2string(&(vb->avb_oid)), tmpbuf);
852 		break;
853 	case AGENTX_DATA_TYPE_IPADDRESS:
854 		if (vb->avb_data.avb_ostring.aos_slen != 4) {
855 			snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>",
856 			    agentx_oid2string(&(vb->avb_oid)));
857 			break;
858 		}
859 		if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string,
860 		    tmpbuf, sizeof(tmpbuf)) == NULL) {
861 			snprintf(buf, sizeof(buf), "%s: (ipaddress)"
862 			    "<unparseable>: %s",
863 			    agentx_oid2string(&(vb->avb_oid)),
864 			    strerror(errno));
865 			break;
866 		}
867 		snprintf(buf, sizeof(buf), "%s: (ipaddress)%s",
868 		    agentx_oid2string(&(vb->avb_oid)), tmpbuf);
869 		break;
870 	case AGENTX_DATA_TYPE_COUNTER32:
871 		snprintf(buf, sizeof(buf), "%s: (counter32)%u",
872 		    agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
873 		break;
874 	case AGENTX_DATA_TYPE_GAUGE32:
875 		snprintf(buf, sizeof(buf), "%s: (gauge32)%u",
876 		    agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
877 		break;
878 	case AGENTX_DATA_TYPE_TIMETICKS:
879 		snprintf(buf, sizeof(buf), "%s: (timeticks)%u",
880 		    agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
881 		break;
882 	case AGENTX_DATA_TYPE_OPAQUE:
883 		p = tmpbuf;
884 		bufleft = sizeof(tmpbuf);
885 		for (i = 0;
886 		    i < vb->avb_data.avb_ostring.aos_slen; i++) {
887 			ret = snprintf(p, bufleft, " %02hhX",
888 			    vb->avb_data.avb_ostring.aos_string[i]);
889 			if (ret >= (int) bufleft) {
890 				p = strrchr(p, ' ');
891 				strlcpy(p, "...", 4);
892 				break;
893 			}
894 			p += 3;
895 			bufleft -= 3;
896 		}
897 		ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s",
898 		    agentx_oid2string(&(vb->avb_oid)), tmpbuf);
899 		if (ret >= (int) sizeof(buf)) {
900 			p  = strrchr(buf, ' ');
901 			strlcpy(p, "...", 4);
902 		}
903 		break;
904 	case AGENTX_DATA_TYPE_COUNTER64:
905 		snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64,
906 		    agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64);
907 		break;
908 	case AGENTX_DATA_TYPE_NOSUCHOBJECT:
909 		snprintf(buf, sizeof(buf), "%s: <noSuchObject>",
910 		    agentx_oid2string(&(vb->avb_oid)));
911 		break;
912 	case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
913 		snprintf(buf, sizeof(buf), "%s: <noSuchInstance>",
914 		    agentx_oid2string(&(vb->avb_oid)));
915 		break;
916 	case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
917 		snprintf(buf, sizeof(buf), "%s: <endOfMibView>",
918 		    agentx_oid2string(&(vb->avb_oid)));
919 		break;
920 	}
921 	return buf;
922 }
923 
924 int
925 agentx_oid_cmp(struct agentx_oid *o1, struct agentx_oid *o2)
926 {
927 	size_t i, min;
928 
929 	min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen;
930 	for (i = 0; i < min; i++) {
931 		if (o1->aoi_id[i] < o2->aoi_id[i])
932 			return -1;
933 		if (o1->aoi_id[i] > o2->aoi_id[i])
934 			return 1;
935 	}
936 	/* o1 is parent of o2 */
937 	if (o1->aoi_idlen < o2->aoi_idlen)
938 		return -2;
939 	/* o1 is child of o2 */
940 	if (o1->aoi_idlen > o2->aoi_idlen)
941 		return 2;
942 	return 0;
943 }
944 
945 int
946 agentx_oid_add(struct agentx_oid *oid, uint32_t value)
947 {
948 	if (oid->aoi_idlen == AGENTX_OID_MAX_LEN)
949 		return -1;
950 	oid->aoi_id[oid->aoi_idlen++] = value;
951 	return 0;
952 }
953 
954 static uint32_t
955 agentx_pdu_queue(struct agentx *ax)
956 {
957 	struct agentx_pdu_header header;
958 	uint32_t packetid, plength;
959 	size_t wbtlen = ax->ax_wbtlen;
960 
961 	header.aph_flags = ax->ax_byteorder == AGENTX_BYTE_ORDER_BE ?
962 	    AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER : 0;
963 	packetid = agentx_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12]));
964 	plength = (ax->ax_wbtlen - ax->ax_wblen) - AGENTX_PDU_HEADER;
965 	ax->ax_wbtlen = ax->ax_wblen + 16;
966 	(void)agentx_pdu_add_uint32(ax, plength);
967 
968 	ax->ax_wblen = ax->ax_wbtlen = wbtlen;
969 
970 	return packetid;
971 }
972 
973 static int
974 agentx_pdu_header(struct agentx *ax, enum agentx_pdu_type type, uint8_t flags,
975     uint32_t sessionid, uint32_t transactionid, uint32_t packetid,
976     struct agentx_ostring *context)
977 {
978 	if (ax->ax_wblen != ax->ax_wbtlen) {
979 		errno = EALREADY;
980 		return -1;
981 	}
982 
983 	ax->ax_wbtlen = ax->ax_wblen;
984 	if (agentx_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 |= AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT;
990 	if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE)
991 		flags |= AGENTX_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 = agentx_packetid(ax);
996 	if (agentx_pdu_add_uint32(ax, sessionid) == -1 ||
997 	    agentx_pdu_add_uint32(ax, transactionid) == -1 ||
998 	    agentx_pdu_add_uint32(ax, packetid) == -1 ||
999 	    agentx_pdu_need(ax, 4) == -1)
1000 		return -1;
1001 	ax->ax_wbtlen += 4;
1002 	if (context != NULL) {
1003 		if (agentx_pdu_add_str(ax, context) == -1)
1004 			return -1;
1005 	}
1006 
1007 	return 0;
1008 }
1009 
1010 static uint32_t
1011 agentx_packetid(struct agentx *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 agentx_pdu_add_uint16(struct agentx *ax, uint16_t value)
1046 {
1047 	if (agentx_pdu_need(ax, sizeof(value)) == -1)
1048 		return -1;
1049 
1050 	if (ax->ax_byteorder == AGENTX_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 agentx_pdu_add_uint32(struct agentx *ax, uint32_t value)
1061 {
1062 	if (agentx_pdu_need(ax, sizeof(value)) == -1)
1063 		return -1;
1064 
1065 	if (ax->ax_byteorder == AGENTX_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 agentx_pdu_add_uint64(struct agentx *ax, uint64_t value)
1076 {
1077 	if (agentx_pdu_need(ax, sizeof(value)) == -1)
1078 		return -1;
1079 
1080 	if (ax->ax_byteorder == AGENTX_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 agentx_pdu_add_oid(struct agentx *ax, struct agentx_oid *oid, int include)
1092 {
1093 	static struct agentx_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 (agentx_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 (agentx_pdu_add_uint32(ax, oid->aoi_id[i]) == -1)
1118 			return -1;
1119 	}
1120 
1121 	return 0;
1122 }
1123 
1124 static int
1125 agentx_pdu_add_str(struct agentx *ax, struct agentx_ostring *str)
1126 {
1127 	size_t length, zeroes;
1128 
1129 	if (agentx_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 (agentx_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 agentx_pdu_add_varbindlist(struct agentx *ax,
1147     struct agentx_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 (agentx_pdu_add_uint16(ax, temp) == -1 ||
1155 		    agentx_pdu_need(ax, 2))
1156 			return -1;
1157 		memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2);
1158 		ax->ax_wbtlen += 2;
1159 		if (agentx_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1)
1160 			return -1;
1161 		switch (vblist[i].avb_type) {
1162 		case AGENTX_DATA_TYPE_INTEGER:
1163 		case AGENTX_DATA_TYPE_COUNTER32:
1164 		case AGENTX_DATA_TYPE_GAUGE32:
1165 		case AGENTX_DATA_TYPE_TIMETICKS:
1166 			if (agentx_pdu_add_uint32(ax,
1167 			    vblist[i].avb_data.avb_uint32) == -1)
1168 				return -1;
1169 			break;
1170 		case AGENTX_DATA_TYPE_COUNTER64:
1171 			if (agentx_pdu_add_uint64(ax,
1172 			    vblist[i].avb_data.avb_uint64) == -1)
1173 				return -1;
1174 			break;
1175 		case AGENTX_DATA_TYPE_OCTETSTRING:
1176 		case AGENTX_DATA_TYPE_IPADDRESS:
1177 		case AGENTX_DATA_TYPE_OPAQUE:
1178 			if (agentx_pdu_add_str(ax,
1179 			    &(vblist[i].avb_data.avb_ostring)) == -1)
1180 				return -1;
1181 			break;
1182 		case AGENTX_DATA_TYPE_OID:
1183 			if (agentx_pdu_add_oid(ax,
1184 			    &(vblist[i].avb_data.avb_oid), 1) == -1)
1185 				return -1;
1186 			break;
1187 		case AGENTX_DATA_TYPE_NULL:
1188 		case AGENTX_DATA_TYPE_NOSUCHOBJECT:
1189 		case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
1190 		case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
1191 			break;
1192 		default:
1193 			errno = EINVAL;
1194 			return -1;
1195 		}
1196 	}
1197 	return 0;
1198 }
1199 
1200 static uint16_t
1201 agentx_pdutoh16(struct agentx_pdu_header *header, uint8_t *buf)
1202 {
1203 	uint16_t value;
1204 
1205 	memcpy(&value, buf, sizeof(value));
1206 	if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
1207 		return be16toh(value);
1208 	return le16toh(value);
1209 }
1210 
1211 static uint32_t
1212 agentx_pdutoh32(struct agentx_pdu_header *header, uint8_t *buf)
1213 {
1214 	uint32_t value;
1215 
1216 	memcpy(&value, buf, sizeof(value));
1217 	if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
1218 		return be32toh(value);
1219 	return le32toh(value);
1220 }
1221 
1222 static uint64_t
1223 agentx_pdutoh64(struct agentx_pdu_header *header, uint8_t *buf)
1224 {
1225 	uint64_t value;
1226 
1227 	memcpy(&value, buf, sizeof(value));
1228 	if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
1229 		return be64toh(value);
1230 	return le64toh(value);
1231 }
1232 
1233 static ssize_t
1234 agentx_pdutooid(struct agentx_pdu_header *header, struct agentx_oid *oid,
1235     uint8_t *buf, size_t rawlen)
1236 {
1237 	size_t i = 0;
1238 	ssize_t nread;
1239 
1240 	if (rawlen < 4)
1241 		goto fail;
1242 	rawlen -= 4;
1243 	nread = 4;
1244 	oid->aoi_idlen = *buf++;
1245 	if (rawlen < (oid->aoi_idlen * 4))
1246 		goto fail;
1247 	nread += oid->aoi_idlen * 4;
1248 	if (*buf != 0) {
1249 		oid->aoi_id[0] = 1;
1250 		oid->aoi_id[1] = 3;
1251 		oid->aoi_id[2] = 6;
1252 		oid->aoi_id[3] = 1;
1253 		oid->aoi_id[4] = *buf;
1254 		oid->aoi_idlen += 5;
1255 		i = 5;
1256 	}
1257 	buf++;
1258 	oid->aoi_include = *buf;
1259 	for (buf += 2; i < oid->aoi_idlen; i++, buf += 4)
1260 		oid->aoi_id[i] = agentx_pdutoh32(header, buf);
1261 
1262 	return nread;
1263 
1264 fail:
1265 	errno = EPROTO;
1266 	return -1;
1267 }
1268 
1269 static ssize_t
1270 agentx_pdutoostring(struct agentx_pdu_header *header,
1271     struct agentx_ostring *ostring, uint8_t *buf, size_t rawlen)
1272 {
1273 	ssize_t nread;
1274 
1275 	if (rawlen < 4)
1276 		goto fail;
1277 
1278 	ostring->aos_slen = agentx_pdutoh32(header, buf);
1279 	rawlen -= 4;
1280 	buf += 4;
1281 	if (ostring->aos_slen > rawlen)
1282 		goto fail;
1283 	if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL)
1284 		return -1;
1285 	memcpy(ostring->aos_string, buf, ostring->aos_slen);
1286 	ostring->aos_string[ostring->aos_slen] = '\0';
1287 
1288 	nread = 4 + ostring->aos_slen;
1289 	if (ostring->aos_slen % 4 != 0)
1290 		nread += 4 - (ostring->aos_slen % 4);
1291 
1292 	return nread;
1293 
1294 fail:
1295 	errno = EPROTO;
1296 	return -1;
1297 }
1298 
1299 static ssize_t
1300 agentx_pdutovarbind(struct agentx_pdu_header *header,
1301     struct agentx_varbind *varbind, uint8_t *buf, size_t rawlen)
1302 {
1303 	ssize_t nread, rread = 4;
1304 
1305 	if (rawlen == 0)
1306 		return 0;
1307 
1308 	if (rawlen < 8)
1309 		goto fail;
1310 	varbind->avb_type = agentx_pdutoh16(header, buf);
1311 
1312 	buf += 4;
1313 	rawlen -= 4;
1314 	nread = agentx_pdutooid(header, &(varbind->avb_oid), buf, rawlen);
1315 	if (nread == -1)
1316 		return -1;
1317 	rread += nread;
1318 	buf += nread;
1319 	rawlen -= nread;
1320 
1321 	switch(varbind->avb_type) {
1322 	case AGENTX_DATA_TYPE_INTEGER:
1323 	case AGENTX_DATA_TYPE_COUNTER32:
1324 	case AGENTX_DATA_TYPE_GAUGE32:
1325 	case AGENTX_DATA_TYPE_TIMETICKS:
1326 		if (rawlen < 4)
1327 			goto fail;
1328 		varbind->avb_data.avb_uint32 = agentx_pdutoh32(header, buf);
1329 		return rread + 4;
1330 	case AGENTX_DATA_TYPE_COUNTER64:
1331 		if (rawlen < 8)
1332 			goto fail;
1333 		varbind->avb_data.avb_uint64 = agentx_pdutoh64(header, buf);
1334 		return rread + 8;
1335 	case AGENTX_DATA_TYPE_OCTETSTRING:
1336 	case AGENTX_DATA_TYPE_IPADDRESS:
1337 	case AGENTX_DATA_TYPE_OPAQUE:
1338 		nread = agentx_pdutoostring(header,
1339 		    &(varbind->avb_data.avb_ostring), buf, rawlen);
1340 		if (nread == -1)
1341 			return -1;
1342 		return nread + rread;
1343 	case AGENTX_DATA_TYPE_OID:
1344 		nread = agentx_pdutooid(header, &(varbind->avb_data.avb_oid),
1345 		    buf, rawlen);
1346 		if (nread == -1)
1347 			return -1;
1348 		return nread + rread;
1349 	case AGENTX_DATA_TYPE_NULL:
1350 	case AGENTX_DATA_TYPE_NOSUCHOBJECT:
1351 	case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
1352 	case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
1353 		return rread;
1354 	}
1355 
1356 fail:
1357 	errno = EPROTO;
1358 	return -1;
1359 }
1360