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