1 /*
2    This file is part of GNU Radius SNMP Library.
3    Copyright (C) 2001,2003,2004,2007 Free Software Foundation, Inc.
4 
5    Written by Sergey Poznyakoff
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 3 of the
10    License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 
26 #include <stdlib.h>
27 #include <snmp/asn1.h>
28 #include <snmp/snmp.h>
29 
30 /* free a *single* snmp variable */
31 void
snmp_var_free(struct snmp_var * var)32 snmp_var_free(struct snmp_var *var)
33 {
34         if (!var)
35                 return;
36         switch (var->type) {
37         case ASN_BOOLEAN:
38         case ASN_INTEGER:
39         case ASN_NULL:
40         case SMI_COUNTER32:
41         case SMI_GAUGE32:
42         case SMI_TIMETICKS:
43                 break;
44         case ASN_OCTET_STR:
45         case SMI_IPADDRESS:
46         case SMI_OPAQUE:
47         case ASN_BIT_STR:
48                 snmp_free(var->var_str);
49                 break;
50         case ASN_OBJECT_ID:
51                 snmp_free(var->var_oid);
52                 break;
53         default:
54                 abort();
55         }
56         snmp_free(var->name);
57         snmp_free(var);
58 }
59 
60 /* free whole snmp variable list */
61 void
snmp_var_free_list(struct snmp_var * var)62 snmp_var_free_list(struct snmp_var *var)
63 {
64         struct snmp_var *next;
65 
66         while (var) {
67                 next = var->next;
68                 snmp_var_free(var);
69                 var = next;
70         }
71 }
72 
73 struct snmp_var *
snmp_var_create(oid_t oid)74 snmp_var_create(oid_t oid)
75 {
76         struct snmp_var *var;
77 
78         var = snmp_alloc(sizeof(*var));
79         if (!var) {
80                 SNMP_SET_ERRNO(E_SNMP_NOMEM);
81                 return NULL;
82         }
83 
84         var->name = oid_dup(oid);
85         if (!var->name) {
86                 SNMP_SET_ERRNO(E_SNMP_NOMEM);
87                 snmp_free(var);
88                 return NULL;
89         }
90         var->type = ASN_NULL;
91         var->next = NULL;
92         return var;
93 }
94 
95 struct snmp_var *
snmp_var_dup(struct snmp_var * src)96 snmp_var_dup(struct snmp_var *src)
97 {
98         struct snmp_var *var;
99 
100         if ((var = snmp_var_create(src->name)) == NULL)
101                 return NULL;
102         var->type = src->type;
103         var->val_length = src->val_length;
104         switch (var->type) {
105         case ASN_BOOLEAN:
106         case ASN_INTEGER:
107         case ASN_NULL:
108         case SMI_COUNTER32:
109         case SMI_GAUGE32:
110         case SMI_TIMETICKS:
111                 var->var_int = src->var_int;
112                 break;
113         case ASN_OCTET_STR:
114         case SMI_IPADDRESS:
115         case SMI_OPAQUE:
116         case ASN_BIT_STR:
117                 var->var_str = snmp_alloc(src->val_length);
118                 if (!var->var_str) {
119                         SNMP_SET_ERRNO(E_SNMP_NOMEM);
120                         snmp_free(var);
121                         return NULL;
122                 }
123                 memcpy(var->var_str, src->var_str, src->val_length);
124                 break;
125         case ASN_OBJECT_ID:
126                 var->var_oid = oid_dup(src->var_oid);
127                 if (!var->var_oid) {
128                         SNMP_SET_ERRNO(E_SNMP_NOMEM);
129                         snmp_free(var);
130                         return NULL;
131                 }
132                 break;
133         default:
134                 abort();
135         }
136         return var;
137 }
138 
139 struct snmp_var *
snmp_var_dup_list(struct snmp_var * var)140 snmp_var_dup_list(struct snmp_var *var)
141 {
142         struct snmp_var *var_head, *var_tail, *vp;
143 
144         var_head = var_tail = NULL;
145         for (; var; var = var->next) {
146                 if ((vp = snmp_var_dup(var)) == NULL) {
147                         snmp_var_free_list(var_head);
148                         return NULL;
149                 }
150                 if (var_tail)
151                         var_tail->next = vp;
152                 else
153                         var_head = vp;
154                 var_tail = vp;
155         }
156         return var_head;
157 }
158 
159 /* RFC 1905: Protocol Operations for SNMPv2
160    VarBind ::= SEQUENCE {
161          name ObjectName
162          CHOICE {
163                  value ObjectSyntax
164                  unSpecified NULL
165                  noSuchObject[0] NULL
166                  noSuchInstance[1] NULL
167                  endOfMibView[2] NULL
168          }
169    }
170 */
171 
172 u_char *
snmp_var_decode(u_char * data,int * length,struct snmp_var ** var_head,int version)173 snmp_var_decode(u_char *data, int *length, struct snmp_var **var_head,
174 		int version)
175 {
176         u_char *buf, *tmp;
177         u_char *data_ptr;
178         u_char type;
179         int list_length = *length;
180         int var_length, data_len;
181         subid_t oid[MAX_OID_LEN+1];
182         struct snmp_var *var = NULL, *var_tail;
183 
184         /* Determine length of the variable list */
185         buf = asn_decode_header(data, &list_length, &type);
186         if (!buf)
187                 return NULL;
188         if (type != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
189                 SNMP_SET_ERRNO(E_SNMP_DECODE);
190                 return NULL;
191         }
192 
193         *var_head = var_tail = NULL;
194         /* Parse variable list */
195         while (list_length > 0) {
196 
197                 /* determine length of this variable; */
198                 var_length = list_length;
199                 tmp = asn_decode_header(buf, &var_length, &type);
200                 if (!tmp)
201                         goto err;
202 
203                 /* adjust total length and restore buffer pointer */
204                 list_length -= var_length + (tmp - buf);
205                 buf = tmp;
206 
207                 if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
208                         SNMP_SET_ERRNO(E_SNMP_DECODE);
209                         goto err;
210                 }
211 
212                 /* read oid */
213                 OIDLEN(oid) = MAX_OID_LEN;
214                 buf = asn_decode_oid(buf, &var_length, &type,
215                                      OIDPTR(oid), &OIDLEN(oid));
216                 if (!buf)
217                         goto err;
218 
219                 if (type != (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID)) {
220                         SNMP_SET_ERRNO(E_SNMP_DECODE);
221                         goto err;
222                 }
223 
224                 /* allocate variable */
225                 var = snmp_var_create(oid);
226 
227                 /* Preserve pointer to the start of data */
228                 data_ptr = buf;
229                 data_len = var_length;
230 
231                 /* read object's header */
232                 buf = asn_decode_header(buf, &data_len, &var->type);
233                 if (!buf)
234                         goto err;
235 
236                 /* now depending on type ... */
237                 switch (var->type) {
238                 case ASN_INTEGER:
239                         var->val_length = sizeof(var->var_int);
240                         buf = asn_decode_int(data_ptr, &var_length,
241                                              &var->type, &var->var_int,
242                                              var->val_length);
243                         break;
244 
245                 case SMI_COUNTER32:
246                 case SMI_GAUGE32:
247                 case SMI_TIMETICKS:
248                         var->val_length = sizeof(var->var_int);
249                         buf = asn_decode_uint(data_ptr, &var_length,
250                                               &var->type, &var->var_int,
251                                               var->val_length);
252                         break;
253 
254                 case ASN_OCTET_STR:
255                 case SMI_IPADDRESS:
256                 case SMI_OPAQUE:
257                         var->val_length = var_length; /* at most ... */
258                         var->var_str = snmp_alloc(var->val_length);
259                         if (!var->var_str) {
260                                 SNMP_SET_ERRNO(E_SNMP_NOMEM);
261                                 goto err;
262                         }
263                         buf = asn_decode_string(data_ptr, &var_length,
264                                                 &var->type, var->var_str,
265                                                 &var->val_length);
266                         break;
267 
268                 case ASN_OBJECT_ID:
269                         var->val_length = MAX_OID_LEN;
270                         buf = asn_decode_oid(data_ptr, &var_length,
271                                              &var->type, OIDPTR(oid),
272                                              &var->val_length);
273                         if (buf) {
274                                 OIDLEN(oid) = var->val_length;
275                                 var->var_oid = oid_dup(oid);
276                         }
277                         break;
278 
279                 case ASN_NULL:
280                         break;
281 
282                 default:
283                         SNMP_SET_ERRNO(E_SNMP_BAD_VARTYPE);
284                         break;
285                 }
286 
287                 if (!buf)
288                         goto err;
289 
290                 if (!var_tail)
291                         *var_head = var;
292                 else
293                         var_tail->next = var;
294                 var_tail = var;
295                 var = NULL;
296         }
297 
298         return buf;
299 
300 err:
301         snmp_var_free(var);
302         snmp_var_free_list(*var_head);
303         return NULL;
304 }
305 
306 u_char *
snmp_var_encode(u_char * data,int * length,struct snmp_var * var,int version)307 snmp_var_encode(u_char *data, int *length, struct snmp_var *var, int version)
308 {
309         u_char *buf = data;
310         u_char *header_ptr, *header_end;
311 
312         for (; var; var = var->next) {
313                 header_ptr = buf;
314                 buf = asn_encode_header(buf, length,
315                                         (ASN_SEQUENCE | ASN_CONSTRUCTOR),
316                                         0xffff);
317                 if (!buf)
318                         return NULL;
319 
320                 header_end = buf;
321 
322                 buf = asn_encode_oid(buf, length,
323                                      (ASN_UNIVERSAL |
324                                       ASN_PRIMITIVE |
325                                       ASN_OBJECT_ID),
326                                      OIDPTR(var->name), OIDLEN(var->name));
327                 if (!buf)
328                         return NULL;
329 
330                 switch (var->type) {
331                 case ASN_INTEGER:
332                         buf = asn_encode_int(buf, length, var->type,
333                                              var->var_int);
334                         break;
335                 case SMI_COUNTER32:
336                 case SMI_GAUGE32:
337                 case SMI_TIMETICKS:
338                         buf = asn_encode_uint(buf, length, var->type,
339                                               var->var_int);
340                         break;
341                 case ASN_OCTET_STR:
342                 case SMI_IPADDRESS:
343                 case SMI_OPAQUE:
344                         buf = asn_encode_string(buf, length, var->type,
345                                                 var->var_str, var->val_length);
346                         break;
347 
348                 case ASN_OBJECT_ID:
349                         buf = asn_encode_oid(buf, length, var->type,
350                                              OIDPTR(var->var_oid),
351                                              OIDLEN(var->var_oid));
352                         break;
353 
354                 case ASN_NULL:
355                         buf = asn_encode_null(buf, length, var->type);
356                         break;
357 
358                 default:
359                         SNMP_SET_ERRNO(E_SNMP_NOT_SUPPORTED);
360                         buf = NULL;
361                 }
362 
363                 if (!buf)
364                         return NULL;
365 
366                 asn_recode_length(header_ptr+1,
367                                   buf - header_end);
368         }
369 
370         return buf;
371 }
372