GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/relayd/agentx.c Lines: 0 423 0.0 %
Date: 2017-11-07 Branches: 0 288 0.0 %

Line Branch Exec Source
1
/*      $OpenBSD: agentx.c,v 1.14 2017/05/28 10:39:15 benno Exp $    */
2
/*
3
 * Copyright (c) 2013,2014 Bret Stephen Lambert <blambert@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
18
#include <sys/types.h>
19
#include <sys/socket.h>
20
#include <sys/queue.h>
21
#include <sys/un.h>
22
23
#include <errno.h>
24
#include <stdlib.h>
25
#include <stdio.h>
26
#include <string.h>
27
#include <unistd.h>
28
29
#include "snmp.h"
30
31
int	snmp_agentx_octetstring(struct agentx_pdu *, char *, int);
32
int	snmp_agentx_buffercheck(struct agentx_pdu *, size_t);
33
int	snmp_agentx_oid(struct agentx_pdu *, struct snmp_oid *);
34
int	snmp_agentx_buffer_consume(struct agentx_pdu *, u_int);
35
int	snmp_agentx_int(struct agentx_pdu *, uint32_t *);
36
int	snmp_agentx_int64(struct agentx_pdu *, uint64_t *);
37
int	snmp_agentx_do_read_raw(struct agentx_pdu *, void *, int, int);
38
void	snmp_agentx_update_ids(struct agentx_handle *, struct agentx_pdu *);
39
struct agentx_pdu *
40
	agentx_find_inflight(struct agentx_handle *, uint32_t, uint32_t);
41
int	snmp_agentx_do_read_oid(struct agentx_pdu *, struct snmp_oid *, int *);
42
43
#ifdef DEBUG
44
static void	snmp_agentx_dump_hdr(struct agentx_hdr *);
45
#endif
46
47
#define PDU_BUFLEN 256
48
49
/* snmpTrapOid.0 */
50
struct snmp_oid trapoid_0 = {
51
	.o_id =	{ 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 },
52
	.o_n = 11
53
};
54
55
/*
56
 * AgentX handle allocation and management routines.
57
 */
58
59
struct agentx_handle *
60
snmp_agentx_alloc(int s)
61
{
62
	struct agentx_handle *h;
63
64
	if ((h = calloc(1, sizeof(*h))) == NULL)
65
		return (NULL);
66
67
	h->fd = s;
68
	h->timeout = AGENTX_DEFAULT_TIMEOUT;
69
70
	TAILQ_INIT(&h->w);
71
	TAILQ_INIT(&h->inflight);
72
73
	return (h);
74
}
75
76
/*
77
 * Synchronous open of unix socket path.
78
 */
79
struct agentx_handle *
80
snmp_agentx_open(const char *path, char *descr, struct snmp_oid *oid)
81
{
82
	struct sockaddr_un	 sun;
83
	struct agentx_handle	*h;
84
	int s;
85
86
	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
87
		return (NULL);
88
89
	bzero(&sun, sizeof(sun));
90
	sun.sun_family = AF_UNIX;
91
	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
92
	    sizeof(sun.sun_path))
93
		goto fail;
94
95
	if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1)
96
		goto fail;
97
98
	if ((h = snmp_agentx_fdopen(s, descr, oid)) == NULL)
99
		goto fail;
100
101
	return (h);
102
 fail:
103
	close(s);
104
	return (NULL);
105
}
106
107
/*
108
 * Synchronous AgentX open operation over previously-opened socket.
109
 */
110
struct agentx_handle *
111
snmp_agentx_fdopen(int s, char *descr, struct snmp_oid *oid)
112
{
113
	struct agentx_handle	*h;
114
	struct agentx_pdu	*pdu = NULL;
115
116
	if ((h = snmp_agentx_alloc(s)) == NULL)
117
		return (NULL);
118
119
	if ((pdu = snmp_agentx_open_pdu(h, descr, oid)) == NULL ||
120
	    (pdu = snmp_agentx_request(h, pdu)) == NULL ||
121
	    snmp_agentx_open_response(h, pdu) == -1) {
122
		if (pdu)
123
			snmp_agentx_pdu_free(pdu);
124
		snmp_agentx_free(h);
125
		return (NULL);
126
	}
127
128
	return (h);
129
}
130
131
/*
132
 * Synchronous close of agentx handle.
133
 */
