snmp.c revision 150921
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 and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/lib/snmp.c,v 1.40 2005/10/04 14:32:42 brandt_h Exp $
30 *
31 * SNMP
32 */
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <stddef.h>
38#include <stdarg.h>
39#ifdef HAVE_STDINT_H
40#include <stdint.h>
41#elif defined(HAVE_INTTYPES_H)
42#include <inttypes.h>
43#endif
44#include <string.h>
45#include <ctype.h>
46#include <netdb.h>
47#include <errno.h>
48
49#include "asn1.h"
50#include "snmp.h"
51#include "snmppriv.h"
52
53static void snmp_error_func(const char *, ...);
54static void snmp_printf_func(const char *, ...);
55
56void (*snmp_error)(const char *, ...) = snmp_error_func;
57void (*snmp_printf)(const char *, ...) = snmp_printf_func;
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 * Check whether what we have is the complete PDU by snooping at the
436 * enclosing structure header. This returns:
437 *   -1		if there are ASN.1 errors
438 *    0		if we need more data
439 *  > 0		the length of this PDU
440 */
441int
442snmp_pdu_snoop(const struct asn_buf *b0)
443{
444	u_int length;
445	asn_len_t len;
446	struct asn_buf b = *b0;
447
448	/* <0x10|0x20> <len> <data...> */
449
450	if (b.asn_len == 0)
451		return (0);
452	if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) {
453		asn_error(&b, "bad sequence type %u", b.asn_cptr[0]);
454		return (-1);
455	}
456	b.asn_len--;
457	b.asn_cptr++;
458
459	if (b.asn_len == 0)
460		return (0);
461
462	if (*b.asn_cptr & 0x80) {
463		/* long length */
464		length = *b.asn_cptr++ & 0x7f;
465		b.asn_len--;
466		if (length == 0) {
467			asn_error(&b, "indefinite length not supported");
468			return (-1);
469		}
470		if (length > ASN_MAXLENLEN) {
471			asn_error(&b, "long length too long (%u)", length);
472			return (-1);
473		}
474		if (length > b.asn_len)
475			return (0);
476		len = 0;
477		while (length--) {
478			len = (len << 8) | *b.asn_cptr++;
479			b.asn_len--;
480		}
481	} else {
482		len = *b.asn_cptr++;
483		b.asn_len--;
484	}
485
486	if (len > b.asn_len)
487		return (0);
488
489	return (len + b.asn_cptr - b0->asn_cptr);
490}
491
492/*
493 * Encode the SNMP PDU without the variable bindings field.
494 * We do this the rather uneffective way by
495 * moving things around and assuming that the length field will never
496 * use more than 2 bytes.
497 * We need a number of pointers to apply the fixes afterwards.
498 */
499enum snmp_code
500snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
501{
502	enum asn_err err;
503
504	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
505	    &pdu->outer_ptr) != ASN_ERR_OK)
506		return (SNMP_CODE_FAILED);
507
508	if (pdu->version == SNMP_V1)
509		err = asn_put_integer(b, 0);
510	else if (pdu->version == SNMP_V2c)
511		err = asn_put_integer(b, 1);
512	else
513		return (SNMP_CODE_BADVERS);
514	if (err != ASN_ERR_OK)
515		return (SNMP_CODE_FAILED);
516
517	if (asn_put_octetstring(b, (u_char *)pdu->community,
518	    strlen(pdu->community)) != ASN_ERR_OK)
519		return (SNMP_CODE_FAILED);
520
521	if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT |
522	    pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK)
523		return (SNMP_CODE_FAILED);
524
525	if (pdu->type == SNMP_PDU_TRAP) {
526		if (pdu->version != SNMP_V1 ||
527		    asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK ||
528		    asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK ||
529		    asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK ||
530		    asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK ||
531		    asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK)
532			return (SNMP_CODE_FAILED);
533	} else {
534		if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK ||
535		    pdu->type == SNMP_PDU_INFORM ||
536		    pdu->type == SNMP_PDU_TRAP2 ||
537		    pdu->type == SNMP_PDU_REPORT))
538			return (SNMP_CODE_FAILED);
539
540		if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK ||
541		    asn_put_integer(b, pdu->error_status) != ASN_ERR_OK ||
542		    asn_put_integer(b, pdu->error_index) != ASN_ERR_OK)
543			return (SNMP_CODE_FAILED);
544	}
545
546	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
547	    &pdu->vars_ptr) != ASN_ERR_OK)
548		return (SNMP_CODE_FAILED);
549
550	return (SNMP_CODE_OK);
551}
552
553enum snmp_code
554snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu)
555{
556	if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK ||
557	    asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK ||
558	    asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK)
559		return (SNMP_CODE_FAILED);
560	return (SNMP_CODE_OK);
561}
562
563/*
564 * Encode a binding. Caller must ensure, that the syntax is ok for that version.
565 * Be sure not to cobber b, when something fails.
566 */
567enum asn_err
568snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
569{
570	u_char *ptr;
571	enum asn_err err;
572	struct asn_buf save = *b;
573
574	if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
575	    ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) {
576		*b = save;
577		return (err);
578	}
579
580	if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) {
581		*b = save;
582		return (err);
583	}
584
585	switch (binding->syntax) {
586
587	  case SNMP_SYNTAX_NULL:
588		err = asn_put_null(b);
589		break;
590
591	  case SNMP_SYNTAX_INTEGER:
592		err = asn_put_integer(b, binding->v.integer);
593		break;
594
595	  case SNMP_SYNTAX_OCTETSTRING:
596		err = asn_put_octetstring(b, binding->v.octetstring.octets,
597		    binding->v.octetstring.len);
598		break;
599
600	  case SNMP_SYNTAX_OID:
601		err = asn_put_objid(b, &binding->v.oid);
602		break;
603
604	  case SNMP_SYNTAX_IPADDRESS:
605		err = asn_put_ipaddress(b, binding->v.ipaddress);
606		break;
607
608	  case SNMP_SYNTAX_TIMETICKS:
609		err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32);
610		break;
611
612	  case SNMP_SYNTAX_COUNTER:
613		err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32);
614		break;
615
616	  case SNMP_SYNTAX_GAUGE:
617		err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32);
618		break;
619
620	  case SNMP_SYNTAX_COUNTER64:
621		err = asn_put_counter64(b, binding->v.counter64);
622		break;
623
624	  case SNMP_SYNTAX_NOSUCHOBJECT:
625		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT);
626		break;
627
628	  case SNMP_SYNTAX_NOSUCHINSTANCE:
629		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE);
630		break;
631
632	  case SNMP_SYNTAX_ENDOFMIBVIEW:
633		err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW);
634		break;
635	}
636
637	if (err != ASN_ERR_OK) {
638		*b = save;
639		return (err);
640	}
641
642	err = asn_commit_header(b, ptr);
643	if (err != ASN_ERR_OK) {
644		*b = save;
645		return (err);
646	}
647
648	return (ASN_ERR_OK);
649}
650
651/*
652 * Encode an PDU.
653 */
654enum snmp_code
655snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b)
656{
657	u_int idx;
658	enum snmp_code err;
659
660	if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK)
661		return (err);
662	for (idx = 0; idx < pdu->nbindings; idx++)
663		if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx]))
664		    != ASN_ERR_OK)
665			return (SNMP_CODE_FAILED);
666
667	return (snmp_fix_encoding(resp_b, pdu));
668}
669
670static void
671dump_binding(const struct snmp_value *b)
672{
673	u_int i;
674	char buf[ASN_OIDSTRLEN];
675
676	snmp_printf("%s=", asn_oid2str_r(&b->var, buf));
677	switch (b->syntax) {
678
679	  case SNMP_SYNTAX_NULL:
680		snmp_printf("NULL");
681		break;
682
683	  case SNMP_SYNTAX_INTEGER:
684		snmp_printf("INTEGER %d", b->v.integer);
685		break;
686
687	  case SNMP_SYNTAX_OCTETSTRING:
688		snmp_printf("OCTET STRING %lu:", b->v.octetstring.len);
689		for (i = 0; i < b->v.octetstring.len; i++)
690			snmp_printf(" %02x", b->v.octetstring.octets[i]);
691		break;
692
693	  case SNMP_SYNTAX_OID:
694		snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf));
695		break;
696
697	  case SNMP_SYNTAX_IPADDRESS:
698		snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0],
699		    b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]);
700		break;
701
702	  case SNMP_SYNTAX_COUNTER:
703		snmp_printf("COUNTER %u", b->v.uint32);
704		break;
705
706	  case SNMP_SYNTAX_GAUGE:
707		snmp_printf("GAUGE %u", b->v.uint32);
708		break;
709
710	  case SNMP_SYNTAX_TIMETICKS:
711		snmp_printf("TIMETICKS %u", b->v.uint32);
712		break;
713
714	  case SNMP_SYNTAX_COUNTER64:
715		snmp_printf("COUNTER64 %lld", b->v.counter64);
716		break;
717
718	  case SNMP_SYNTAX_NOSUCHOBJECT:
719		snmp_printf("NoSuchObject");
720		break;
721
722	  case SNMP_SYNTAX_NOSUCHINSTANCE:
723		snmp_printf("NoSuchInstance");
724		break;
725
726	  case SNMP_SYNTAX_ENDOFMIBVIEW:
727		snmp_printf("EndOfMibView");
728		break;
729
730	  default:
731		snmp_printf("UNKNOWN SYNTAX %u", b->syntax);
732		break;
733	}
734}
735
736static __inline void
737dump_bindings(const struct snmp_pdu *pdu)
738{
739	u_int i;
740
741	for (i = 0; i < pdu->nbindings; i++) {
742		snmp_printf(" [%u]: ", i);
743		dump_binding(&pdu->bindings[i]);
744		snmp_printf("\n");
745	}
746}
747
748static __inline void
749dump_notrap(const struct snmp_pdu *pdu)
750{
751	snmp_printf(" request_id=%d", pdu->request_id);
752	snmp_printf(" error_status=%d", pdu->error_status);
753	snmp_printf(" error_index=%d\n", pdu->error_index);
754	dump_bindings(pdu);
755}
756
757void
758snmp_pdu_dump(const struct snmp_pdu *pdu)
759{
760	char buf[ASN_OIDSTRLEN];
761	const char *vers;
762	static const char *types[] = {
763		[SNMP_PDU_GET] =	"GET",
764		[SNMP_PDU_GETNEXT] =	"GETNEXT",
765		[SNMP_PDU_RESPONSE] =	"RESPONSE",
766		[SNMP_PDU_SET] =	"SET",
767		[SNMP_PDU_TRAP] =	"TRAPv1",
768		[SNMP_PDU_GETBULK] =	"GETBULK",
769		[SNMP_PDU_INFORM] =	"INFORM",
770		[SNMP_PDU_TRAP2] =	"TRAPv2",
771		[SNMP_PDU_REPORT] =	"REPORT",
772	};
773
774	if (pdu->version == SNMP_V1)
775		vers = "SNMPv1";
776	else if (pdu->version == SNMP_V2c)
777		vers = "SNMPv2c";
778	else
779		vers = "v?";
780
781	switch (pdu->type) {
782	  case SNMP_PDU_TRAP:
783		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
784		snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf));
785		snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0],
786		    pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]);
787		snmp_printf(" generic_trap=%d", pdu->generic_trap);
788		snmp_printf(" specific_trap=%d", pdu->specific_trap);
789		snmp_printf(" time-stamp=%u\n", pdu->time_stamp);
790		dump_bindings(pdu);
791		break;
792
793	  case SNMP_PDU_GET:
794	  case SNMP_PDU_GETNEXT:
795	  case SNMP_PDU_RESPONSE:
796	  case SNMP_PDU_SET:
797	  case SNMP_PDU_GETBULK:
798	  case SNMP_PDU_INFORM:
799	  case SNMP_PDU_TRAP2:
800	  case SNMP_PDU_REPORT:
801		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
802		dump_notrap(pdu);
803		break;
804
805	  default:
806		snmp_printf("bad pdu type %u\n", pdu->type);
807		break;
808	}
809}
810
811void
812snmp_value_free(struct snmp_value *value)
813{
814	if (value->syntax == SNMP_SYNTAX_OCTETSTRING)
815		free(value->v.octetstring.octets);
816	value->syntax = SNMP_SYNTAX_NULL;
817}
818
819int
820snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
821{
822	to->var = from->var;
823	to->syntax = from->syntax;
824
825	if (from->syntax == SNMP_SYNTAX_OCTETSTRING) {
826		if ((to->v.octetstring.len = from->v.octetstring.len) == 0)
827			to->v.octetstring.octets = NULL;
828		else {
829			to->v.octetstring.octets = malloc(to->v.octetstring.len);
830			if (to->v.octetstring.octets == NULL)
831				return (-1);
832			(void)memcpy(to->v.octetstring.octets,
833			    from->v.octetstring.octets, to->v.octetstring.len);
834		}
835	} else
836		to->v = from->v;
837	return (0);
838}
839
840void
841snmp_pdu_free(struct snmp_pdu *pdu)
842{
843	u_int i;
844
845	for (i = 0; i < pdu->nbindings; i++)
846		snmp_value_free(&pdu->bindings[i]);
847}
848
849/*
850 * Parse an ASCII SNMP value into the binary form
851 */
852int
853snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v)
854{
855	char *end;
856
857	switch (syntax) {
858
859	  case SNMP_SYNTAX_NULL:
860	  case SNMP_SYNTAX_NOSUCHOBJECT:
861	  case SNMP_SYNTAX_NOSUCHINSTANCE:
862	  case SNMP_SYNTAX_ENDOFMIBVIEW:
863		if (*str != '\0')
864			return (-1);
865		return (0);
866
867	  case SNMP_SYNTAX_INTEGER:
868		v->integer = strtoll(str, &end, 0);
869		if (*end != '\0')
870			return (-1);
871		return (0);
872
873	  case SNMP_SYNTAX_OCTETSTRING:
874	    {
875		u_long len;	/* actual length of string */
876		u_long alloc;	/* allocate length of string */
877		u_char *octs;	/* actual octets */
878		u_long oct;	/* actual octet */
879		u_char *nocts;	/* to avoid memory leak */
880		u_char c;	/* actual character */
881
882# define STUFFC(C)							\
883		if (alloc == len) {					\
884			alloc += 100;					\
885			if ((nocts = realloc(octs, alloc)) == NULL) {	\
886				free(octs);				\
887				return (-1);				\
888			}						\
889			octs = nocts;					\
890		}							\
891		octs[len++] = (C);
892
893		len = alloc = 0;
894		octs = NULL;
895
896		if (*str == '"') {
897			str++;
898			while((c = *str++) != '\0') {
899				if (c == '"') {
900					if (*str != '\0') {
901						free(octs);
902						return (-1);
903					}
904					break;
905				}
906				if (c == '\\') {
907					switch (c = *str++) {
908
909					  case '\\':
910						break;
911					  case 'a':
912						c = '\a';
913						break;
914					  case 'b':
915						c = '\b';
916						break;
917					  case 'f':
918						c = '\f';
919						break;
920					  case 'n':
921						c = '\n';
922						break;
923					  case 'r':
924						c = '\r';
925						break;
926					  case 't':
927						c = '\t';
928						break;
929					  case 'v':
930						c = '\v';
931						break;
932					  case 'x':
933						c = 0;
934						if (!isxdigit(*str))
935							break;
936						if (isdigit(*str))
937							c = *str++ - '0';
938						else if (isupper(*str))
939							c = *str++ - 'A' + 10;
940						else
941							c = *str++ - 'a' + 10;
942						if (!isxdigit(*str))
943							break;
944						if (isdigit(*str))
945							c += *str++ - '0';
946						else if (isupper(*str))
947							c += *str++ - 'A' + 10;
948						else
949							c += *str++ - 'a' + 10;
950						break;
951					  case '0': case '1': case '2':
952					  case '3': case '4': case '5':
953					  case '6': case '7':
954						c = *str++ - '0';
955						if (*str < '0' || *str > '7')
956							break;
957						c = *str++ - '0';
958						if (*str < '0' || *str > '7')
959							break;
960						c = *str++ - '0';
961						break;
962					  default:
963						break;
964					}
965				}
966				STUFFC(c);
967			}
968		} else {
969			while (*str != '\0') {
970				oct = strtoul(str, &end, 16);
971				str = end;
972				if (oct > 0xff) {
973					free(octs);
974					return (-1);
975				}
976				STUFFC(oct);
977				if (*str == ':')
978					str++;
979				else if(*str != '\0') {
980					free(octs);
981					return (-1);
982				}
983			}
984		}
985		v->octetstring.octets = octs;
986		v->octetstring.len = len;
987		return (0);
988# undef STUFFC
989	    }
990
991	  case SNMP_SYNTAX_OID:
992	    {
993		u_long subid;
994
995		v->oid.len = 0;
996
997		for (;;) {
998			if (v->oid.len == ASN_MAXOIDLEN)
999				return (-1);
1000			subid = strtoul(str, &end, 10);
1001			str = end;
1002			if (subid > ASN_MAXID)
1003				return (-1);
1004			v->oid.subs[v->oid.len++] = (asn_subid_t)subid;
1005			if (*str == '\0')
1006				break;
1007			if (*str != '.')
1008				return (-1);
1009			str++;
1010		}
1011		return (0);
1012	    }
1013
1014	  case SNMP_SYNTAX_IPADDRESS:
1015	    {
1016		struct hostent *he;
1017		u_long ip[4];
1018		int n;
1019
1020		if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2],
1021		    &ip[3], &n) == 4 && (size_t)n == strlen(str) &&
1022		    ip[0] <= 0xff && ip[1] <= 0xff &&
1023		    ip[2] <= 0xff && ip[3] <= 0xff) {
1024			v->ipaddress[0] = (u_char)ip[0];
1025			v->ipaddress[1] = (u_char)ip[1];
1026			v->ipaddress[2] = (u_char)ip[2];
1027			v->ipaddress[3] = (u_char)ip[3];
1028			return (0);
1029		}
1030
1031		if ((he = gethostbyname(str)) == NULL)
1032			return (-1);
1033		if (he->h_addrtype != AF_INET)
1034			return (-1);
1035
1036		v->ipaddress[0] = he->h_addr[0];
1037		v->ipaddress[1] = he->h_addr[1];
1038		v->ipaddress[2] = he->h_addr[2];
1039		v->ipaddress[3] = he->h_addr[3];
1040		return (0);
1041	    }
1042
1043	  case SNMP_SYNTAX_COUNTER:
1044	  case SNMP_SYNTAX_GAUGE:
1045	  case SNMP_SYNTAX_TIMETICKS:
1046	    {
1047		uint64_t sub;
1048
1049		sub = strtoull(str, &end, 0);
1050		if (*end != '\0' || sub > 0xffffffff)
1051			return (-1);
1052		v->uint32 = (uint32_t)sub;
1053		return (0);
1054	    }
1055
1056	  case SNMP_SYNTAX_COUNTER64:
1057		v->counter64 = strtoull(str, &end, 0);
1058		if (*end != '\0')
1059			return (-1);
1060		return (0);
1061	}
1062	abort();
1063}
1064
1065static void
1066snmp_error_func(const char *fmt, ...)
1067{
1068	va_list ap;
1069
1070	va_start(ap, fmt);
1071	fprintf(stderr, "SNMP: ");
1072	vfprintf(stderr, fmt, ap);
1073	fprintf(stderr, "\n");
1074	va_end(ap);
1075}
1076
1077static void
1078snmp_printf_func(const char *fmt, ...)
1079{
1080	va_list ap;
1081
1082	va_start(ap, fmt);
1083	vfprintf(stderr, fmt, ap);
1084	va_end(ap);
1085}
1086