]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/lib/snmp.c
This commit was generated by cvs2svn to compensate for changes in r146299,
[FreeBSD/FreeBSD.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.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 }