xref: /freebsd/contrib/bsnmp/lib/snmp.c (revision 7bd6fde3)
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 
53 static void snmp_error_func(const char *, ...);
54 static void snmp_printf_func(const char *, ...);
55 
56 void (*snmp_error)(const char *, ...) = snmp_error_func;
57 void (*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  */
63 static enum asn_err
64 get_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  */
190 enum asn_err
191 snmp_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 
237 static enum asn_err
238 parse_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  */
277 enum asn_err
278 snmp_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 
357 static enum asn_err
358 parse_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  */
388 enum snmp_code
389 snmp_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  */
441 int
442 snmp_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  */
499 enum snmp_code
500 snmp_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 
553 enum snmp_code
554 snmp_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  */
567 enum asn_err
568 snmp_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  */
654 enum snmp_code
655 snmp_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 
670 static void
671 dump_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 
736 static __inline void
737 dump_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 
748 static __inline void
749 dump_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 
757 void
758 snmp_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 
811 void
812 snmp_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 
819 int
820 snmp_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 
840 void
841 snmp_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  */
852 int
853 snmp_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 
1065 static void
1066 snmp_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 
1077 static void
1078 snmp_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