134
int
135
snmp_agentx_close(struct agentx_handle *h, uint8_t reason)
136
{
137
	struct agentx_pdu			*pdu;
138
	int					 error = 0;
139
140
	if ((pdu = snmp_agentx_close_pdu(h, reason)) == NULL)
141
		return (-1);
142
	if ((pdu = snmp_agentx_request(h, pdu)) == NULL)
143
		return (-1);
144
	if (snmp_agentx_response(h, pdu) == -1)
145
		error = -1;
146
147
	snmp_agentx_pdu_free(pdu);
148
149
	return (error);
150
}
151
152
void
153
snmp_agentx_free(struct agentx_handle *h)
154
{
155
	struct agentx_pdu *pdu;
156
157
	if (h->fd != -1)
158
		close(h->fd);
159
160
	while ((pdu = TAILQ_FIRST(&h->w))) {
161
		TAILQ_REMOVE(&h->w, pdu, entry);
162
		snmp_agentx_pdu_free(pdu);
163
	}
164
	while ((pdu = TAILQ_FIRST(&h->inflight))) {
165
		TAILQ_REMOVE(&h->w, pdu, entry);
166
		snmp_agentx_pdu_free(pdu);
167
	}
168
	if (h->r)
169
		snmp_agentx_pdu_free(h->r);
170
171
	free(h);
172
}
173
174
/*
175
 * AgentX pdu allocation routines.
176
 */
177
178
/*
179
 * Allocate an AgentX PDU.
180
 */
181
struct agentx_pdu *
182
snmp_agentx_pdu_alloc(void)
183
{
184
	struct agentx_pdu	*pdu;
185
186
	if ((pdu = calloc(1, sizeof(*pdu))) == NULL)
187
		return (NULL);
188
	if ((pdu->buffer = calloc(PDU_BUFLEN, sizeof(uint8_t))) == NULL) {
189
		free(pdu);
190
		return (NULL);
191
	}
192
193
	pdu->buflen = PDU_BUFLEN;
194
195
	bzero(pdu->buffer, pdu->buflen);
196
	pdu->ptr = pdu->buffer + sizeof(struct agentx_hdr);
197
	pdu->ioptr = pdu->buffer;
198
	pdu->hdr = (struct agentx_hdr *)pdu->buffer;
199
	pdu->hdr->version = AGENTX_VERSION;
200
	pdu->hdr->flags = AGENTX_LOCAL_BYTE_ORDER_FLAG;
201
	pdu->hdr->reserved = 0;
202
	pdu->hdr->length = 0;
203
	pdu->datalen = sizeof(struct agentx_hdr);
204
205
	return (pdu);
206
}
207
208
/*
209
 * Read the response PDU for a generic operation.
210
 */
211
int
212
snmp_agentx_response(struct agentx_handle *h, struct agentx_pdu *pdu)
213
{
214
	struct agentx_response_data resp;
215
216
	if (snmp_agentx_read_raw(pdu, &resp, sizeof(resp)) == -1)
217
		return (-1);
218
219
	if (!snmp_agentx_byteorder_native(pdu->hdr)) {
220
		resp.error = snmp_agentx_int16_byteswap(resp.error);
221
		resp.index = snmp_agentx_int16_byteswap(resp.index);
222
	}
223
224
	h->error = resp.error;
225
	if (resp.error != AGENTX_ERR_NONE)
226
		return (-1);
227
228
	return (0);
229
}
230
231
/*
232
 * Read the response PDU for an open operation.
233
 */
234
int
235
snmp_agentx_open_response(struct agentx_handle *h, struct agentx_pdu *pdu)
236
{
237
238
	if (snmp_agentx_response(h, pdu) == -1)
239
		return (-1);
240
241
	h->sessionid = pdu->hdr->sessionid;
242
	return (0);
243
}
244
245
void
246
snmp_agentx_pdu_free(struct agentx_pdu *pdu)
247
{
248
	free(pdu->buffer);
249
	free(pdu->request);
250
	free(pdu);
251
}
252
253
int
254
snmp_agentx_buffer_consume(struct agentx_pdu *b, u_int len)
255
{
256
	int padding;
257
258
	padding = ((len + 3) & ~0x03) - len;
259
260
	if (b->datalen < (len + padding))
261
		return (-1);
262
263
	b->datalen -= len + padding;
264
	b->ptr += len + padding;
265
266
	return (0);
267
}
268
269
/*
270
 * Send an AgentX PDU. Flushes any already-enqueued PDUs.
271
 */
