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

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