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