]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bsnmp/lib/snmp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / bsnmp / lib / snmp.c
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 }