File: | src/lib/libagentx/ax.c |
Warning: | line 1034, column 15 Array access (via field 'ax_packetids') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ax.c,v 1.8 2021/10/24 17:43:38 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(*__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_HEADER20 20 | |||
35 | ||||
36 | static int ax_pdu_need(struct ax *, size_t); | |||
37 | static int ax_pdu_header(struct ax *, | |||
38 | enum ax_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t, | |||
39 | struct ax_ostring *); | |||
40 | static uint32_t ax_packetid(struct ax *); | |||
41 | static uint32_t ax_pdu_queue(struct ax *); | |||
42 | static int ax_pdu_add_uint16(struct ax *, uint16_t); | |||
43 | static int ax_pdu_add_uint32(struct ax *, uint32_t); | |||
44 | static int ax_pdu_add_uint64(struct ax *, uint64_t); | |||
45 | static int ax_pdu_add_oid(struct ax *, struct ax_oid *, int); | |||
46 | static int ax_pdu_add_str(struct ax *, struct ax_ostring *); | |||
47 | static int ax_pdu_add_varbindlist( struct ax *, struct ax_varbind *, | |||
48 | size_t); | |||
49 | static uint16_t ax_pdutoh16(struct ax_pdu_header *, uint8_t *); | |||
50 | static uint32_t ax_pdutoh32(struct ax_pdu_header *, uint8_t *); | |||
51 | static uint64_t ax_pdutoh64(struct ax_pdu_header *, uint8_t *); | |||
52 | static ssize_t ax_pdutooid(struct ax_pdu_header *, struct ax_oid *, | |||
53 | uint8_t *, size_t); | |||
54 | static ssize_t ax_pdutoostring(struct ax_pdu_header *, | |||
55 | struct ax_ostring *, uint8_t *, size_t); | |||
56 | static ssize_t ax_pdutovarbind(struct ax_pdu_header *, | |||
57 | struct ax_varbind *, uint8_t *, size_t); | |||
58 | ||||
59 | struct ax * | |||
60 | ax_new(int fd) | |||
61 | { | |||
62 | struct ax *ax; | |||
63 | ||||
64 | if (fd == -1) { | |||
65 | errno(*__errno()) = EINVAL22; | |||
66 | return NULL((void *)0); | |||
67 | } | |||
68 | ||||
69 | if ((ax = calloc(1, sizeof(*ax))) == NULL((void *)0)) | |||
70 | return NULL((void *)0); | |||
71 | ax->ax_fd = fd; | |||
72 | ax->ax_rbsize = 512; | |||
73 | if ((ax->ax_rbuf = malloc(ax->ax_rbsize)) == NULL((void *)0)) | |||
74 | goto fail; | |||
75 | ax->ax_byteorder = AX_BYTE_ORDER_NATIVEAX_BYTE_ORDER_LE; | |||
76 | ||||
77 | return ax; | |||
78 | ||||
79 | fail: | |||
80 | free(ax); | |||
81 | return NULL((void *)0); | |||
82 | } | |||
83 | ||||
84 | void | |||
85 | ax_free(struct ax *ax) | |||
86 | { | |||
87 | if (ax == NULL((void *)0)) | |||
88 | return; | |||
89 | close(ax->ax_fd); | |||
90 | free(ax->ax_rbuf); | |||
91 | free(ax->ax_wbuf); | |||
92 | free(ax->ax_packetids); | |||
93 | free(ax); | |||
94 | } | |||
95 | ||||
96 | struct ax_pdu * | |||
97 | ax_recv(struct ax *ax) | |||
98 | { | |||
99 | struct ax_pdu *pdu; | |||
100 | struct ax_pdu_header header; | |||
101 | struct ax_pdu_response *response; | |||
102 | struct ax_varbind *varbind; | |||
103 | struct ax_pdu_searchrangelist *srl = NULL((void *)0); | |||
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_HEADER20) { | |||
114 | if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, | |||
115 | AX_PDU_HEADER20 - ax->ax_rblen)) == 0) { | |||
116 | errno(*__errno()) = ECONNRESET54; | |||
117 | return NULL((void *)0); | |||
118 | } | |||
119 | if (nread == -1) | |||
120 | return NULL((void *)0); | |||
121 | ax->ax_rblen += nread; | |||
122 | if (ax->ax_rblen < AX_PDU_HEADER20) { | |||
123 | errno(*__errno()) = EAGAIN35; | |||
124 | return NULL((void *)0); | |||
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(*__errno()) = EPROTO95; | |||
142 | return NULL((void *)0); | |||
143 | } | |||
144 | if (ax->ax_rblen < AX_PDU_HEADER20 + header.aph_plength) { | |||
145 | if (AX_PDU_HEADER20 + header.aph_plength > ax->ax_rbsize) { | |||
146 | rbsize = (((AX_PDU_HEADER20 + header.aph_plength) | |||
147 | / 512) + 1) * 512; | |||
148 | if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize, | |||
149 | rbsize, sizeof(*rbuf))) == NULL((void *)0)) | |||
150 | return NULL((void *)0); | |||
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_HEADER20)); | |||
156 | if (nread == 0) | |||
157 | errno(*__errno()) = ECONNRESET54; | |||
158 | if (nread <= 0) | |||
159 | return NULL((void *)0); | |||
160 | ax->ax_rblen += nread; | |||
161 | if (ax->ax_rblen < AX_PDU_HEADER20 + header.aph_plength) { | |||
162 | errno(*__errno()) = EAGAIN35; | |||
163 | return NULL((void *)0); | |||
164 | } | |||
165 | } | |||
166 | ||||
167 | if ((pdu = calloc(1, sizeof(*pdu))) == NULL((void *)0)) | |||
168 | return NULL((void *)0); | |||
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(&__sF[2]), "received packet:\n"); | |||
178 | for (i = 0; i < pdu->ap_header.aph_plength + AX_PDU_HEADER20; | |||
179 | i++) { | |||
180 | fprintf(stderr(&__sF[2]), "%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(&__sF[2]), "%.4s", chars); | |||
187 | fprintf(stderr(&__sF[2]), "\n"); | |||
188 | print = 1; | |||
189 | } | |||
190 | } | |||
191 | } | |||
192 | #endif | |||
193 | ||||
194 | u8 = (ax->ax_rbuf) + AX_PDU_HEADER20; | |||
195 | rawlen = pdu->ap_header.aph_plength; | |||
196 | if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT(1 << 3)) { | |||
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(*__errno()) = EPROTO95; | |||
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((void *)0)) | |||
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((void *)0)) | |||
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(*__errno()) = EPROTO95; | |||
266 | goto fail; | |||
267 | } | |||
268 | break; | |||
269 | case AX_PDU_TYPE_RESPONSE: | |||
270 | if (ax->ax_packetids != NULL((void *)0)) { | |||
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(*__errno()) = EPROTO95; | |||
285 | goto fail; | |||
286 | } | |||
287 | } | |||
288 | if (rawlen < 8) { | |||
289 | errno(*__errno()) = EPROTO95; | |||
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((void *)0)) | |||
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((void *)0)) | |||
320 | goto fail; | |||
321 | memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AX_PDU_HEADER20, | |||
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((void *)0); | |||
332 | } | |||
333 | ||||
334 | static int | |||
335 | ax_pdu_need(struct ax *ax, size_t need) | |||
336 | { | |||
337 | uint8_t *wbuf; | |||
338 | size_t wbsize; | |||
339 | ||||
340 | if (ax->ax_wbtlen + need >= ax->ax_wbsize) { | |||
341 | wbsize = (((ax->ax_wbtlen + need) / 512) + 1) * 512; | |||
342 | wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1); | |||
343 | if (wbuf == NULL((void *)0)) { | |||
344 | ax->ax_wbtlen = ax->ax_wblen; | |||
345 | return -1; | |||
346 | } | |||
347 | ax->ax_wbsize = wbsize; | |||
348 | ax->ax_wbuf = wbuf; | |||
349 | } | |||
350 | ||||
351 | return 0; | |||
352 | } | |||
353 | ||||
354 | ssize_t | |||
355 | ax_send(struct ax *ax) | |||
356 | { | |||
357 | ssize_t nwrite; | |||
358 | ||||
359 | if (ax->ax_wblen != ax->ax_wbtlen) { | |||
360 | errno(*__errno()) = EALREADY37; | |||
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(&__sF[2]), "sending packet:\n"); | |||
374 | for (i = 0; i < ax->ax_wblen; i++) { | |||
375 | fprintf(stderr(&__sF[2]), "%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(&__sF[2]), "%.4s", chars); | |||
382 | fprintf(stderr(&__sF[2]), "\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_NOSIGNAL0x400 | MSG_DONTWAIT0x80)) == -1) | |||
391 | return -1; | |||
392 | ||||
393 | memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite); | |||
394 | ax->ax_wblen -= nwrite; | |||
395 | ax->ax_wbtlen = ax->ax_wblen; | |||
396 | ||||
397 | return ax->ax_wblen; | |||
398 | } | |||
399 | ||||
400 | uint32_t | |||
401 | ax_open(struct ax *ax, uint8_t timeout, struct ax_oid *oid, | |||
402 | struct ax_ostring *descr) | |||
403 | { | |||
404 | if (ax_pdu_header(ax, AX_PDU_TYPE_OPEN, 0, 0, 0, 0, | |||
405 | NULL((void *)0)) == -1) | |||
406 | return 0; | |||
407 | ax_pdu_need(ax, 4); | |||
408 | ax->ax_wbuf[ax->ax_wbtlen++] = timeout; | |||
409 | memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); | |||
410 | ax->ax_wbtlen += 3; | |||
411 | if (ax_pdu_add_oid(ax, oid, 0) == -1) | |||
412 | return 0; | |||
413 | if (ax_pdu_add_str(ax, descr) == -1) | |||
414 | return 0; | |||
415 | ||||
416 | return ax_pdu_queue(ax); | |||
417 | } | |||
418 | ||||
419 | uint32_t | |||
420 | ax_close(struct ax *ax, uint32_t sessionid, | |||
421 | enum ax_close_reason reason) | |||
422 | { | |||
423 | if (ax_pdu_header(ax, AX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0, | |||
424 | NULL((void *)0)) == -1) | |||
425 | return 0; | |||
426 | ||||
427 | if (ax_pdu_need(ax, 4) == -1) | |||
428 | return 0; | |||
429 | ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason; | |||
430 | memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); | |||
431 | ax->ax_wbtlen += 3; | |||
432 | ||||
433 | return ax_pdu_queue(ax); | |||
434 | } | |||
435 | ||||
436 | uint32_t | |||
437 | ax_indexallocate(struct ax *ax, uint8_t flags, uint32_t sessionid, | |||
438 | struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) | |||
439 | { | |||
440 | if (flags & ~(AX_PDU_FLAG_NEW_INDEX(1 << 1) | AX_PDU_FLAG_ANY_INDEX(1 << 2))) { | |||
441 | errno(*__errno()) = EINVAL22; | |||
442 | return 0; | |||
443 | } | |||
444 | ||||
445 | if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXALLOCATE, flags, | |||
446 | sessionid, 0, 0, context) == -1) | |||
447 | return 0; | |||
448 | ||||
449 | if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) | |||
450 | return 0; | |||
451 | ||||
452 | return ax_pdu_queue(ax); | |||
453 | } | |||
454 | ||||
455 | uint32_t | |||
456 | ax_indexdeallocate(struct ax *ax, uint32_t sessionid, | |||
457 | struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb) | |||
458 | { | |||
459 | if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXDEALLOCATE, 0, | |||
460 | sessionid, 0, 0, context) == -1) | |||
461 | return 0; | |||
462 | ||||
463 | if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) | |||
464 | return 0; | |||
465 | ||||
466 | return ax_pdu_queue(ax); | |||
467 | } | |||
468 | ||||
469 | uint32_t | |||
470 | ax_addagentcaps(struct ax *ax, uint32_t sessionid, | |||
471 | struct ax_ostring *context, struct ax_oid *id, | |||
472 | struct ax_ostring *descr) | |||
473 | { | |||
474 | if (ax_pdu_header(ax, AX_PDU_TYPE_ADDAGENTCAPS, 0, | |||
475 | sessionid, 0, 0, context) == -1) | |||
476 | return 0; | |||
477 | if (ax_pdu_add_oid(ax, id, 0) == -1) | |||
478 | return 0; | |||
479 | if (ax_pdu_add_str(ax, descr) == -1) | |||
480 | return 0; | |||
481 | ||||
482 | return ax_pdu_queue(ax); | |||
483 | } | |||
484 | ||||
485 | uint32_t | |||
486 | ax_removeagentcaps(struct ax *ax, uint32_t sessionid, | |||
487 | struct ax_ostring *context, struct ax_oid *id) | |||
488 | { | |||
489 | if (ax_pdu_header(ax, AX_PDU_TYPE_REMOVEAGENTCAPS, 0, | |||
490 | sessionid, 0, 0, context) == -1) | |||
491 | return 0; | |||
492 | if (ax_pdu_add_oid(ax, id, 0) == -1) | |||
493 | return 0; | |||
494 | ||||
495 | return ax_pdu_queue(ax); | |||
496 | ||||
497 | } | |||
498 | ||||
499 | uint32_t | |||
500 | ax_register(struct ax *ax, uint8_t flags, uint32_t sessionid, | |||
501 | struct ax_ostring *context, uint8_t timeout, uint8_t priority, | |||
502 | uint8_t range_subid, struct ax_oid *subtree, uint32_t upperbound) | |||
503 | { | |||
504 | if (flags & ~(AX_PDU_FLAG_INSTANCE_REGISTRATION(1 << 0))) { | |||
505 | errno(*__errno()) = EINVAL22; | |||
506 | return 0; | |||
507 | } | |||
508 | ||||
509 | if (ax_pdu_header(ax, AX_PDU_TYPE_REGISTER, flags, | |||
510 | sessionid, 0, 0, context) == -1) | |||
511 | return 0; | |||
512 | ||||
513 | if (ax_pdu_need(ax, 4) == -1) | |||
514 | return 0; | |||
515 | ax->ax_wbuf[ax->ax_wbtlen++] = timeout; | |||
516 | ax->ax_wbuf[ax->ax_wbtlen++] = priority; | |||
517 | ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; | |||
518 | ax->ax_wbuf[ax->ax_wbtlen++] = 0; | |||
519 | if (ax_pdu_add_oid(ax, subtree, 0) == -1) | |||
520 | return 0; | |||
521 | if (range_subid != 0) { | |||
522 | if (ax_pdu_add_uint32(ax, upperbound) == -1) | |||
523 | return 0; | |||
524 | } | |||
525 | ||||
526 | return ax_pdu_queue(ax); | |||
527 | } | |||
528 | ||||
529 | uint32_t | |||
530 | ax_unregister(struct ax *ax, uint32_t sessionid, | |||
531 | struct ax_ostring *context, uint8_t priority, uint8_t range_subid, | |||
532 | struct ax_oid *subtree, uint32_t upperbound) | |||
533 | { | |||
534 | if (ax_pdu_header(ax, AX_PDU_TYPE_UNREGISTER, 0, | |||
535 | sessionid, 0, 0, context) == -1) | |||
536 | return 0; | |||
537 | ||||
538 | if (ax_pdu_need(ax, 4) == -1) | |||
539 | return 0; | |||
540 | ax->ax_wbuf[ax->ax_wbtlen++] = 0; | |||
541 | ax->ax_wbuf[ax->ax_wbtlen++] = priority; | |||
542 | ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; | |||
543 | ax->ax_wbuf[ax->ax_wbtlen++] = 0; | |||
544 | if (ax_pdu_add_oid(ax, subtree, 0) == -1) | |||
545 | return 0; | |||
546 | if (range_subid != 0) { | |||
547 | if (ax_pdu_add_uint32(ax, upperbound) == -1) | |||
548 | return 0; | |||
549 | } | |||
550 | ||||
551 | return ax_pdu_queue(ax); | |||
552 | } | |||
553 | ||||
554 | int | |||
555 | ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid, | |||
556 | uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime, | |||
557 | uint16_t error, uint16_t index, struct ax_varbind *vblist, size_t nvb) | |||
558 | { | |||
559 | if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid, | |||
| ||||
560 | transactionid, packetid, context) == -1) | |||
561 | return -1; | |||
562 | ||||
563 | if (ax_pdu_add_uint32(ax, sysuptime) == -1 || | |||
564 | ax_pdu_add_uint16(ax, error) == -1 || | |||
565 | ax_pdu_add_uint16(ax, index) == -1) | |||
566 | return -1; | |||
567 | ||||
568 | if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1) | |||
569 | return -1; | |||
570 | if (ax_pdu_queue(ax) == 0) | |||
571 | return -1; | |||
572 | return 0; | |||
573 | } | |||
574 | ||||
575 | void | |||
576 | ax_pdu_free(struct ax_pdu *pdu) | |||
577 | { | |||
578 | size_t i; | |||
579 | struct ax_pdu_response *response; | |||
580 | ||||
581 | if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT(1 << 3)) | |||
582 | free(pdu->ap_context.aos_string); | |||
583 | ||||
584 | switch (pdu->ap_header.aph_type) { | |||
585 | case AX_PDU_TYPE_GET: | |||
586 | case AX_PDU_TYPE_GETNEXT: | |||
587 | case AX_PDU_TYPE_GETBULK: | |||
588 | free(pdu->ap_payload.ap_srl.ap_sr); | |||
589 | break; | |||
590 | case AX_PDU_TYPE_RESPONSE: | |||
591 | response = &(pdu->ap_payload.ap_response); | |||
592 | for (i = 0; i < response->ap_nvarbind; i++) | |||
593 | ax_varbind_free(&(response->ap_varbindlist[i])); | |||
594 | free(response->ap_varbindlist); | |||
595 | break; | |||
596 | default: | |||
597 | free(pdu->ap_payload.ap_raw); | |||
598 | break; | |||
599 | } | |||
600 | free(pdu); | |||
601 | } | |||
602 | ||||
603 | void | |||
604 | ax_varbind_free(struct ax_varbind *varbind) | |||
605 | { | |||
606 | switch (varbind->avb_type) { | |||
607 | case AX_DATA_TYPE_OCTETSTRING: | |||
608 | case AX_DATA_TYPE_IPADDRESS: | |||
609 | case AX_DATA_TYPE_OPAQUE: | |||
610 | free(varbind->avb_data.avb_ostring.aos_string); | |||
611 | break; | |||
612 | default: | |||
613 | break; | |||
614 | } | |||
615 | } | |||
616 | ||||
617 | const char * | |||
618 | ax_error2string(enum ax_pdu_error error) | |||
619 | { | |||
620 | static char buffer[64]; | |||
621 | switch (error) { | |||
622 | case AX_PDU_ERROR_NOERROR: | |||
623 | return "No error"; | |||
624 | case AX_PDU_ERROR_GENERR: | |||
625 | return "Generic error"; | |||
626 | case AX_PDU_ERROR_NOACCESS: | |||
627 | return "No access"; | |||
628 | case AX_PDU_ERROR_WRONGTYPE: | |||
629 | return "Wrong type"; | |||
630 | case AX_PDU_ERROR_WRONGLENGTH: | |||
631 | return "Wrong length"; | |||
632 | case AX_PDU_ERROR_WRONGENCODING: | |||
633 | return "Wrong encoding"; | |||
634 | case AX_PDU_ERROR_WRONGVALUE: | |||
635 | return "Wrong value"; | |||
636 | case AX_PDU_ERROR_NOCREATION: | |||
637 | return "No creation"; | |||
638 | case AX_PDU_ERROR_INCONSISTENTVALUE: | |||
639 | return "Inconsistent value"; | |||
640 | case AX_PDU_ERROR_RESOURCEUNAVAILABLE: | |||
641 | return "Resource unavailable"; | |||
642 | case AX_PDU_ERROR_COMMITFAILED: | |||
643 | return "Commit failed"; | |||
644 | case AX_PDU_ERROR_UNDOFAILED: | |||
645 | return "Undo failed"; | |||
646 | case AX_PDU_ERROR_NOTWRITABLE: | |||
647 | return "Not writable"; | |||
648 | case AX_PDU_ERROR_INCONSISTENTNAME: | |||
649 | return "Inconsistent name"; | |||
650 | case AX_PDU_ERROR_OPENFAILED: | |||
651 | return "Open Failed"; | |||
652 | case AX_PDU_ERROR_NOTOPEN: | |||
653 | return "Not open"; | |||
654 | case AX_PDU_ERROR_INDEXWRONGTYPE: | |||
655 | return "Index wrong type"; | |||
656 | case AX_PDU_ERROR_INDEXALREADYALLOCATED: | |||
657 | return "Index already allocated"; | |||
658 | case AX_PDU_ERROR_INDEXNONEAVAILABLE: | |||
659 | return "Index none available"; | |||
660 | case AX_PDU_ERROR_INDEXNOTALLOCATED: | |||
661 | return "Index not allocated"; | |||
662 | case AX_PDU_ERROR_UNSUPPORTEDCONETXT: | |||
663 | return "Unsupported context"; | |||
664 | case AX_PDU_ERROR_DUPLICATEREGISTRATION: | |||
665 | return "Duplicate registration"; | |||
666 | case AX_PDU_ERROR_UNKNOWNREGISTRATION: | |||
667 | return "Unkown registration"; | |||
668 | case AX_PDU_ERROR_UNKNOWNAGENTCAPS: | |||
669 | return "Unknown agent capabilities"; | |||
670 | case AX_PDU_ERROR_PARSEERROR: | |||
671 | return "Parse error"; | |||
672 | case AX_PDU_ERROR_REQUESTDENIED: | |||
673 | return "Request denied"; | |||
674 | case AX_PDU_ERROR_PROCESSINGERROR: | |||
675 | return "Processing error"; | |||
676 | } | |||
677 | snprintf(buffer, sizeof(buffer), "Unknown error: %d", error); | |||
678 | return buffer; | |||
679 | } | |||
680 | ||||
681 | const char * | |||
682 | ax_pdutype2string(enum ax_pdu_type type) | |||
683 | { | |||
684 | static char buffer[64]; | |||
685 | switch(type) { | |||
686 | case AX_PDU_TYPE_OPEN: | |||
687 | return "agentx-Open-PDU"; | |||
688 | case AX_PDU_TYPE_CLOSE: | |||
689 | return "agentx-Close-PDU"; | |||
690 | case AX_PDU_TYPE_REGISTER: | |||
691 | return "agentx-Register-PDU"; | |||
692 | case AX_PDU_TYPE_UNREGISTER: | |||
693 | return "agentx-Unregister-PDU"; | |||
694 | case AX_PDU_TYPE_GET: | |||
695 | return "agentx-Get-PDU"; | |||
696 | case AX_PDU_TYPE_GETNEXT: | |||
697 | return "agentx-GetNext-PDU"; | |||
698 | case AX_PDU_TYPE_GETBULK: | |||
699 | return "agentx-GetBulk-PDU"; | |||
700 | case AX_PDU_TYPE_TESTSET: | |||
701 | return "agentx-TestSet-PDU"; | |||
702 | case AX_PDU_TYPE_COMMITSET: | |||
703 | return "agentx-CommitSet-PDU"; | |||
704 | case AX_PDU_TYPE_UNDOSET: | |||
705 | return "agentx-UndoSet-PDU"; | |||
706 | case AX_PDU_TYPE_CLEANUPSET: | |||
707 | return "agentx-CleanupSet-PDU"; | |||
708 | case AX_PDU_TYPE_NOTIFY: | |||
709 | return "agentx-Notify-PDU"; | |||
710 | case AX_PDU_TYPE_PING: | |||
711 | return "agentx-Ping-PDU"; | |||
712 | case AX_PDU_TYPE_INDEXALLOCATE: | |||
713 | return "agentx-IndexAllocate-PDU"; | |||
714 | case AX_PDU_TYPE_INDEXDEALLOCATE: | |||
715 | return "agentx-IndexDeallocate-PDU"; | |||
716 | case AX_PDU_TYPE_ADDAGENTCAPS: | |||
717 | return "agentx-AddAgentCaps-PDU"; | |||
718 | case AX_PDU_TYPE_REMOVEAGENTCAPS: | |||
719 | return "agentx-RemoveAgentCaps-PDU"; | |||
720 | case AX_PDU_TYPE_RESPONSE: | |||
721 | return "agentx-Response-PDU"; | |||
722 | } | |||
723 | snprintf(buffer, sizeof(buffer), "Unknown type: %d", type); | |||
724 | return buffer; | |||
725 | } | |||
726 | ||||
727 | const char * | |||
728 | ax_closereason2string(enum ax_close_reason reason) | |||
729 | { | |||
730 | static char buffer[64]; | |||
731 | ||||
732 | switch (reason) { | |||
733 | case AX_CLOSE_OTHER: | |||
734 | return "Undefined reason"; | |||
735 | case AX_CLOSEN_PARSEERROR: | |||
736 | return "Too many AgentX parse errors from peer"; | |||
737 | case AX_CLOSE_PROTOCOLERROR: | |||
738 | return "Too many AgentX protocol errors from peer"; | |||
739 | case AX_CLOSE_TIMEOUTS: | |||
740 | return "Too many timeouts waiting for peer"; | |||
741 | case AX_CLOSE_SHUTDOWN: | |||
742 | return "shutting down"; | |||
743 | case AX_CLOSE_BYMANAGER: | |||
744 | return "Manager shuts down"; | |||
745 | } | |||
746 | snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason); | |||
747 | return buffer; | |||
748 | } | |||
749 | ||||
750 | const char * | |||
751 | ax_oid2string(struct ax_oid *oid) | |||
752 | { | |||
753 | return ax_oidrange2string(oid, 0, 0); | |||
754 | } | |||
755 | ||||
756 | const char * | |||
757 | ax_oidrange2string(struct ax_oid *oid, uint8_t range_subid, | |||
758 | uint32_t upperbound) | |||
759 | { | |||
760 | static char buf[1024]; | |||
761 | char *p; | |||
762 | size_t i, rest; | |||
763 | int ret; | |||
764 | ||||
765 | rest = sizeof(buf); | |||
766 | p = buf; | |||
767 | 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 * | |||
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_INET2, vb->avb_data.avb_ostring.aos_string, | |||
863 | tmpbuf, sizeof(tmpbuf)) == NULL((void *)0)) { | |||
864 | snprintf(buf, sizeof(buf), "%s: (ipaddress)" | |||
865 | "<unparseable>: %s", | |||
866 | ax_oid2string(&(vb->avb_oid)), | |||
867 | strerror(errno(*__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"llu", | |||
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 | |||
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 | |||
949 | ax_oid_add(struct ax_oid *oid, uint32_t value) | |||
950 | { | |||
951 | if (oid->aoi_idlen == AX_OID_MAX_LEN128) | |||
952 | return -1; | |||
953 | oid->aoi_id[oid->aoi_idlen++] = value; | |||
954 | return 0; | |||
955 | } | |||
956 | ||||
957 | static uint32_t | |||
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(1 << 4) : 0; | |||
966 | packetid = ax_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12])); | |||
967 | plength = (ax->ax_wbtlen - ax->ax_wblen) - AX_PDU_HEADER20; | |||
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 | |||
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(*__errno()) = EALREADY37; | |||
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((void *)0)) | |||
991 | flags |= AX_PDU_FLAG_NON_DEFAULT_CONTEXT(1 << 3); | |||
992 | if (ax->ax_byteorder == AX_BYTE_ORDER_BE) | |||
993 | flags |= AX_PDU_FLAG_NETWORK_BYTE_ORDER(1 << 4); | |||
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((void *)0)) { | |||
1005 | if (ax_pdu_add_str(ax, context) == -1) | |||
1006 | return -1; | |||
1007 | } | |||
1008 | ||||
1009 | return 0; | |||
1010 | } | |||
1011 | ||||
1012 | static uint32_t | |||
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((void *)0)) { | |||
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((void *)0)) | |||
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 | |||
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)(__uint16_t)(__builtin_constant_p(value) ? (__uint16_t)(((__uint16_t )(value) & 0xffU) << 8 | ((__uint16_t)(value) & 0xff00U) >> 8) : __swap16md(value)); | |||
1054 | else | |||
1055 | value = htole16(value)((__uint16_t)(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 | |||
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)(__uint32_t)(__builtin_constant_p(value) ? (__uint32_t)(((__uint32_t )(value) & 0xff) << 24 | ((__uint32_t)(value) & 0xff00) << 8 | ((__uint32_t)(value) & 0xff0000) >> 8 | ((__uint32_t)(value) & 0xff000000) >> 24) : __swap32md (value)); | |||
1069 | else | |||
1070 | value = htole32(value)((__uint32_t)(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 | |||
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)(__uint64_t)(__builtin_constant_p(value) ? (__uint64_t)((((__uint64_t )(value) & 0xff) << 56) | ((__uint64_t)(value) & 0xff00ULL) << 40 | ((__uint64_t)(value) & 0xff0000ULL ) << 24 | ((__uint64_t)(value) & 0xff000000ULL) << 8 | ((__uint64_t)(value) & 0xff00000000ULL) >> 8 | ((__uint64_t)(value) & 0xff0000000000ULL) >> 24 | ( (__uint64_t)(value) & 0xff000000000000ULL) >> 40 | ( (__uint64_t)(value) & 0xff00000000000000ULL) >> 56) : __swap64md(value)); | |||
1084 | else | |||
1085 | value = htole64(value)((__uint64_t)(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 | |||
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((void *)0)) | |||
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_MAX0xff) { | |||
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 | |||
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 | |||
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(*__errno()) = EINVAL22; | |||
1200 | return -1; | |||
1201 | } | |||
1202 | } | |||
1203 | return 0; | |||
1204 | } | |||
1205 | ||||
1206 | static uint16_t | |||
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(1 << 4)) | |||
1213 | return be16toh(value)(__uint16_t)(__builtin_constant_p(value) ? (__uint16_t)(((__uint16_t )(value) & 0xffU) << 8 | ((__uint16_t)(value) & 0xff00U) >> 8) : __swap16md(value)); | |||
1214 | return le16toh(value)((__uint16_t)(value)); | |||
1215 | } | |||
1216 | ||||
1217 | static uint32_t | |||
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(1 << 4)) | |||
1224 | return be32toh(value)(__uint32_t)(__builtin_constant_p(value) ? (__uint32_t)(((__uint32_t )(value) & 0xff) << 24 | ((__uint32_t)(value) & 0xff00) << 8 | ((__uint32_t)(value) & 0xff0000) >> 8 | ((__uint32_t)(value) & 0xff000000) >> 24) : __swap32md (value)); | |||
1225 | return le32toh(value)((__uint32_t)(value)); | |||
1226 | } | |||
1227 | ||||
1228 | static uint64_t | |||
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(1 << 4)) | |||
1235 | return be64toh(value)(__uint64_t)(__builtin_constant_p(value) ? (__uint64_t)((((__uint64_t )(value) & 0xff) << 56) | ((__uint64_t)(value) & 0xff00ULL) << 40 | ((__uint64_t)(value) & 0xff0000ULL ) << 24 | ((__uint64_t)(value) & 0xff000000ULL) << 8 | ((__uint64_t)(value) & 0xff00000000ULL) >> 8 | ((__uint64_t)(value) & 0xff0000000000ULL) >> 24 | ( (__uint64_t)(value) & 0xff000000000000ULL) >> 40 | ( (__uint64_t)(value) & 0xff00000000000000ULL) >> 56) : __swap64md(value)); | |||
1236 | return le64toh(value)((__uint64_t)(value)); | |||
1237 | } | |||
1238 | ||||
1239 | static ssize_t | |||
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 | for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) | |||
1266 | oid->aoi_id[i] = ax_pdutoh32(header, buf); | |||
1267 | ||||
1268 | return nread; | |||
1269 | ||||
1270 | fail: | |||
1271 | errno(*__errno()) = EPROTO95; | |||
1272 | return -1; | |||
1273 | } | |||
1274 | ||||
1275 | static ssize_t | |||
1276 | ax_pdutoostring(struct ax_pdu_header *header, | |||
1277 | struct ax_ostring *ostring, uint8_t *buf, size_t rawlen) | |||
1278 | { | |||
1279 | ssize_t nread; | |||
1280 | ||||
1281 | if (rawlen < 4) | |||
1282 | goto fail; | |||
1283 | ||||
1284 | ostring->aos_slen = ax_pdutoh32(header, buf); | |||
1285 | rawlen -= 4; | |||
1286 | buf += 4; | |||
1287 | if (ostring->aos_slen > rawlen) | |||
1288 | goto fail; | |||
1289 | if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL((void *)0)) | |||
1290 | return -1; | |||
1291 | memcpy(ostring->aos_string, buf, ostring->aos_slen); | |||
1292 | ostring->aos_string[ostring->aos_slen] = '\0'; | |||
1293 | ||||
1294 | nread = 4 + ostring->aos_slen; | |||
1295 | if (ostring->aos_slen % 4 != 0) | |||
1296 | nread += 4 - (ostring->aos_slen % 4); | |||
1297 | ||||
1298 | return nread; | |||
1299 | ||||
1300 | fail: | |||
1301 | errno(*__errno()) = EPROTO95; | |||
1302 | return -1; | |||
1303 | } | |||
1304 | ||||
1305 | static ssize_t | |||
1306 | ax_pdutovarbind(struct ax_pdu_header *header, | |||
1307 | struct ax_varbind *varbind, uint8_t *buf, size_t rawlen) | |||
1308 | { | |||
1309 | ssize_t nread, rread = 4; | |||
1310 | ||||
1311 | if (rawlen == 0) | |||
1312 | return 0; | |||
1313 | ||||
1314 | if (rawlen < 8) | |||
1315 | goto fail; | |||
1316 | varbind->avb_type = ax_pdutoh16(header, buf); | |||
1317 | ||||
1318 | buf += 4; | |||
1319 | rawlen -= 4; | |||
1320 | nread = ax_pdutooid(header, &(varbind->avb_oid), buf, rawlen); | |||
1321 | if (nread == -1) | |||
1322 | return -1; | |||
1323 | rread += nread; | |||
1324 | buf += nread; | |||
1325 | rawlen -= nread; | |||
1326 | ||||
1327 | switch(varbind->avb_type) { | |||
1328 | case AX_DATA_TYPE_INTEGER: | |||
1329 | if (rawlen < 4) | |||
1330 | goto fail; | |||
1331 | varbind->avb_data.avb_int32 = ax_pdutoh32(header, buf); | |||
1332 | return rread + 4; | |||
1333 | case AX_DATA_TYPE_COUNTER32: | |||
1334 | case AX_DATA_TYPE_GAUGE32: | |||
1335 | case AX_DATA_TYPE_TIMETICKS: | |||
1336 | if (rawlen < 4) | |||
1337 | goto fail; | |||
1338 | varbind->avb_data.avb_uint32 = ax_pdutoh32(header, buf); | |||
1339 | return rread + 4; | |||
1340 | case AX_DATA_TYPE_COUNTER64: | |||
1341 | if (rawlen < 8) | |||
1342 | goto fail; | |||
1343 | varbind->avb_data.avb_uint64 = ax_pdutoh64(header, buf); | |||
1344 | return rread + 8; | |||
1345 | case AX_DATA_TYPE_OCTETSTRING: | |||
1346 | case AX_DATA_TYPE_IPADDRESS: | |||
1347 | case AX_DATA_TYPE_OPAQUE: | |||
1348 | nread = ax_pdutoostring(header, | |||
1349 | &(varbind->avb_data.avb_ostring), buf, rawlen); | |||
1350 | if (nread == -1) | |||
1351 | return -1; | |||
1352 | return nread + rread; | |||
1353 | case AX_DATA_TYPE_OID: | |||
1354 | nread = ax_pdutooid(header, &(varbind->avb_data.avb_oid), | |||
1355 | buf, rawlen); | |||
1356 | if (nread == -1) | |||
1357 | return -1; | |||
1358 | return nread + rread; | |||
1359 | case AX_DATA_TYPE_NULL: | |||
1360 | case AX_DATA_TYPE_NOSUCHOBJECT: | |||
1361 | case AX_DATA_TYPE_NOSUCHINSTANCE: | |||
1362 | case AX_DATA_TYPE_ENDOFMIBVIEW: | |||
1363 | return rread; | |||
1364 | } | |||
1365 | ||||
1366 | fail: | |||
1367 | errno(*__errno()) = EPROTO95; | |||
1368 | return -1; | |||
1369 | } |