272
int
273
snmp_agentx_send(struct agentx_handle *h, struct agentx_pdu *pdu)
274
{
275
	ssize_t n;
276
277
	/* set the appropriate IDs in the protocol header */
278
	if (pdu != NULL &&
279
	    (pdu->datalen == pdu->hdr->length + sizeof(struct agentx_hdr))) {
280
		pdu->hdr->sessionid = h->sessionid;
281
282
		if (pdu->hdr->type != AGENTX_RESPONSE) {
283
			++h->transactid;
284
			++h->packetid;
285
		}
286
287
		pdu->hdr->transactid = h->transactid;
288
		pdu->hdr->packetid = h->packetid;
289
		TAILQ_INSERT_TAIL(&h->w, pdu, entry);
290
	}
291
292
 again:
293
	if ((pdu = TAILQ_FIRST(&h->w)) == NULL)
294
		return (0);
295
296
	if ((n = send(h->fd, pdu->ioptr, pdu->datalen, 0)) == -1)
297
		return (-1);
298
299
	pdu->ioptr += n;
300
	pdu->datalen -= n;
301
302
	if (pdu->datalen > 0) {
303
		errno = EAGAIN;
304
		return (-1);
305
	}
306
307
#ifdef DEBUG
308
	snmp_agentx_dump_hdr(pdu->hdr);
309
#endif
310
311
	TAILQ_REMOVE(&h->w, pdu, entry);
312
	TAILQ_INSERT_TAIL(&h->inflight, pdu, entry);
313
314
	goto again;
315
}
316
317
/*
318
 * Attempt to read a single AgentX PDU.
319
 */
320
struct agentx_pdu *
321
snmp_agentx_recv(struct agentx_handle *h)
322
{
323
	struct agentx_pdu *pdu, *match;
324
	ssize_t n;
325
326
	h->error = AGENTX_ERR_NONE;
327
328
	if (h->r == NULL) {
329
		if ((h->r = snmp_agentx_pdu_alloc()) == NULL)
330
			return (NULL);
331
		h->r->datalen = 0;	/* XXX force this for receive buffers */
332
	}
333
	pdu = h->r;
334
335
	if (snmp_agentx_buffercheck(pdu, sizeof(struct agentx_hdr)) == -1)
336
		return (NULL);
337
338
	/* read header */
339
	if (pdu->datalen < sizeof(struct agentx_hdr)) {
340
		n = recv(h->fd, pdu->ioptr, sizeof(struct agentx_hdr), 0);
341
342
		if (n == 0 || n == -1)
343
			return (NULL);
344
345
		pdu->datalen += n;
346
		pdu->ioptr += n;
347
348
		if (pdu->datalen < sizeof(struct agentx_hdr)) {
349
			errno = EAGAIN;
350
			return (NULL);
351
		}
352
353
		if (pdu->hdr->version != AGENTX_VERSION) {
354
			h->error = AGENTX_ERR_PARSE_ERROR;
355
			return (NULL);
356
		}
357
358
		if (snmp_agentx_buffercheck(pdu, pdu->hdr->length) == -1)
359
			return (NULL);
360
	}
361
362
	/* read body */
363
	if (pdu->hdr->length > 0) {
364
		n = recv(h->fd, pdu->ioptr, pdu->hdr->length, 0);
365
366
		if (n == 0 || n == -1)
367
			return (NULL);
368
369
		pdu->datalen += n;
370
		pdu->ioptr += n;
371
	}
372
373
	if (pdu->datalen < pdu->hdr->length + sizeof(struct agentx_hdr)) {
374
		errno = EAGAIN;
375
		return (NULL);
376
	}
377
378
	if (pdu->hdr->version != AGENTX_VERSION) {
379
		h->error = AGENTX_ERR_PARSE_ERROR;
380
		goto fail;
381
	}
382
383
	/* If this is an open on a new connection, fix it up */
384
	if (pdu->hdr->type == AGENTX_OPEN && h->sessionid == 0) {
385
		pdu->hdr->sessionid = 0;		/* ignored, per RFC */
386
		h->transactid = pdu->hdr->transactid;
387
		h->packetid = pdu->hdr->packetid;
388
	}
389
390
	if (pdu->hdr->type == AGENTX_RESPONSE) {
391
392
		match = agentx_find_inflight(h, pdu->hdr->transactid,
393
		    pdu->hdr->packetid);
394
		if (match == NULL) {
395
			errno = ESRCH;		/* XXX */
396
			goto fail;
397
		}
398
399
		TAILQ_REMOVE(&h->inflight, match, entry);
400
		pdu->request = match;
401
		h->r = NULL;
402
403
	} else {
404
		if (pdu->hdr->sessionid != h->sessionid) {
405
			h->error = AGENTX_ERR_NOT_OPEN;
406
			goto fail;
407
		}
408
409
		if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) {
410
			h->error = AGENTX_ERR_UNSUPPORTED_CONTEXT;
411
			goto fail;
412
		}
413
414
		snmp_agentx_update_ids(h, pdu);              /* XXX */
415
416
		if (pdu->datalen != pdu->hdr->length + sizeof(*pdu->hdr)) {
417
			h->error = AGENTX_ERR_PARSE_ERROR;
418
			goto fail;
419
		}
420
	}
