snmp.c revision 122394
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution of this software and documentation and use in source and
9 * binary forms, with or without modification, are permitted provided that
10 * the following conditions are met:
11 *
12 * 1. Redistributions of source code or documentation must retain the above
13 *    copyright notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
25 * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Begemot: bsnmp/lib/snmp.c,v 1.34 2003/01/28 13:44:34 hbb Exp $
34 *
35 * SNMP
36 */
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <stddef.h>
42#include <stdarg.h>
43#include <string.h>
44#include <ctype.h>
45#include <netdb.h>
46#include <errno.h>
47
48#include "asn1.h"
49#include "snmp.h"
50#include "snmppriv.h"
51
52static void snmp_error_func(const char *, ...);
53static void snmp_printf_func(const char *, ...);
54
55void (*snmp_error)(const char *, ...) = snmp_error_func;
56void (*snmp_printf)(const char *, ...) = snmp_printf_func;
57
58
59/*
60 * Get the next variable binding from the list.
61 * ASN errors on the sequence or the OID are always fatal.
62 */
63static enum asn_err
64get_var_binding(struct asn_buf *b, struct snmp_value *binding)
65{
66	u_char type;
67	asn_len_t len, trailer;
68	enum asn_err err;
69
70	if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
71		snmp_error("cannot parse varbind header");
72		return (ASN_ERR_FAILED);
73	}
74
75	/* temporary truncate the length so that the parser does not
76	 * eat up bytes behind the sequence in the case the encoding is
77	 * wrong of inner elements. */
78	trailer = b->asn_len - len;
79	b->asn_len = len;
80
81	if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) {
82		snmp_error("cannot parse binding objid");
83		return (ASN_ERR_FAILED);
84	}
85	if (asn_get_header(b, &type, &len) != ASN_ERR_OK) {
86		snmp_error("cannot parse binding value header");
87		return (ASN_ERR_FAILED);
88	}
89
90	switch (type) {
91
92	  case ASN_TYPE_NULL:
93		binding->syntax = SNMP_SYNTAX_NULL;
94		err = asn_get_null_raw(b, len);
95		break;
96
97	  case ASN_TYPE_INTEGER:
98		binding->syntax = SNMP_SYNTAX_INTEGER;
99		err = asn_get_integer_raw(b, len, &binding->v.integer);
100		break;
101
102	  case ASN_TYPE_OCTETSTRING:
103		binding->syntax = SNMP_SYNTAX_OCTETSTRING;
104		binding->v.octetstring.octets = malloc(len);
105		if (binding->v.octetstring.octets == NULL) {
106			snmp_error("%s", strerror(errno));
107			return (ASN_ERR_FAILED);
108		}
109		binding->v.octetstring.len = len;
110		err = asn_get_octetstring_raw(b, len,
111		    binding->v.octetstring.octets,
112		    &binding->v.octetstring.len);
113		if (ASN_ERR_STOPPED(err)) {
114			free(binding->v.octetstring.octets);
115			binding->v.octetstring.octets = NULL;
116		}
117		break;
118
119	  case ASN_TYPE_OBJID:
120		binding->syntax = SNMP_SYNTAX_OID;
121		err = asn_get_objid_raw(b, len, &binding->v.oid);
122		break;
123
124	  case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS:
125		binding->syntax = SNMP_SYNTAX_IPADDRESS;
126		err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress);
127		break;
128
129	  case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS:
130		binding->syntax = SNMP_SYNTAX_TIMETICKS;
131		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
132		break;
133
134	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER:
135		binding->syntax = SNMP_SYNTAX_COUNTER;
136		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
137		break;
138
139	  case ASN_CLASS_APPLICATION|ASN_APP_GAUGE:
140		binding->syntax = SNMP_SYNTAX_GAUGE;
141		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
142		break;
143
144	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64:
145		binding->syntax = SNMP_SYNTAX_COUNTER64;
146		err = asn_get_counter64_raw(b, len, &binding->v.counter64);
147		break;
148
149	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT:
150		binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT;
151		err = asn_get_null_raw(b, len);
152		break;
153
154	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE:
155		binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
156		err = asn_get_null_raw(b, len);
157		break;
158
159	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW:
160		binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
161		err = asn_get_null_raw(b, len);
162		break;
163
164	  default:
165		if ((err = asn_skip(b, len)) == ASN_ERR_OK)
166			err = ASN_ERR_TAG;
167		snmp_error("bad binding value type 0x%x", type);
168		break;
169	}
170
171	if (ASN_ERR_STOPPED(err)) {
172		snmp_error("cannot parse binding value");
173		return (err);
174	}
175
176	if (b->asn_len != 0)
177		snmp_error("ignoring junk at end of binding");
178
179	b->asn_len = trailer;
180
181	return (err);
182}
183
184/*
185 * Parse the different PDUs contents. Any ASN error in the outer components
186 * are fatal. Only errors in variable values may be tolerated. If all
187 * components can be parsed it returns either ASN_ERR_OK or the first
188 * error that was found.
189 */
190enum asn_err
191snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
192{
193	if (pdu->type == SNMP_PDU_TRAP) {
194		if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) {
195			snmp_error("cannot parse trap enterprise");
196			return (ASN_ERR_FAILED);
197		}
198		if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) {
199			snmp_error("cannot parse trap agent address");
200			return (ASN_ERR_FAILED);
201		}
202		if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) {
203			snmp_error("cannot parse 'generic-trap'");
204			return (ASN_ERR_FAILED);
205		}
206		if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) {
207			snmp_error("cannot parse 'specific-trap'");
208			return (ASN_ERR_FAILED);
209		}
210		if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) {
211			snmp_error("cannot parse trap 'time-stamp'");
212			return (ASN_ERR_FAILED);
213		}
214	} else {
215		if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) {
216			snmp_error("cannot parse 'request-id'");
217			return (ASN_ERR_FAILED);
218		}
219		if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) {
220			snmp_error("cannot parse 'error_status'");
221			return (ASN_ERR_FAILED);
222		}
223		if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) {
224			snmp_error("cannot parse 'error_index'");
225			return (ASN_ERR_FAILED);
226		}
227	}
228
229	if (asn_get_sequence(b, lenp) != ASN_ERR_OK) {
230		snmp_error("cannot get varlist header");
231		return (ASN_ERR_FAILED);
232	}
233
234	return (ASN_ERR_OK);
235}
236
237static enum asn_err
238parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
239{
240	asn_len_t len, trailer;
241	struct snmp_value *v;
242	enum asn_err err, err1;
243
244	err = snmp_parse_pdus_hdr(b, pdu, &len);
245	if (ASN_ERR_STOPPED(err))
246		return (err);
247
248	trailer = b->asn_len - len;
249
250	v = pdu->bindings;
251	err = ASN_ERR_OK;
252	while (b->asn_len != 0) {
253		if (pdu->nbindings == SNMP_MAX_BINDINGS) {
254			snmp_error("too many bindings (> %u) in PDU",
255			    SNMP_MAX_BINDINGS);
256			return (ASN_ERR_FAILED);
257		}
258		err1 = get_var_binding(b, v);
259		if (ASN_ERR_STOPPED(err1))
260			return (ASN_ERR_FAILED);
261		if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) {
262			err = err1;
263			*ip = pdu->nbindings + 1;
264		}
265		pdu->nbindings++;
266		v++;
267	}
268
269	b->asn_len = trailer;
270
271	return (err);
272}
273
274/*
275 * Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'.
276 */
277enum asn_err
278snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
279{
280	int32_t version;
281	u_char type;
282	u_int comm_len;
283
284	if (asn_get_integer(b, &version) != ASN_ERR_OK) {
285		snmp_error("cannot decode version");
286		return (ASN_ERR_FAILED);
287	}
288
289	if (version == 0) {
290		pdu->version = SNMP_V1;
291	} else if (version == 1) {
292		pdu->version = SNMP_V2c;
293	} else {
294		pdu->version = SNMP_Verr;
295		snmp_error("unsupported SNMP version");
296		return (ASN_ERR_TAG);
297	}
298
299	comm_len = SNMP_COMMUNITY_MAXLEN;
300	if (asn_get_octetstring(b, (u_char *)pdu->community,
301	    &comm_len) != ASN_ERR_OK) {
302		snmp_error("cannot decode community");
303		return (ASN_ERR_FAILED);
304	}
305	pdu->community[comm_len] = '\0';
306
307	if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) {
308		snmp_error("cannot get pdu header");
309		return (ASN_ERR_FAILED);
310	}
311	if ((type & ~ASN_TYPE_MASK) !=
312	    (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
313		snmp_error("bad pdu header tag");
314		return (ASN_ERR_FAILED);
315	}
316	pdu->type = type & ASN_TYPE_MASK;
317
318	switch (pdu->type) {
319
320	  case SNMP_PDU_GET:
321	  case SNMP_PDU_GETNEXT:
322	  case SNMP_PDU_RESPONSE:
323	  case SNMP_PDU_SET:
324		break;
325
326	  case SNMP_PDU_TRAP:
327		if (pdu->version != SNMP_V1) {
328			snmp_error("bad pdu type %u", pdu->type);
329			return (ASN_ERR_FAILED);
330		}
331		break;
332
333	  case SNMP_PDU_GETBULK:
334	  case SNMP_PDU_INFORM:
335	  case SNMP_PDU_TRAP2:
336	  case SNMP_PDU_REPORT:
337		if (pdu->version == SNMP_V1) {
338			snmp_error("bad pdu type %u", pdu->type);
339			return (ASN_ERR_FAILED);
340		}
341		break;
342
343	  default:
344		snmp_error("bad pdu type %u", pdu->type);
345		return (ASN_ERR_FAILED);
346	}
347
348
349	if (*lenp > b->asn_len) {
350		snmp_error("pdu length too long");
351		return (ASN_ERR_FAILED);
352	}
353
354	return (ASN_ERR_OK);
355}
356
357static enum asn_err
358parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
359{
360	enum asn_err err;
361	asn_len_t len, trailer;
362
363	err = snmp_parse_message_hdr(b, pdu, &len);
364	if (ASN_ERR_STOPPED(err))
365		return (err);
366
367	trailer = b->asn_len - len;
368	b->asn_len = len;
369
370	err = parse_pdus(b, pdu, ip);
371	if (ASN_ERR_STOPPED(err))
372		return (ASN_ERR_FAILED);
373
374	if (b->asn_len != 0)
375		snmp_error("ignoring trailing junk after pdu");
376
377	b->asn_len = trailer;
378
379	return (err);
380}
381
382/*
383 * Decode the PDU except for the variable bindings itself.
384 * If decoding fails because of a bad binding, but the rest can be
385 * decoded, ip points to the index of the failed variable (errors
386 * OORANGE, BADLEN or BADVERS).
387 */
388enum snmp_code
389snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
390{
391	asn_len_t len;
392
393	memset(pdu, 0, sizeof(*pdu));
394
395	if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
396		snmp_error("cannot decode pdu header");
397		return (SNMP_CODE_FAILED);
398	}
399	if (b->asn_len < len) {
400		snmp_error("outer sequence value too short");
401		return (SNMP_CODE_FAILED);
402	}
403	if (b->asn_len != len) {
404		snmp_error("ignoring trailing junk in message");
405		b->asn_len = len;
406	}
407
408	switch (parse_message(b, pdu, ip)) {
409
410	  case ASN_ERR_OK:
411		return (SNMP_CODE_OK);
412
413	  case ASN_ERR_FAILED:
414	  case ASN_ERR_EOBUF:
415		snmp_pdu_free(pdu);
416		return (SNMP_CODE_FAILED);
417
418	  case ASN_ERR_BADLEN:
419		return (SNMP_CODE_BADLEN);
420
421	  case ASN_ERR_RANGE:
422		return (SNMP_CODE_OORANGE);
423
424	  case ASN_ERR_TAG:
425		if (pdu->version == SNMP_Verr)
426			return (SNMP_CODE_BADVERS);
427		else
428			return (SNMP_CODE_BADENC);
429	}
430
431	return (SNMP_CODE_OK);
432}
433
434/*
435 * Encode the SNMP PDU without the variable bindings field.
436 * We do this the rather uneffective way by
437 * moving things around and assuming that the length field will never
438 * use more than 2 bytes.
439 * We need a number of pointers to apply the fixes afterwards.
440 */
441enum snmp_code
442snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
443{
444	enum asn_err err;
445
446	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
447	    &pdu->outer_ptr) != ASN_ERR_OK)
448		return (SNMP_CODE_FAILED);
449
450	if (pdu->version == SNMP_V1)
451		err = asn_put_integer(b, 0);
452	else if (pdu->version == SNMP_V2c)
453		err = asn_put_integer(b, 1);
454	else
455		return (SNMP_CODE_BADVERS);
456	if (err != ASN_ERR_OK)
457		return (SNMP_CODE_FAILED);
458
459	if (asn_put_octetstring(b, (u_char *)pdu->community,
460	    strlen(pdu->community)) != ASN_ERR_OK)
461		return (SNMP_CODE_FAILED);
462
463	if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT |
464	    pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK)
465		return (SNMP_CODE_FAILED);
466
467	if (pdu->type == SNMP_PDU_TRAP) {
468		if (pdu->version != SNMP_V1 ||
469		    asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK ||
470		    asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK ||
471		    asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK ||
472		    asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK ||
473		    asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK)
474			return (SNMP_CODE_FAILED);
475	} else {
476		if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK ||
477		    pdu->type == SNMP_PDU_INFORM ||
478		    pdu->type == SNMP_PDU_TRAP2 ||
479		    pdu->type == SNMP_PDU_REPORT))
480			return (SNMP_CODE_FAILED);
481
482		if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK ||
483		    asn_put_integer(b, pdu->error_status) != ASN_ERR_OK ||
484		    asn_put_integer(b, pdu->error_index) != ASN_ERR_OK)
485			return (SNMP_CODE_FAILED);
486	}
487
488	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
489	    &pdu->vars_ptr) != ASN_ERR_OK)
490		return (SNMP_CODE_FAILED);
491
492	return (SNMP_CODE_OK);
493}
494
495enum snmp_code
496snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu)
497{
498	if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK ||
499	    asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK ||
500	    asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK)
501		return (SNMP_CODE_FAILED);
502	return (SNMP_CODE_OK);
503}
504
505/*
506 * Encode a binding. Caller must ensure, that the syntax is ok for that version.
507 * Be sure not to cobber b, when something fails.
508 */
509enum asn_err
510snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
511{
512	u_char *ptr;
513	enum asn_err err;
514	struct asn_buf save = *b;
515
516	if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
517	    ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) {
518		*b = save;
519		return (err);
520	}
521
522	if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) {
523		*b = save;
524		return (err);
525	}
526
527	switch (binding->syntax) {
528
529	  case SNMP_SYNTAX_NULL:
530		err = asn_put_null(b);
531		break;
532
533	  case SNMP_SYNTAX_INTEGER:
534		err = asn_put_integer(b, binding->v.integer);
535		break;
536
537	  case SNMP_SYNTAX_OCTETSTRING:
538		err = asn_put_octetstring(b, binding->v.octetstring.octets,
539		    binding->v.octetstring.len);
540		break;
541
542	  case SNMP_SYNTAX_OID:
543		err = asn_put_objid(b, &binding->v.oid);
544		break;
545
546	  case SNMP_SYNTAX_IPADDRESS:
547		err = asn_put_ipaddress(b, binding->v.ipaddress);
548		break;
549
550	  case SNMP_SYNTAX_TIMETICKS:
551		err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32);
552		break;
553
554	  case SNMP_SYNTAX_COUNTER:
555		err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32);
556		break;
557
558	  case SNMP_SYNTAX_GAUGE:
559		err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32);
560		break;
561
562	  case SNMP_SYNTAX_COUNTER64:
563		err = asn_put_counter64(b, binding->v.counter64);
564		break;
565
566	  case SNMP_SYNTAX_NOSUCHOBJECT:
567		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT);
568		break;
569
570	  case SNMP_SYNTAX_NOSUCHINSTANCE:
571		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE);
572		break;
573
574	  case SNMP_SYNTAX_ENDOFMIBVIEW:
575		err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW);
576		break;
577	}
578
579	if (err != ASN_ERR_OK) {
580		*b = save;
581		return (err);
582	}
583
584	err = asn_commit_header(b, ptr);
585	if (err != ASN_ERR_OK) {
586		*b = save;
587		return (err);
588	}
589
590	return (ASN_ERR_OK);
591}
592
593/*
594 * Encode an PDU.
595 */
596enum snmp_code
597snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b)
598{
599	u_int idx;
600	enum snmp_code err;
601
602	if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK)
603		return (err);
604	for (idx = 0; idx < pdu->nbindings; idx++)
605		if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx]))
606		    != ASN_ERR_OK)
607			return (SNMP_CODE_FAILED);
608
609	return (snmp_fix_encoding(resp_b, pdu));
610}
611
612static void
613dump_binding(const struct snmp_value *b)
614{
615	u_int i;
616	char buf[ASN_OIDSTRLEN];
617
618	snmp_printf("%s=", asn_oid2str_r(&b->var, buf));
619	switch (b->syntax) {
620
621	  case SNMP_SYNTAX_NULL:
622		snmp_printf("NULL");
623		break;
624
625	  case SNMP_SYNTAX_INTEGER:
626		snmp_printf("INTEGER %d", b->v.integer);
627		break;
628
629	  case SNMP_SYNTAX_OCTETSTRING:
630		snmp_printf("OCTET STRING %lu:", b->v.octetstring.len);
631		for (i = 0; i < b->v.octetstring.len; i++)
632			snmp_printf(" %02x", b->v.octetstring.octets[i]);
633		break;
634
635	  case SNMP_SYNTAX_OID:
636		snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf));
637		break;
638
639	  case SNMP_SYNTAX_IPADDRESS:
640		snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0],
641		    b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]);
642		break;
643
644	  case SNMP_SYNTAX_COUNTER:
645		snmp_printf("COUNTER %u", b->v.uint32);
646		break;
647
648	  case SNMP_SYNTAX_GAUGE:
649		snmp_printf("GAUGE %u", b->v.uint32);
650		break;
651
652	  case SNMP_SYNTAX_TIMETICKS:
653		snmp_printf("TIMETICKS %u", b->v.uint32);
654		break;
655
656	  case SNMP_SYNTAX_COUNTER64:
657		snmp_printf("COUNTER64 %lld", b->v.counter64);
658		break;
659
660	  case SNMP_SYNTAX_NOSUCHOBJECT:
661		snmp_printf("NoSuchObject");
662		break;
663
664	  case SNMP_SYNTAX_NOSUCHINSTANCE:
665		snmp_printf("NoSuchInstance");
666		break;
667
668	  case SNMP_SYNTAX_ENDOFMIBVIEW:
669		snmp_printf("EndOfMibView");
670		break;
671
672	  default:
673		snmp_printf("UNKNOWN SYNTAX %u", b->syntax);
674		break;
675	}
676}
677
678static __inline void
679dump_bindings(const struct snmp_pdu *pdu)
680{
681	u_int i;
682
683	for (i = 0; i < pdu->nbindings; i++) {
684		snmp_printf(" [%u]: ", i);
685		dump_binding(&pdu->bindings[i]);
686		snmp_printf("\n");
687	}
688}
689
690static __inline void
691dump_notrap(const struct snmp_pdu *pdu)
692{
693	snmp_printf(" request_id=%d", pdu->request_id);
694	snmp_printf(" error_status=%d", pdu->error_status);
695	snmp_printf(" error_index=%d\n", pdu->error_index);
696	dump_bindings(pdu);
697}
698
699void
700snmp_pdu_dump(const struct snmp_pdu *pdu)
701{
702	char buf[ASN_OIDSTRLEN];
703	const char *vers;
704	static const char *types[] = {
705		[SNMP_PDU_GET] =	"GET",
706		[SNMP_PDU_GETNEXT] =	"GETNEXT",
707		[SNMP_PDU_RESPONSE] =	"RESPONSE",
708		[SNMP_PDU_SET] =	"SET",
709		[SNMP_PDU_TRAP] =	"TRAPv1",
710		[SNMP_PDU_GETBULK] =	"GETBULK",
711		[SNMP_PDU_INFORM] =	"INFORM",
712		[SNMP_PDU_TRAP2] =	"TRAPv2",
713		[SNMP_PDU_REPORT] =	"REPORT",
714	};
715
716	if (pdu->version == SNMP_V1)
717		vers = "SNMPv1";
718	else if (pdu->version == SNMP_V2c)
719		vers = "SNMPv2c";
720	else
721		vers = "v?";
722
723	switch (pdu->type) {
724	  case SNMP_PDU_TRAP:
725		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
726		snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf));
727		snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0],
728		    pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]);
729		snmp_printf(" generic_trap=%d", pdu->generic_trap);
730		snmp_printf(" specific_trap=%d", pdu->specific_trap);
731		snmp_printf(" time-stamp=%u\n", pdu->time_stamp);
732		dump_bindings(pdu);
733		break;
734
735	  case SNMP_PDU_GET:
736	  case SNMP_PDU_GETNEXT:
737	  case SNMP_PDU_RESPONSE:
738	  case SNMP_PDU_SET:
739	  case SNMP_PDU_GETBULK:
740	  case SNMP_PDU_INFORM:
741	  case SNMP_PDU_TRAP2:
742	  case SNMP_PDU_REPORT:
743		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
744		dump_notrap(pdu);
745		break;
746
747	  default:
748		snmp_printf("bad pdu type %u\n", pdu->type);
749		break;
750	}
751}
752
753void
754snmp_value_free(struct snmp_value *value)
755{
756	if (value->syntax == SNMP_SYNTAX_OCTETSTRING)
757		free(value->v.octetstring.octets);
758	value->syntax = SNMP_SYNTAX_NULL;
759}
760
761int
762snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
763{
764	to->var = from->var;
765	to->syntax = from->syntax;
766
767	if (from->syntax == SNMP_SYNTAX_OCTETSTRING) {
768		if ((to->v.octetstring.len = from->v.octetstring.len) == 0)
769			to->v.octetstring.octets = NULL;
770		else {
771			to->v.octetstring.octets = malloc(to->v.octetstring.len);
772			if (to->v.octetstring.octets == NULL)
773				return (-1);
774			(void)memcpy(to->v.octetstring.octets,
775			    from->v.octetstring.octets, to->v.octetstring.len);
776		}
777	} else
778		to->v = from->v;
779	return (0);
780}
781
782void
783snmp_pdu_free(struct snmp_pdu *pdu)
784{
785	u_int i;
786
787	for (i = 0; i < pdu->nbindings; i++)
788		snmp_value_free(&pdu->bindings[i]);
789}
790
791/*
792 * Parse an ASCII SNMP value into the binary form
793 */
794int
795snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v)
796{
797	char *end;
798
799	switch (syntax) {
800
801	  case SNMP_SYNTAX_NULL:
802	  case SNMP_SYNTAX_NOSUCHOBJECT:
803	  case SNMP_SYNTAX_NOSUCHINSTANCE:
804	  case SNMP_SYNTAX_ENDOFMIBVIEW:
805		if (*str != '\0')
806			return (-1);
807		return (0);
808
809	  case SNMP_SYNTAX_INTEGER:
810		v->integer = strtoll(str, &end, 0);
811		if (*end != '\0')
812			return (-1);
813		return (0);
814
815	  case SNMP_SYNTAX_OCTETSTRING:
816	    {
817		u_long len;	/* actual length of string */
818		u_long alloc;	/* allocate length of string */
819		u_char *octs;	/* actual octets */
820		u_long oct;	/* actual octet */
821		u_char *nocts;	/* to avoid memory leak */
822		u_char c;	/* actual character */
823
824# define STUFFC(C)							\
825		if (alloc == len) {					\
826			alloc += 100;					\
827			if ((nocts = realloc(octs, alloc)) == NULL) {	\
828				free(octs);				\
829				return (-1);				\
830			}						\
831			octs = nocts;					\
832		}							\
833		octs[len++] = (C);
834
835		len = alloc = 0;
836		octs = NULL;
837
838		if (*str == '"') {
839			str++;
840			while((c = *str++) != '\0') {
841				if (c == '"') {
842					if (*str != '\0') {
843						free(octs);
844						return (-1);
845					}
846					break;
847				}
848				if (c == '\\') {
849					switch (c = *str++) {
850
851					  case '\\':
852						break;
853					  case 'a':
854						c = '\a';
855						break;
856					  case 'b':
857						c = '\b';
858						break;
859					  case 'f':
860						c = '\f';
861						break;
862					  case 'n':
863						c = '\n';
864						break;
865					  case 'r':
866						c = '\r';
867						break;
868					  case 't':
869						c = '\t';
870						break;
871					  case 'v':
872						c = '\v';
873						break;
874					  case 'x':
875						c = 0;
876						if (!isxdigit(*str))
877							break;
878						if (isdigit(*str))
879							c = *str++ - '0';
880						else if (isupper(*str))
881							c = *str++ - 'A' + 10;
882						else
883							c = *str++ - 'a' + 10;
884						if (!isxdigit(*str))
885							break;
886						if (isdigit(*str))
887							c += *str++ - '0';
888						else if (isupper(*str))
889							c += *str++ - 'A' + 10;
890						else
891							c += *str++ - 'a' + 10;
892						break;
893					  case '0': case '1': case '2':
894					  case '3': case '4': case '5':
895					  case '6': case '7':
896						c = *str++ - '0';
897						if (*str < '0' || *str > '7')
898							break;
899						c = *str++ - '0';
900						if (*str < '0' || *str > '7')
901							break;
902						c = *str++ - '0';
903						break;
904					  default:
905						break;
906					}
907				}
908				STUFFC(c);
909			}
910		} else {
911			while (*str != '\0') {
912				oct = strtoul(str, &end, 16);
913				str = end;
914				if (oct > 0xff) {
915					free(octs);
916					return (-1);
917				}
918				STUFFC(oct);
919				if (*str == ':')
920					str++;
921				else if(*str != '\0') {
922					free(octs);
923					return (-1);
924				}
925			}
926		}
927		v->octetstring.octets = octs;
928		v->octetstring.len = len;
929		return (0);
930# undef STUFFC
931	    }
932
933	  case SNMP_SYNTAX_OID:
934	    {
935		u_long subid;
936
937		v->oid.len = 0;
938
939		for (;;) {
940			if (v->oid.len == ASN_MAXOIDLEN)
941				return (-1);
942			subid = strtoul(str, &end, 10);
943			str = end;
944			if (subid > ASN_MAXID)
945				return (-1);
946			v->oid.subs[v->oid.len++] = (asn_subid_t)subid;
947			if (*str == '\0')
948				break;
949			if (*str != '.')
950				return (-1);
951			str++;
952		}
953		return (0);
954	    }
955
956	  case SNMP_SYNTAX_IPADDRESS:
957	    {
958		struct hostent *he;
959		u_long ip[4];
960		int n;
961
962		if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2],
963		    &ip[3], &n) == 4 && (size_t)n == strlen(str) &&
964		    ip[0] <= 0xff && ip[1] <= 0xff &&
965		    ip[2] <= 0xff && ip[3] <= 0xff) {
966			v->ipaddress[0] = (u_char)ip[0];
967			v->ipaddress[1] = (u_char)ip[1];
968			v->ipaddress[2] = (u_char)ip[2];
969			v->ipaddress[3] = (u_char)ip[3];
970			return (0);
971		}
972
973		if ((he = gethostbyname(str)) == NULL)
974			return (-1);
975		if (he->h_addrtype != AF_INET)
976			return (-1);
977
978		v->ipaddress[0] = he->h_addr[0];
979		v->ipaddress[1] = he->h_addr[1];
980		v->ipaddress[2] = he->h_addr[2];
981		v->ipaddress[3] = he->h_addr[3];
982		return (0);
983	    }
984
985	  case SNMP_SYNTAX_COUNTER:
986	  case SNMP_SYNTAX_GAUGE:
987	  case SNMP_SYNTAX_TIMETICKS:
988	    {
989		u_int64_t sub;
990
991		sub = strtoull(str, &end, 0);
992		if (*end != '\0' || sub > 0xffffffff)
993			return (-1);
994		v->uint32 = (u_int32_t)sub;
995		return (0);
996	    }
997
998	  case SNMP_SYNTAX_COUNTER64:
999		v->counter64 = strtoull(str, &end, 0);
1000		if (*end != '\0')
1001			return (-1);
1002		return (0);
1003	}
1004	abort();
1005}
1006
1007static void
1008snmp_error_func(const char *fmt, ...)
1009{
1010	va_list ap;
1011
1012	va_start(ap, fmt);
1013	fprintf(stderr, "SNMP: ");
1014	vfprintf(stderr, fmt, ap);
1015	fprintf(stderr, "\n");
1016	va_end(ap);
1017}
1018
1019static void
1020snmp_printf_func(const char *fmt, ...)
1021{
1022	va_list ap;
1023
1024	va_start(ap, fmt);
1025	vfprintf(stderr, fmt, ap);
1026	va_end(ap);
1027}
1028