xref: /openbsd-src/lib/libagentx/ax.c (revision 6f5d93645a6e9b04bccbd48ae2196a66638a49ca)
1 /*	$OpenBSD: ax.c,v 1.10 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 *, 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 *
ax_new(int fd)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
ax_free(struct ax * ax)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 *
ax_recv(struct ax * ax)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
ax_pdu_need(struct ax * ax,size_t need)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
ax_send(struct ax * ax)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
ax_open(struct ax * ax,uint8_t timeout,struct ax_oid * oid,struct ax_ostring * descr)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
ax_close(struct ax * ax,uint32_t sessionid,enum ax_close_reason reason)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
ax_indexallocate(struct ax * ax,uint8_t flags,uint32_t sessionid,struct ax_ostring * context,struct ax_varbind * vblist,size_t nvb)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
ax_indexdeallocate(struct ax * ax,uint32_t sessionid,struct ax_ostring * context,struct ax_varbind * vblist,size_t nvb)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
ax_addagentcaps(struct ax * ax,uint32_t sessionid,struct ax_ostring * context,struct ax_oid * id,struct ax_ostring * descr)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
ax_removeagentcaps(struct ax * ax,uint32_t sessionid,struct ax_ostring * context,struct ax_oid * id)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
ax_register(struct ax * ax,uint8_t flags,uint32_t sessionid,struct ax_ostring * context,uint8_t timeout,uint8_t priority,uint8_t range_subid,struct ax_oid * subtree,uint32_t upperbound)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
ax_unregister(struct ax * ax,uint32_t sessionid,struct ax_ostring * context,uint8_t priority,uint8_t range_subid,struct ax_oid * subtree,uint32_t upperbound)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
ax_response(struct ax * ax,uint32_t sessionid,uint32_t transactionid,uint32_t packetid,uint32_t sysuptime,uint16_t error,uint16_t index,struct ax_varbind * vblist,size_t nvb)555 ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
556     uint32_t packetid, uint32_t sysuptime, uint16_t error, uint16_t index,
557     struct ax_varbind *vblist, size_t nvb)
558 {
559 	if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid,
560 	    transactionid, packetid, NULL) == -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
ax_pdu_free(struct ax_pdu * pdu)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
ax_varbind_free(struct ax_varbind * varbind)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 *
ax_error2string(enum ax_pdu_error error)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 *
ax_pdutype2string(enum ax_pdu_type type)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 *
ax_closereason2string(enum ax_close_reason reason)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 *
ax_oid2string(struct ax_oid * oid)751 ax_oid2string(struct ax_oid *oid)
752 {
753 	return ax_oidrange2string(oid, 0, 0);
754 }
755 
756 const char *
ax_oidrange2string(struct ax_oid * oid,uint8_t range_subid,uint32_t upperbound)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 	if (oid->aoi_idlen == 0)
768 		(void)strlcpy(buf, "null", sizeof(buf));
769 	for (i = 0; i < oid->aoi_idlen; i++) {
770 		if (range_subid != 0 && range_subid - 1 == (uint8_t)i)
771 			ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i],
772 			    upperbound);
773 		else
774 			ret = snprintf(p, rest, ".%u", oid->aoi_id[i]);
775 		if ((size_t) ret >= rest) {
776 			snprintf(buf, sizeof(buf), "Couldn't parse oid");
777 			return buf;
778 		}
779 		p += ret;
780 		rest -= (size_t) ret;
781 	}
782 	return buf;
783 }
784 
785 const char *
ax_varbind2string(struct ax_varbind * vb)786 ax_varbind2string(struct ax_varbind *vb)
787 {
788 	static char buf[1024];
789 	char tmpbuf[1024];
790 	size_t i, bufleft;
791 	int ishex = 0;
792 	char *p;
793 	int ret;
794 
795 	switch (vb->avb_type) {
796 	case AX_DATA_TYPE_INTEGER:
797 		snprintf(buf, sizeof(buf), "%s: (int)%d",
798 		    ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_int32);
799 		break;
800 	case AX_DATA_TYPE_OCTETSTRING:
801 		for (i = 0;
802 		    i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) {
803 			if (!isprint(vb->avb_data.avb_ostring.aos_string[i]))
804 				ishex = 1;
805 		}
806 		if (ishex) {
807 			p = tmpbuf;
808 			bufleft = sizeof(tmpbuf);
809 			for (i = 0;
810 			    i < vb->avb_data.avb_ostring.aos_slen; i++) {
811 				ret = snprintf(p, bufleft, " %02hhX",
812 				    vb->avb_data.avb_ostring.aos_string[i]);
813 				if (ret >= (int) bufleft) {
814 					p = strrchr(p, ' ');
815 					strlcpy(p, "...", 4);
816 					break;
817 				}
818 				p += 3;
819 				bufleft -= 3;
820 			}
821 			ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s",
822 			    ax_oid2string(&(vb->avb_oid)), tmpbuf);
823 			if (ret >= (int) sizeof(buf)) {
824 				p  = strrchr(buf, ' ');
825 				strlcpy(p, "...", 4);
826 			}
827 		} else {
828 			ret = snprintf(buf, sizeof(buf), "%s: (string)",
829 			    ax_oid2string(&(vb->avb_oid)));
830 			if (ret >= (int) sizeof(buf)) {
831 				snprintf(buf, sizeof(buf), "<too large OID>: "
832 				    "(string)<too large string>");
833 				break;
834 			}
835 			p = buf + ret;
836 			bufleft = (int) sizeof(buf) - ret;
837 			if (snprintf(p, bufleft, "%.*s",
838 			    vb->avb_data.avb_ostring.aos_slen,
839 			    vb->avb_data.avb_ostring.aos_string) >=
840 			    (int) bufleft) {
841 				p = buf + sizeof(buf) - 4;
842 				strlcpy(p, "...", 4);
843 			}
844 		}
845 		break;
846 	case AX_DATA_TYPE_NULL:
847 		snprintf(buf, sizeof(buf), "%s: <null>",
848 		    ax_oid2string(&(vb->avb_oid)));
849 		break;
850 	case AX_DATA_TYPE_OID:
851 		strlcpy(tmpbuf,
852 		    ax_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf));
853 		snprintf(buf, sizeof(buf), "%s: (oid)%s",
854 		    ax_oid2string(&(vb->avb_oid)), tmpbuf);
855 		break;
856 	case AX_DATA_TYPE_IPADDRESS:
857 		if (vb->avb_data.avb_ostring.aos_slen != 4) {
858 			snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>",
859 			    ax_oid2string(&(vb->avb_oid)));
860 			break;
861 		}
862 		if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string,
863 		    tmpbuf, sizeof(tmpbuf)) == NULL) {
864 			snprintf(buf, sizeof(buf), "%s: (ipaddress)"
865 			    "<unparseable>: %s",
866 			    ax_oid2string(&(vb->avb_oid)),
867 			    strerror(errno));
868 			break;
869 		}
870 		snprintf(buf, sizeof(buf), "%s: (ipaddress)%s",
871 		    ax_oid2string(&(vb->avb_oid)), tmpbuf);
872 		break;
873 	case AX_DATA_TYPE_COUNTER32:
874 		snprintf(buf, sizeof(buf), "%s: (counter32)%u",
875 		    ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
876 		break;
877 	case AX_DATA_TYPE_GAUGE32:
878 		snprintf(buf, sizeof(buf), "%s: (gauge32)%u",
879 		    ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
880 		break;
881 	case AX_DATA_TYPE_TIMETICKS:
882 		snprintf(buf, sizeof(buf), "%s: (timeticks)%u",
883 		    ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
884 		break;
885 	case AX_DATA_TYPE_OPAQUE:
886 		p = tmpbuf;
887 		bufleft = sizeof(tmpbuf);
888 		for (i = 0;
889 		    i < vb->avb_data.avb_ostring.aos_slen; i++) {
890 			ret = snprintf(p, bufleft, " %02hhX",
891 			    vb->avb_data.avb_ostring.aos_string[i]);
892 			if (ret >= (int) bufleft) {
893 				p = strrchr(p, ' ');
894 				strlcpy(p, "...", 4);
895 				break;
896 			}
897 			p += 3;
898 			bufleft -= 3;
899 		}
900 		ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s",
901 		    ax_oid2string(&(vb->avb_oid)), tmpbuf);
902 		if (ret >= (int) sizeof(buf)) {
903 			p  = strrchr(buf, ' ');
904 			strlcpy(p, "...", 4);
905 		}
906 		break;
907 	case AX_DATA_TYPE_COUNTER64:
908 		snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64,
909 		    ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64);
910 		break;
911 	case AX_DATA_TYPE_NOSUCHOBJECT:
912 		snprintf(buf, sizeof(buf), "%s: <noSuchObject>",
913 		    ax_oid2string(&(vb->avb_oid)));
914 		break;
915 	case AX_DATA_TYPE_NOSUCHINSTANCE:
916 		snprintf(buf, sizeof(buf), "%s: <noSuchInstance>",
917 		    ax_oid2string(&(vb->avb_oid)));
918 		break;
919 	case AX_DATA_TYPE_ENDOFMIBVIEW:
920 		snprintf(buf, sizeof(buf), "%s: <endOfMibView>",
921 		    ax_oid2string(&(vb->avb_oid)));
922 		break;
923 	}
924 	return buf;
925 }
926 
927 int
ax_oid_cmp(struct ax_oid * o1,struct ax_oid * o2)928 ax_oid_cmp(struct ax_oid *o1, struct ax_oid *o2)
929 {
930 	size_t i, min;
931 
932 	min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen;
933 	for (i = 0; i < min; i++) {
934 		if (o1->aoi_id[i] < o2->aoi_id[i])
935 			return -1;
936 		if (o1->aoi_id[i] > o2->aoi_id[i])
937 			return 1;
938 	}
939 	/* o1 is parent of o2 */
940 	if (o1->aoi_idlen < o2->aoi_idlen)
941 		return -2;
942 	/* o1 is child of o2 */
943 	if (o1->aoi_idlen > o2->aoi_idlen)
944 		return 2;
945 	return 0;
946 }
947 
948 int
ax_oid_add(struct ax_oid * oid,uint32_t value)949 ax_oid_add(struct ax_oid *oid, uint32_t value)
950 {
951 	if (oid->aoi_idlen == AX_OID_MAX_LEN)
952 		return -1;
953 	oid->aoi_id[oid->aoi_idlen++] = value;
954 	return 0;
955 }
956 
957 static uint32_t
ax_pdu_queue(struct ax * ax)958 ax_pdu_queue(struct ax *ax)
959 {
960 	struct ax_pdu_header header;
961 	uint32_t packetid, plength;
962 	size_t wbtlen = ax->ax_wbtlen;
963 
964 	header.aph_flags = ax->ax_byteorder == AX_BYTE_ORDER_BE ?
965 	    AX_PDU_FLAG_NETWORK_BYTE_ORDER : 0;
966 	packetid = ax_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12]));
967 	plength = (ax->ax_wbtlen - ax->ax_wblen) - AX_PDU_HEADER;
968 	ax->ax_wbtlen = ax->ax_wblen + 16;
969 	(void)ax_pdu_add_uint32(ax, plength);
970 
971 	ax->ax_wblen = ax->ax_wbtlen = wbtlen;
972 
973 	return packetid;
974 }
975 
976 static int
ax_pdu_header(struct ax * ax,enum ax_pdu_type type,uint8_t flags,uint32_t sessionid,uint32_t transactionid,uint32_t packetid,struct ax_ostring * context)977 ax_pdu_header(struct ax *ax, enum ax_pdu_type type, uint8_t flags,
978     uint32_t sessionid, uint32_t transactionid, uint32_t packetid,
979     struct ax_ostring *context)
980 {
981 	if (ax->ax_wblen != ax->ax_wbtlen) {
982 		errno = EALREADY;
983 		return -1;
984 	}
985 
986 	if (ax_pdu_need(ax, 4) == -1)
987 		return -1;
988 	ax->ax_wbuf[ax->ax_wbtlen++] = 1;
989 	ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type;
990 	if (context != NULL)
991 		flags |= AX_PDU_FLAG_NON_DEFAULT_CONTEXT;
992 	if (ax->ax_byteorder == AX_BYTE_ORDER_BE)
993 		flags |= AX_PDU_FLAG_NETWORK_BYTE_ORDER;
994 	ax->ax_wbuf[ax->ax_wbtlen++] = flags;
995 	ax->ax_wbuf[ax->ax_wbtlen++] = 0;
996 	if (packetid == 0)
997 		packetid = ax_packetid(ax);
998 	if (ax_pdu_add_uint32(ax, sessionid) == -1 ||
999 	    ax_pdu_add_uint32(ax, transactionid) == -1 ||
1000 	    ax_pdu_add_uint32(ax, packetid) == -1 ||
1001 	    ax_pdu_need(ax, 4) == -1)
1002 		return -1;
1003 	ax->ax_wbtlen += 4;
1004 	if (context != NULL) {
1005 		if (ax_pdu_add_str(ax, context) == -1)
1006 			return -1;
1007 	}
1008 
1009 	return 0;
1010 }
1011 
1012 static uint32_t
ax_packetid(struct ax * ax)1013 ax_packetid(struct ax *ax)
1014 {
1015 	uint32_t packetid, *packetids;
1016 	size_t npackets = 0, i;
1017 	int found;
1018 
1019 	if (ax->ax_packetids != NULL) {
1020 		for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++)
1021 			continue;
1022 	}
1023 	if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) {
1024 		packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize,
1025 		    ax->ax_packetidsize + 25, sizeof(*packetids));
1026 		if (packetids == NULL)
1027 			return 0;
1028 		ax->ax_packetidsize += 25;
1029 		ax->ax_packetids = packetids;
1030 	}
1031 	do {
1032 		found = 0;
1033 		packetid = arc4random();
1034 		for (i = 0; ax->ax_packetids[i] != 0; i++) {
1035 			if (ax->ax_packetids[i] == packetid) {
1036 				found = 1;
1037 				break;
1038 			}
1039 		}
1040 	} while (packetid == 0 || found);
1041 	ax->ax_packetids[npackets] = packetid;
1042 
1043 	return packetid;
1044 }
1045 
1046 static int
ax_pdu_add_uint16(struct ax * ax,uint16_t value)1047 ax_pdu_add_uint16(struct ax *ax, uint16_t value)
1048 {
1049 	if (ax_pdu_need(ax, sizeof(value)) == -1)
1050 		return -1;
1051 
1052 	if (ax->ax_byteorder == AX_BYTE_ORDER_BE)
1053 		value = htobe16(value);
1054 	else
1055 		value = htole16(value);
1056 	memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
1057 	ax->ax_wbtlen += sizeof(value);
1058 	return 0;
1059 }
1060 
1061 static int
ax_pdu_add_uint32(struct ax * ax,uint32_t value)1062 ax_pdu_add_uint32(struct ax *ax, uint32_t value)
1063 {
1064 	if (ax_pdu_need(ax, sizeof(value)) == -1)
1065 		return -1;
1066 
1067 	if (ax->ax_byteorder == AX_BYTE_ORDER_BE)
1068 		value = htobe32(value);
1069 	else
1070 		value = htole32(value);
1071 	memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
1072 	ax->ax_wbtlen += sizeof(value);
1073 	return 0;
1074 }
1075 
1076 static int
ax_pdu_add_uint64(struct ax * ax,uint64_t value)1077 ax_pdu_add_uint64(struct ax *ax, uint64_t value)
1078 {
1079 	if (ax_pdu_need(ax, sizeof(value)) == -1)
1080 		return -1;
1081 
1082 	if (ax->ax_byteorder == AX_BYTE_ORDER_BE)
1083 		value = htobe64(value);
1084 	else
1085 		value = htole64(value);
1086 	memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
1087 	ax->ax_wbtlen += sizeof(value);
1088 	return 0;
1089 }
1090 
1091 
1092 static int
ax_pdu_add_oid(struct ax * ax,struct ax_oid * oid,int include)1093 ax_pdu_add_oid(struct ax *ax, struct ax_oid *oid, int include)
1094 {
1095 	static struct ax_oid nulloid = {0};
1096 	uint8_t prefix = 0, n_subid, i = 0;
1097 
1098 	n_subid = oid->aoi_idlen;
1099 
1100 	if (oid == NULL)
1101 		oid = &nulloid;
1102 
1103 	if (oid->aoi_idlen > 4 &&
1104 	    oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 &&
1105 	    oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 &&
1106 	    oid->aoi_id[4] <= UINT8_MAX) {
1107 		prefix = oid->aoi_id[4];
1108 		i = 5;
1109 	}
1110 
1111 	if (ax_pdu_need(ax, 4) == -1)
1112 		return -1;
1113 	ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i;
1114 	ax->ax_wbuf[ax->ax_wbtlen++] = prefix;
1115 	ax->ax_wbuf[ax->ax_wbtlen++] = !!include;
1116 	ax->ax_wbuf[ax->ax_wbtlen++] = 0;
1117 
1118 	for (; i < n_subid; i++) {
1119 		if (ax_pdu_add_uint32(ax, oid->aoi_id[i]) == -1)
1120 			return -1;
1121 	}
1122 
1123 	return 0;
1124 }
1125 
1126 static int
ax_pdu_add_str(struct ax * ax,struct ax_ostring * str)1127 ax_pdu_add_str(struct ax *ax, struct ax_ostring *str)
1128 {
1129 	size_t length, zeroes;
1130 
1131 	if (ax_pdu_add_uint32(ax, str->aos_slen) == -1)
1132 		return -1;
1133 
1134 	if ((zeroes = (4 - (str->aos_slen % 4))) == 4)
1135 		zeroes = 0;
1136 	length = str->aos_slen + zeroes;
1137 	if (ax_pdu_need(ax, length) == -1)
1138 		return -1;
1139 
1140 	memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen);
1141 	ax->ax_wbtlen += str->aos_slen;
1142 	memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes);
1143 	ax->ax_wbtlen += zeroes;
1144 	return 0;
1145 }
1146 
1147 static int
ax_pdu_add_varbindlist(struct ax * ax,struct ax_varbind * vblist,size_t nvb)1148 ax_pdu_add_varbindlist(struct ax *ax,
1149     struct ax_varbind *vblist, size_t nvb)
1150 {
1151 	size_t i;
1152 	uint16_t temp;
1153 
1154 	for (i = 0; i < nvb; i++) {
1155 		temp = (uint16_t) vblist[i].avb_type;
1156 		if (ax_pdu_add_uint16(ax, temp) == -1 ||
1157 		    ax_pdu_need(ax, 2))
1158 			return -1;
1159 		memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2);
1160 		ax->ax_wbtlen += 2;
1161 		if (ax_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1)
1162 			return -1;
1163 		switch (vblist[i].avb_type) {
1164 		case AX_DATA_TYPE_INTEGER:
1165 			if (ax_pdu_add_uint32(ax,
1166 			    vblist[i].avb_data.avb_int32) == -1)
1167 				return -1;
1168 			break;
1169 		case AX_DATA_TYPE_COUNTER32:
1170 		case AX_DATA_TYPE_GAUGE32:
1171 		case AX_DATA_TYPE_TIMETICKS:
1172 			if (ax_pdu_add_uint32(ax,
1173 			    vblist[i].avb_data.avb_uint32) == -1)
1174 				return -1;
1175 			break;
1176 		case AX_DATA_TYPE_COUNTER64:
1177 			if (ax_pdu_add_uint64(ax,
1178 			    vblist[i].avb_data.avb_uint64) == -1)
1179 				return -1;
1180 			break;
1181 		case AX_DATA_TYPE_OCTETSTRING:
1182 		case AX_DATA_TYPE_IPADDRESS:
1183 		case AX_DATA_TYPE_OPAQUE:
1184 			if (ax_pdu_add_str(ax,
1185 			    &(vblist[i].avb_data.avb_ostring)) == -1)
1186 				return -1;
1187 			break;
1188 		case AX_DATA_TYPE_OID:
1189 			if (ax_pdu_add_oid(ax,
1190 			    &(vblist[i].avb_data.avb_oid), 1) == -1)
1191 				return -1;
1192 			break;
1193 		case AX_DATA_TYPE_NULL:
1194 		case AX_DATA_TYPE_NOSUCHOBJECT:
1195 		case AX_DATA_TYPE_NOSUCHINSTANCE:
1196 		case AX_DATA_TYPE_ENDOFMIBVIEW:
1197 			break;
1198 		default:
1199 			errno = EINVAL;
1200 			return -1;
1201 		}
1202 	}
1203 	return 0;
1204 }
1205 
1206 static uint16_t
ax_pdutoh16(struct ax_pdu_header * header,uint8_t * buf)1207 ax_pdutoh16(struct ax_pdu_header *header, uint8_t *buf)
1208 {
1209 	uint16_t value;
1210 
1211 	memcpy(&value, buf, sizeof(value));
1212 	if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
1213 		return be16toh(value);
1214 	return le16toh(value);
1215 }
1216 
1217 static uint32_t
ax_pdutoh32(struct ax_pdu_header * header,uint8_t * buf)1218 ax_pdutoh32(struct ax_pdu_header *header, uint8_t *buf)
1219 {
1220 	uint32_t value;
1221 
1222 	memcpy(&value, buf, sizeof(value));
1223 	if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
1224 		return be32toh(value);
1225 	return le32toh(value);
1226 }
1227 
1228 static uint64_t
ax_pdutoh64(struct ax_pdu_header * header,uint8_t * buf)1229 ax_pdutoh64(struct ax_pdu_header *header, uint8_t *buf)
1230 {
1231 	uint64_t value;
1232 
1233 	memcpy(&value, buf, sizeof(value));
1234 	if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
1235 		return be64toh(value);
1236 	return le64toh(value);
1237 }
1238 
1239 static ssize_t
ax_pdutooid(struct ax_pdu_header * header,struct ax_oid * oid,uint8_t * buf,size_t rawlen)1240 ax_pdutooid(struct ax_pdu_header *header, struct ax_oid *oid,
1241     uint8_t *buf, size_t rawlen)
1242 {
1243 	size_t i = 0;
1244 	ssize_t nread;
1245 
1246 	if (rawlen < 4)
1247 		goto fail;
1248 	rawlen -= 4;
1249 	nread = 4;
1250 	oid->aoi_idlen = *buf++;
1251 	if (rawlen < (oid->aoi_idlen * 4))
1252 		goto fail;
1253 	nread += oid->aoi_idlen * 4;
1254 	if (*buf != 0) {
1255 		oid->aoi_id[0] = 1;
1256 		oid->aoi_id[1] = 3;
1257 		oid->aoi_id[2] = 6;
1258 		oid->aoi_id[3] = 1;
1259 		oid->aoi_id[4] = *buf;
1260 		oid->aoi_idlen += 5;
1261 		i = 5;
1262 	}
1263 	buf++;
1264 	oid->aoi_include = *buf;
1265 	if (oid->aoi_idlen > AX_OID_MAX_LEN)
1266 		goto fail;
1267 	for (buf += 2; i < oid->aoi_idlen; i++, buf += 4)
1268 		oid->aoi_id[i] = ax_pdutoh32(header, buf);
1269 
1270 	return nread;
1271 
1272 fail:
1273 	errno = EPROTO;
1274 	return -1;
1275 }
1276 
1277 static ssize_t
ax_pdutoostring(struct ax_pdu_header * header,struct ax_ostring * ostring,uint8_t * buf,size_t rawlen)1278 ax_pdutoostring(struct ax_pdu_header *header,
1279     struct ax_ostring *ostring, uint8_t *buf, size_t rawlen)
1280 {
1281 	ssize_t nread;
1282 
1283 	if (rawlen < 4)
1284 		goto fail;
1285 
1286 	ostring->aos_slen = ax_pdutoh32(header, buf);
1287 	rawlen -= 4;
1288 	buf += 4;
1289 	if (ostring->aos_slen > rawlen)
1290 		goto fail;
1291 	if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL)
1292 		return -1;
1293 	memcpy(ostring->aos_string, buf, ostring->aos_slen);
1294 	ostring->aos_string[ostring->aos_slen] = '\0';
1295 
1296 	nread = 4 + ostring->aos_slen;
1297 	if (ostring->aos_slen % 4 != 0)
1298 		nread += 4 - (ostring->aos_slen % 4);
1299 
1300 	return nread;
1301 
1302 fail:
1303 	errno = EPROTO;
1304 	return -1;
1305 }
1306 
1307 static ssize_t
ax_pdutovarbind(struct ax_pdu_header * header,struct ax_varbind * varbind,uint8_t * buf,size_t rawlen)1308 ax_pdutovarbind(struct ax_pdu_header *header,
1309     struct ax_varbind *varbind, uint8_t *buf, size_t rawlen)
1310 {
1311 	ssize_t nread, rread = 4;
1312 
1313 	if (rawlen == 0)
1314 		return 0;
1315 
1316 	if (rawlen < 8)
1317 		goto fail;
1318 	varbind->avb_type = ax_pdutoh16(header, buf);
1319 
1320 	buf += 4;
1321 	rawlen -= 4;
1322 	nread = ax_pdutooid(header, &(varbind->avb_oid), buf, rawlen);
1323 	if (nread == -1)
1324 		return -1;
1325 	rread += nread;
1326 	buf += nread;
1327 	rawlen -= nread;
1328 
1329 	switch(varbind->avb_type) {
1330 	case AX_DATA_TYPE_INTEGER:
1331 		if (rawlen < 4)
1332 			goto fail;
1333 		varbind->avb_data.avb_int32 = ax_pdutoh32(header, buf);
1334 		return rread + 4;
1335 	case AX_DATA_TYPE_COUNTER32:
1336 	case AX_DATA_TYPE_GAUGE32:
1337 	case AX_DATA_TYPE_TIMETICKS:
1338 		if (rawlen < 4)
1339 			goto fail;
1340 		varbind->avb_data.avb_uint32 = ax_pdutoh32(header, buf);
1341 		return rread + 4;
1342 	case AX_DATA_TYPE_COUNTER64:
1343 		if (rawlen < 8)
1344 			goto fail;
1345 		varbind->avb_data.avb_uint64 = ax_pdutoh64(header, buf);
1346 		return rread + 8;
1347 	case AX_DATA_TYPE_OCTETSTRING:
1348 	case AX_DATA_TYPE_IPADDRESS:
1349 	case AX_DATA_TYPE_OPAQUE:
1350 		nread = ax_pdutoostring(header,
1351 		    &(varbind->avb_data.avb_ostring), buf, rawlen);
1352 		if (nread == -1)
1353 			return -1;
1354 		return nread + rread;
1355 	case AX_DATA_TYPE_OID:
1356 		nread = ax_pdutooid(header, &(varbind->avb_data.avb_oid),
1357 		    buf, rawlen);
1358 		if (nread == -1)
1359 			return -1;
1360 		return nread + rread;
1361 	case AX_DATA_TYPE_NULL:
1362 	case AX_DATA_TYPE_NOSUCHOBJECT:
1363 	case AX_DATA_TYPE_NOSUCHINSTANCE:
1364 	case AX_DATA_TYPE_ENDOFMIBVIEW:
1365 		return rread;
1366 	}
1367 
1368 fail:
1369 	errno = EPROTO;
1370 	return -1;
1371 }
1372