421
422
#ifdef DEBUG
423
	snmp_agentx_dump_hdr(pdu->hdr);
424
#endif
425
	h->r = NULL;
426
427
	return (pdu);
428
429
 fail:
430
#ifdef DEBUG
431
	snmp_agentx_dump_hdr(pdu->hdr);
432
#endif
433
	snmp_agentx_pdu_free(pdu);
434
	h->r = NULL;
435
436
	return (NULL);
437
}
438
439
/*
440
 * Synchonous request and receipt of response.
441
 */
442
struct agentx_pdu *
443
snmp_agentx_request(struct agentx_handle *h, struct agentx_pdu *pdu)
444
{
445
446
	if (snmp_agentx_send(h, pdu) == -1) {
447
		if (errno != EAGAIN)
448
			return (NULL);
449
	}
450
	while (snmp_agentx_send(h, NULL) == -1) {
451
		if (errno != EAGAIN)
452
			return (NULL);
453
	}
454
	while ((pdu = snmp_agentx_recv(h)) == NULL) {
455
		if (errno != EAGAIN)
456
			return (NULL);
457
	}
458
	h->error = AGENTX_ERR_NONE;
459
460
	return (pdu);
461
}
462
463
struct agentx_pdu *
464
agentx_find_inflight(struct agentx_handle *h, uint32_t tid, uint32_t pid)
465
{
466
	struct agentx_pdu *pdu;
467
468
	TAILQ_FOREACH(pdu, &h->inflight, entry)
469
		if (pdu->hdr->transactid == tid && pdu->hdr->packetid == pid)
470
			break;
471
	return (pdu);
472
}
473
474
int
475
snmp_agentx_buffercheck(struct agentx_pdu *pdu, size_t len)
476
{
477
	uint8_t *newptr;
478
	size_t newlen;
479
480
	if (pdu->buflen - pdu->datalen >= len)
481
		return (0);
482
483
	newlen = pdu->buflen + len;
484
	if (newlen < pdu->buflen || newlen < len)
485
		return (-1);
486
487
	if ((newptr = realloc(pdu->buffer, newlen)) == NULL)
488
		return (-1);
489
490
	pdu->buflen = newlen;
491
	pdu->ioptr = &newptr[pdu->ioptr - pdu->buffer];
492
	pdu->buffer = newptr;
493
	pdu->hdr = (struct agentx_hdr *)pdu->buffer;
494
	pdu->ptr = &pdu->buffer[pdu->datalen];
495
496
	return (0);
497
}
498
499
/*
500
 * Utility routines for initializing common AgentX PDUs.
501
 */
502
503
struct agentx_pdu *
504
snmp_agentx_open_pdu(struct agentx_handle *h, char *descr,
505
    struct snmp_oid *oid)
506
{
507
	struct agentx_open_timeout to;
508
	struct snmp_oid nulloid;
509
	struct agentx_pdu *pdu;
510
511
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
512
		return (NULL);
513
514
	pdu->hdr->type = AGENTX_OPEN;
515
516
	if (oid == NULL) {
517
		bzero(&nulloid, sizeof(nulloid));
518
		oid = &nulloid;
519
	}
520
521
	bzero(&to, sizeof(to));
522
	to.timeout = AGENTX_DEFAULT_TIMEOUT;
523
524
	if (snmp_agentx_raw(pdu, &to, sizeof(to)) == -1 ||
525
	    snmp_agentx_oid(pdu, oid) == -1 ||
526
	    snmp_agentx_octetstring(pdu, descr, strlen(descr)) == -1)
527
		goto fail;
528
529
	return (pdu);
530
 fail:
531
	snmp_agentx_pdu_free(pdu);
532
	return (NULL);
533
}
534
535
struct agentx_pdu *
536
snmp_agentx_close_pdu(struct agentx_handle *h, uint8_t reason)
537
{
538
	struct agentx_close_request_data	 req;
539
	struct agentx_pdu			*pdu;
540
541
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
542
		return (NULL);
543
	pdu->hdr->type = AGENTX_CLOSE;
544
545
	bzero(&req, sizeof(req));
546
	req.reason = reason;
547
548
	if (snmp_agentx_raw(pdu, &req, sizeof(req)) == -1) {
549
		snmp_agentx_pdu_free(pdu);
550
		return (NULL);
551
	}
552
553
	return (pdu);
554
}
555
556
struct agentx_pdu *
557
snmp_agentx_notify_pdu(struct snmp_oid *oid)
558
{
559
	struct agentx_pdu	*pdu;
560
561
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
562
		return (NULL);
563
	pdu->hdr->type = AGENTX_NOTIFY;
564
565
	if (snmp_agentx_varbind(pdu, &trapoid_0,
566
	    AGENTX_OBJECT_IDENTIFIER, oid, sizeof(*oid)) == -1) {
567
		snmp_agentx_pdu_free(pdu);
568
		return (NULL);
569
	}
570
571
	return (pdu);
572
}
573
574
struct agentx_pdu *
575
snmp_agentx_response_pdu(int uptime, int error, int idx)
576
{
577
	struct agentx_response_data	 resp;
578
	struct agentx_pdu		*pdu;
579
580
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
581
		return (NULL);
582
	pdu->hdr->type = AGENTX_RESPONSE;
583
584
	resp.sysuptime = uptime;
585
	resp.error = error;
586
	resp.index = idx;
587
588
	if (snmp_agentx_raw(pdu, &resp, sizeof(resp)) == -1) {
589
		snmp_agentx_pdu_free(pdu);
590
		return (NULL);
591
	}
592
593
	return (pdu);
594
}
595
596
struct agentx_pdu *
597
snmp_agentx_ping_pdu(void)
598
{
599
	struct agentx_pdu *pdu;
600
601
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
602
		return (NULL);
603
	pdu->hdr->version = AGENTX_VERSION;
604
	pdu->hdr->type = AGENTX_PING;
605
606
	return (pdu);
607
}
608
609
struct agentx_pdu *
610
snmp_agentx_register_pdu(struct snmp_oid *oid, int timeout, int range_index,
611
    int range_bound)
612
{
613
	struct agentx_register_hdr	 rhdr;
614
	struct agentx_pdu		*pdu;
615
616
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
617
		return (NULL);
618
619
	pdu->hdr->version = AGENTX_VERSION;
620
	pdu->hdr->type = AGENTX_REGISTER;
621
622
	rhdr.timeout = timeout;
623
	rhdr.priority = AGENTX_REGISTER_PRIO_DEFAULT;
624
	rhdr.subrange = range_index;
625
	rhdr.reserved = 0;
626
627
	if (snmp_agentx_raw(pdu, &rhdr, sizeof(rhdr)) == -1 ||
628
	    snmp_agentx_oid(pdu, oid) == -1 ||
629
	    (range_index && snmp_agentx_int(pdu, &range_bound) == -1)) {
630
		snmp_agentx_pdu_free(pdu);
631
		return (NULL);
632
	}
633
634
	return (pdu);
635
}
636
637
struct agentx_pdu *
638
snmp_agentx_unregister_pdu(struct snmp_oid *oid, int range_index,
639
    int range_bound)
640
{
641
	struct agentx_unregister_hdr	 uhdr;
642
	struct agentx_pdu		*pdu;
643
644
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
645
		return (NULL);
646
647
	pdu->hdr->version = AGENTX_VERSION;
648
	pdu->hdr->type = AGENTX_UNREGISTER;
649
650
	uhdr.reserved1 = 0;
651
	uhdr.priority = AGENTX_REGISTER_PRIO_DEFAULT;
652
	uhdr.subrange = range_index;
653
	uhdr.reserved2 = 0;
654
655
	if (snmp_agentx_raw(pdu, &uhdr, sizeof(uhdr)) == -1 ||
656
	    snmp_agentx_oid(pdu, oid) == -1 ||
657
	    snmp_agentx_oid(pdu, oid) == -1 ||
658
	    (range_index && snmp_agentx_int(pdu, &range_bound) == -1)) {
659
		snmp_agentx_pdu_free(pdu);
660
		return (NULL);
661
	}
662
663
	return (pdu);
664
}
665
666
struct agentx_pdu *
667
snmp_agentx_get_pdu(struct snmp_oid oid[], int noid)
668
{
669
	struct snmp_oid		 nulloid;
670
	struct agentx_pdu	*pdu;
671
	int			 i;
672
673
	if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
674
		return (NULL);
675
676
	pdu->hdr->version = AGENTX_VERSION;
677
	pdu->hdr->type = AGENTX_GET;
678
679
	bzero(&nulloid, sizeof(nulloid));
680
681
	for (i = 0; i < noid; i++) {
682
		if (snmp_agentx_oid(pdu, &oid[i]) == -1 ||
683
		    snmp_agentx_oid(pdu, &nulloid) == -1) {
684
			snmp_agentx_pdu_free(pdu);
685
			return (NULL);
686
		}
687
	}
688
689
	return (pdu);
690
}
691
692
/*
693
 * AgentX PDU write routines.
694
 */
695
696
int
697
snmp_agentx_raw(struct agentx_pdu *pdu, void *data, int len)
698
{
699
700
	if (snmp_agentx_buffercheck(pdu, len) == -1)
701
		return (-1);
702
703
	memcpy(pdu->ptr, data, len);
704
705
	pdu->hdr->length += len;
706
	pdu->ptr += len;
707
	pdu->datalen += len;
708
709
	return (0);
710
}
711
712
int
713
snmp_agentx_int(struct agentx_pdu *pdu, uint32_t *i)
714
{
715
	return (snmp_agentx_raw(pdu, i, sizeof(*i)));
716
}
717
718
int
719
snmp_agentx_int64(struct agentx_pdu *pdu, uint64_t *i)
720
{
721
	return (snmp_agentx_raw(pdu, i, sizeof(*i)));
722
}
723
724
int
725
snmp_agentx_octetstring(struct agentx_pdu *pdu, char *str, int len)
726
{
727
	static uint8_t pad[4] = { 0, 0, 0, 0 };
728
	int padding;
729
	uint32_t l;
730
731
	padding = ((len + 3) & ~0x03) - len;
732
733
	l = len;
734
	if (snmp_agentx_int(pdu, &l) == -1 ||
735
	    snmp_agentx_raw(pdu, str, len) == -1 ||
736
	    snmp_agentx_raw(pdu, pad, padding) == -1)
737
		return (-1);
738
739
	return (0);
740
}
741
742
int
743
snmp_agentx_oid(struct agentx_pdu *pdu, struct snmp_oid *oid)
744
{
745
	struct agentx_oid_hdr ohdr;
746
	u_int i, prefix;
747
748
	i = prefix = 0;
749
750
	if (oid->o_id[0] == 1 && oid->o_id[1] == 3 &&
751
	    oid->o_id[2] == 6 && oid->o_id[3] == 1 &&
752
	    oid->o_id[4] < 256) {
753
		prefix = oid->o_id[4];
754
		i = 5;
755
	}
756
757
	if (prefix)
758
		ohdr.n_subid = oid->o_n - 5;
759
	else
760
		ohdr.n_subid = oid->o_n;
761
	ohdr.prefix = prefix;
762
	ohdr.include = 0;
763
	ohdr.reserved = 0;
764
765
	if (snmp_agentx_raw(pdu, &ohdr, sizeof(ohdr)) == -1)
766
		return (-1);
767
768
	for (; i < oid->o_n; i++)
769
		if (snmp_agentx_int(pdu, &oid->o_id[i]) == -1)
770
			return (-1);
771
772
	return (0);
773
}
774
775
int
776
snmp_agentx_varbind(struct agentx_pdu *pdu, struct snmp_oid *oid, int type,
777
    void *data, int len)
778
{
779
	struct agentx_varbind_hdr vbhdr;
780
781
	vbhdr.type = type;
782
	vbhdr.reserved = 0;
783
	if (snmp_agentx_raw(pdu, &vbhdr, sizeof(vbhdr)) == -1)
784
		return (-1);
785
786
	if (snmp_agentx_oid(pdu, oid) == -1)
787
		return (-1);
788
789
	switch (type) {
790
791
	case AGENTX_NO_SUCH_OBJECT:
792
	case AGENTX_NO_SUCH_INSTANCE:
793
	case AGENTX_END_OF_MIB_VIEW:
794
	case AGENTX_NULL:
795
		/* no data follows the OID */
796
		return (0);
797
798
	case AGENTX_IP_ADDRESS:
799
	case AGENTX_OPAQUE:
800
	case AGENTX_OCTET_STRING:
801
		return (snmp_agentx_octetstring(pdu, data, len));
802
803
	case AGENTX_OBJECT_IDENTIFIER:
804
		return (snmp_agentx_oid(pdu, (struct snmp_oid *)data));
805
806
	case AGENTX_INTEGER:
807
	case AGENTX_COUNTER32:
808
	case AGENTX_GAUGE32:
809
	case AGENTX_TIME_TICKS:
810
		return (snmp_agentx_int(pdu, (uint32_t *)data));
811
812
	case AGENTX_COUNTER64:
813
		return (snmp_agentx_int64(pdu, (uint64_t *)data));
814
815
	default:
816
		return (-1);
817
	}
818
	/* NOTREACHED */
819
}
820
821
/*
822
 * AgentX PDU read routines.
823
 */
824
825
int
826
snmp_agentx_read_vbhdr(struct agentx_pdu *pdu,
827
    struct agentx_varbind_hdr *vbhdr)
828
{
829
	if (snmp_agentx_read_raw(pdu, vbhdr, sizeof(*vbhdr)) == -1)
830
		return (-1);
831
	if (!snmp_agentx_byteorder_native(pdu->hdr))
832
		vbhdr->type = snmp_agentx_int16_byteswap(vbhdr->type);
833
	return (0);
834
}
835
836
int
837
snmp_agentx_copy_raw(struct agentx_pdu *pdu, void *v, int len)
838
{
839
	return (snmp_agentx_do_read_raw(pdu, v, len, 0));
840
}
841
842
int
843
snmp_agentx_read_raw(struct agentx_pdu *pdu, void *v, int len)
844
{
845
	return (snmp_agentx_do_read_raw(pdu, v, len, 1));
846
}
847
848
int
849
snmp_agentx_do_read_raw(struct agentx_pdu *pdu, void *v, int len, int consume)
850
{
851
	void *ptr = pdu->ptr;
852
853
	if (consume)
854
		if (snmp_agentx_buffer_consume(pdu, len) == -1)
855
			return (-1);
856
857
	memcpy(v, ptr, len);
858
859
	return (0);
860
}
861
862
int
863
snmp_agentx_read_int(struct agentx_pdu *pdu, uint32_t *i)
864
{
865
	if (snmp_agentx_read_raw(pdu, i, sizeof(*i)) == -1)
866
		return (-1);
867
	if (!snmp_agentx_byteorder_native(pdu->hdr))
868
		*i = snmp_agentx_int_byteswap(*i);
869
	return (0);
870
}
871
872
int
873
snmp_agentx_read_int64(struct agentx_pdu *pdu, uint64_t *i)
874
{
875
	if (snmp_agentx_read_raw(pdu, i, sizeof(*i)) == -1)
876
		return (-1);
877
	if (!snmp_agentx_byteorder_native(pdu->hdr))
878
		*i = snmp_agentx_int64_byteswap(*i);
879
	return (0);
880
}
881
882
int
883
snmp_agentx_read_oid(struct agentx_pdu *pdu, struct snmp_oid *oid)
884
{
885
	int dummy;
886
887
	return (snmp_agentx_do_read_oid(pdu, oid, &dummy));
888
}
889
890
int
891
snmp_agentx_do_read_oid(struct agentx_pdu *pdu, struct snmp_oid *oid,
892
    int *include)
893
{
894
	struct agentx_oid_hdr ohdr;
895
	int i = 0;
896
897
	if (snmp_agentx_read_raw(pdu, &ohdr, sizeof(ohdr)) == -1)
898
		return (-1);
899
900
	bzero(oid, sizeof(*oid));
901
902
	if (ohdr.prefix != 0) {
903
		oid->o_id[0] = 1;
904
		oid->o_id[1] = 3;
905
		oid->o_id[2] = 6;
906
		oid->o_id[3] = 1;
907
		oid->o_id[4] = ohdr.prefix;
908
		i = 5;
909
	}
910
911
	while (ohdr.n_subid--)
912
		if (snmp_agentx_read_int(pdu, &oid->o_id[i++]) == -1)
913
			return (-1);
914
915
	oid->o_n = i;
916
	*include = ohdr.include;
917
918
	return (0);
919
}
920
921
int
922
snmp_agentx_read_searchrange(struct agentx_pdu *pdu,
923
    struct agentx_search_range *sr)
924
{
925
	if (snmp_agentx_do_read_oid(pdu, &sr->start, &sr->include) == -1 ||
926
	    snmp_agentx_read_oid(pdu, &sr->end) == -1)
927
		return (-1);
928
929
	return (0);
930
}
931
932
char *
933
snmp_agentx_read_octetstr(struct agentx_pdu *pdu, int *len)
934
{
935
	char *str;
936
	uint32_t l;
937
938
	if (snmp_agentx_read_int(pdu, &l) == -1)
939
		return (NULL);
940
941
	if ((str = malloc(l)) == NULL)
942
		return (NULL);
943
944
	if (snmp_agentx_read_raw(pdu, str, l) == -1) {
945
		free(str);
946
		return (NULL);
947
	}
948
	*len = l;
949
950
	return (str);
951
}
952
953
/*
954
 * Synchronous AgentX calls.
955
 */
956
957
int
958
snmp_agentx_ping(struct agentx_handle *h)
959
{
960
	struct agentx_pdu	*pdu;
961
	int			 error = 0;
962
963
	if ((pdu = snmp_agentx_ping_pdu()) == NULL)
964
		return (-1);
965
966
	/* Attaches the pdu to the handle; will be released later */
967
	if ((pdu = snmp_agentx_request(h, pdu)) == NULL)
968
		return (-1);
969
970
	if (snmp_agentx_response(h, pdu) == -1)
971
		error = -1;
972
	snmp_agentx_pdu_free(pdu);
973
974
	return (error);
975
}
976
977
/*
978
 * Internal utility functions.
979
 */
980
981
void
982
snmp_agentx_update_ids(struct agentx_handle *h, struct agentx_pdu *pdu)
983
{
984
	/* XXX -- update to reflect the new queueing semantics */
985
	h->transactid = pdu->hdr->transactid;
986
	h->packetid = pdu->hdr->packetid;
987
}
988
989
char *
990
snmp_oid2string(struct snmp_oid *o, char *buf, size_t len)
991
{
992
	char		 str[256];
993
	size_t		 i;
994
995
	bzero(buf, len);
996
997
	for (i = 0; i < o->o_n; i++) {
998
		snprintf(str, sizeof(str), "%u", o->o_id[i]);
999
		strlcat(buf, str, len);
1000
		if (i < (o->o_n - 1))
1001
			strlcat(buf, ".", len);
1002
	}
1003
1004
	return (buf);
1005
}
1006
1007
int
1008
snmp_oid_cmp(struct snmp_oid *a, struct snmp_oid *b)
1009
{
1010
	size_t		i;
1011
1012
	for (i = 0; i < SNMP_MAX_OID_LEN; i++) {
1013
		if (a->o_id[i] != 0) {
1014
			if (a->o_id[i] == b->o_id[i])
1015
				continue;
1016
			else if (a->o_id[i] < b->o_id[i]) {
1017
				/* b is a successor of a */
1018
				return (1);
1019
			} else {
1020
				/* b is a predecessor of a */
1021
				return (-1);
1022
			}
1023
		} else if (b->o_id[i] != 0) {
1024
			/* b is larger, but a child of a */
1025
			return (2);
1026
		} else
1027
			break;
1028
	}
1029
1030
	/* b and a are identical */
1031
	return (0);
1032
}
1033
1034
void
1035
snmp_oid_increment(struct snmp_oid *o)
1036
{
1037
	u_int		i;
1038
1039
	for (i = o->o_n; i > 0; i--) {
1040
		o->o_id[i - 1]++;
1041
		if (o->o_id[i - 1] != 0)
1042
			break;
1043
	}
1044
}
1045
1046
char *
1047
snmp_agentx_type2name(int type)
1048
{
1049
	static char *names[] = {
1050
		"AGENTX_OPEN",
1051
		"AGENTX_CLOSE",
1052
		"AGENTX_REGISTER",
1053
		"AGENTX_UNREGISTER",
1054
		"AGENTX_GET",
1055
		"AGENTX_GET_NEXT",
1056
		"AGENTX_GET_BULK",
1057
		"AGENTX_TEST_SET",
1058
		"AGENTX_COMMIT_SET",
1059
		"AGENTX_UNDO_SET",
1060
		"AGENTX_CLEANUP_SET",
1061
		"AGENTX_NOTIFY",
1062
		"AGENTX_PING",
1063
		"AGENTX_INDEX_ALLOCATE",
1064
		"AGENTX_INDEX_DEALLOCATE",
1065
		"AGENTX_ADD_AGENT_CAPS",
1066
		"AGENTX_REMOVE_AGENT_CAPS",
1067
		"AGENTX_RESPONSE"
1068
	};
1069
1070
	if (type > 18)
1071
		return ("unknown");
1072
1073
	return (names[type - 1]);
1074
}
1075
1076
#ifdef DEBUG
1077
static void
1078
snmp_agentx_dump_hdr(struct agentx_hdr *hdr)
1079
{
1080
	if (hdr == NULL) {
1081
		printf("NULL\n");
1082
		return;
1083
	}
1084
1085
	fprintf(stderr,
1086
	    "agentx: version %d type %s flags %d reserved %d"
1087
	    " sessionid %d transactid %d packetid %d length %d",
1088
	    hdr->version, snmp_agentx_type2name(hdr->type), hdr->flags,
1089
	    hdr->reserved, hdr->sessionid, hdr->transactid,
1090
	    hdr->packetid, hdr->length);
1091
1092
	if (hdr->type == AGENTX_RESPONSE) {
1093
		struct agentx_response *r = (struct agentx_response *)hdr;
1094
1095
		fprintf(stderr, " sysuptime %d error %d index %d",
1096
		    r->data.sysuptime, r->data.error, r->data.index);
1097
	}
1098
1099
	fprintf(stderr, "\n");
1100
}
1101
#endif