]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | /* | |
3 | * SNMP Variable Binding. Complies with: | |
4 | * | |
5 | * RFC 1905: Protocol Operations for SNMPv2 | |
6 | * | |
7 | */ | |
8 | ||
9 | /********************************************************************** | |
10 | * | |
11 | * Copyright 1997 by Carnegie Mellon University | |
12 | * | |
13 | * All Rights Reserved | |
14 | * | |
15 | * Permission to use, copy, modify, and distribute this software and its | |
16 | * documentation for any purpose and without fee is hereby granted, | |
17 | * provided that the above copyright notice appear in all copies and that | |
18 | * both that copyright notice and this permission notice appear in | |
19 | * supporting documentation, and that the name of CMU not be | |
20 | * used in advertising or publicity pertaining to distribution of the | |
21 | * software without specific, written prior permission. | |
22 | * | |
23 | * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
24 | * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
25 | * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
26 | * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
27 | * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
28 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
29 | * SOFTWARE. | |
30 | * | |
31 | * Author: Ryan Troll <ryan+@andrew.cmu.edu> | |
32 | * | |
33 | **********************************************************************/ | |
34 | ||
35 | #include "squid.h" | |
36 | ||
37 | #include <stdio.h> | |
38 | ||
39 | #if HAVE_UNISTD_H | |
40 | #include <unistd.h> | |
41 | #endif | |
42 | #if HAVE_STDLIB_H | |
43 | #include <stdlib.h> | |
44 | #endif | |
45 | #if HAVE_SYS_TYPES_H | |
46 | #include <sys/types.h> | |
47 | #endif | |
48 | #if HAVE_CTYPE_H | |
49 | #include <ctype.h> | |
50 | #endif | |
51 | #if HAVE_GNUMALLOC_H | |
52 | #include <gnumalloc.h> | |
53 | #elif HAVE_MALLOC_H | |
54 | #include <malloc.h> | |
55 | #endif | |
56 | #if HAVE_MEMORY_H | |
57 | #include <memory.h> | |
58 | #endif | |
59 | #if HAVE_STRING_H | |
60 | #include <string.h> | |
61 | #endif | |
62 | #if HAVE_STRINGS_H | |
63 | #include <strings.h> | |
64 | #endif | |
65 | #if HAVE_BSTRING_H | |
66 | #include <bstring.h> | |
67 | #endif | |
68 | #if HAVE_SYS_SOCKET_H | |
69 | #include <sys/socket.h> | |
70 | #endif | |
71 | #if HAVE_NETINET_IN_H | |
72 | #include <netinet/in.h> | |
73 | #endif | |
74 | #if HAVE_ARPA_INET_H | |
75 | #include <arpa/inet.h> | |
76 | #endif | |
77 | #if HAVE_SYS_TIME_H | |
78 | #include <sys/time.h> | |
79 | #endif | |
80 | #if HAVE_NETDB_H | |
81 | #include <netdb.h> | |
82 | #endif | |
83 | ||
84 | #include "asn1.h" | |
85 | #include "snmp.h" | |
86 | #include "snmp_vars.h" | |
87 | #if 0 | |
88 | #include "mibii.h" | |
89 | #endif | |
90 | #include "snmp_api_error.h" | |
91 | #include "snmp_msg.h" | |
92 | #include "snmp_pdu.h" | |
93 | ||
94 | #include "util.h" | |
95 | ||
96 | /* #define DEBUG_VARS 1 */ | |
97 | /* #define DEBUG_VARS_MALLOC 1 */ | |
98 | /* #define DEBUG_VARS_DECODE 1 */ | |
99 | /* #define DEBUG_VARS_ENCODE 1 */ | |
100 | ||
101 | /* Create a new variable_list structure representing oid Name of length Len. | |
102 | * | |
103 | * Returns NULL upon error. | |
104 | */ | |
105 | ||
106 | struct variable_list * | |
107 | snmp_var_new(oid * Name, int Len) { | |
108 | struct variable_list *New; | |
109 | ||
110 | #if DEBUG_VARS | |
111 | printf("VARS: Creating.\n"); | |
112 | #endif | |
113 | ||
114 | New = xmalloc(sizeof(*New)); | |
115 | /* XXX xmalloc never returns NULL */ | |
116 | if (New == NULL) { | |
117 | snmp_set_api_error(SNMPERR_OS_ERR); | |
118 | return (NULL); | |
119 | } | |
120 | memset(New, '\0', sizeof(struct variable_list)); | |
121 | /* New->next_variable = NULL; */ | |
122 | ||
123 | New->type = ASN_NULL; | |
124 | New->name_length = Len; | |
125 | ||
126 | if (New->name_length == 0) { | |
127 | New->name = NULL; | |
128 | return (New); | |
129 | } | |
130 | New->name = (oid *) xmalloc(Len * sizeof(oid)); | |
131 | /* XXX xmalloc never returns NULL */ | |
132 | if (New->name == NULL) { | |
133 | xfree(New); | |
134 | snmp_set_api_error(SNMPERR_OS_ERR); | |
135 | return (NULL); | |
136 | } | |
137 | #if DEBUG_VARS | |
138 | printf("VARS: Copying name, size (%d)\n", Len); | |
139 | #endif | |
140 | ||
141 | /* Only copy a name if it was specified. */ | |
142 | if (Name) | |
143 | memcpy((char *) New->name, (char *) Name, Len * sizeof(oid)); | |
144 | ||
145 | return (New); | |
146 | } | |
147 | ||
148 | struct variable_list * | |
149 | snmp_var_new_integer(oid * Name, int Len, int ival, unsigned char type) { | |
150 | variable_list *v = snmp_var_new(Name, Len); | |
151 | v->val_len = sizeof(int); | |
152 | v->val.integer = xmalloc(sizeof(int)); | |
153 | v->type = type; | |
154 | *(v->val.integer) = ival; | |
155 | return v; | |
156 | } | |
157 | ||
158 | /* Clone a variable list. | |
159 | * | |
160 | * Returns NULL upon error. | |
161 | */ | |
162 | ||
163 | struct variable_list * | |
164 | snmp_var_clone(struct variable_list *Src) { | |
165 | struct variable_list *Dest; | |
166 | ||
167 | #if DEBUG_VARS | |
168 | printf("VARS: Cloning.\n"); | |
169 | #endif | |
170 | ||
171 | Dest = (struct variable_list *) xmalloc(sizeof(struct variable_list)); | |
172 | if (Dest == NULL) { | |
173 | snmp_set_api_error(SNMPERR_OS_ERR); | |
174 | return (NULL); | |
175 | } | |
176 | #if DEBUG_VARS | |
177 | printf("VARS: Copying entire variable list. (Size %d)\n", | |
178 | sizeof(struct variable_list)); | |
179 | #endif | |
180 | ||
181 | memcpy((char *) Dest, (char *) Src, sizeof(struct variable_list)); | |
182 | ||
183 | if (Src->name != NULL) { | |
184 | Dest->name = (oid *) xmalloc(Src->name_length * sizeof(oid)); | |
185 | if (Dest->name == NULL) { | |
186 | snmp_set_api_error(SNMPERR_OS_ERR); | |
187 | xfree(Dest); | |
188 | return (NULL); | |
189 | } | |
190 | #if DEBUG_VARS | |
191 | printf("VARS: Copying name OID. (Size %d)\n", Src->name_length); | |
192 | #endif | |
193 | memcpy((char *) Dest->name, (char *) Src->name, | |
194 | Src->name_length * sizeof(oid)); | |
195 | } | |
196 | /* CISCO Catalyst 2900 returns NULL strings as data of length 0. */ | |
197 | if ((Src->val.string != NULL) && | |
198 | (Src->val_len)) { | |
199 | Dest->val.string = (u_char *) xmalloc(Src->val_len); | |
200 | if (Dest->val.string == NULL) { | |
201 | snmp_set_api_error(SNMPERR_OS_ERR); | |
202 | xfree(Dest->name); | |
203 | xfree(Dest); | |
204 | return (NULL); | |
205 | } | |
206 | #if DEBUG_VARS | |
207 | printf("VARS: Copying value (Size %d)\n", Src->val_len); | |
208 | #endif | |
209 | memcpy((char *) Dest->val.string, (char *) Src->val.string, Src->val_len); | |
210 | } | |
211 | #if DEBUG_VARS | |
212 | printf("VARS: Cloned %x.\n", (unsigned int) Dest); | |
213 | #endif | |
214 | #if DEBUG_VARS_MALLOC | |
215 | printf("VARS: Cloned (%x)\n", (unsigned int) Dest); | |
216 | printf("VARS: Name is (%x)\n", (unsigned int) Dest->name); | |
217 | #endif | |
218 | ||
219 | return (Dest); | |
220 | } | |
221 | ||
222 | /* Free a variable_list. | |
223 | */ | |
224 | void | |
225 | snmp_var_free(struct variable_list *Ptr) | |
226 | { | |
227 | if (Ptr->name) | |
228 | xfree((char *) Ptr->name); | |
229 | ||
230 | if (Ptr->val.string) | |
231 | xfree((char *) Ptr->val.string); | |
232 | else if (Ptr->val.integer) | |
233 | xfree((char *) Ptr->val.integer); | |
234 | ||
235 | xfree(Ptr); | |
236 | } | |
237 | ||
238 | /**********************************************************************/ | |
239 | ||
240 | /* Build a variable binding. | |
241 | * | |
242 | * RFC 1905: Protocol Operations for SNMPv2 | |
243 | * | |
244 | * VarBind ::= | |
245 | * SEQUENCE { | |
246 | * name ObjectName | |
247 | * CHOICE { | |
248 | * value ObjectSyntax | |
249 | * unSpecified NULL | |
250 | * noSuchObject[0] NULL | |
251 | * noSuchInstance[1] NULL | |
252 | * endOfMibView[2] NULL | |
253 | * } | |
254 | * } | |
255 | */ | |
256 | u_char * | |
257 | snmp_var_EncodeVarBind(u_char * Buffer, int *BufLenP, | |
258 | variable_list * VarList, | |
259 | int Version) | |
260 | { | |
261 | struct variable_list *Vars; | |
262 | u_char *bufp; | |
263 | u_char *HeaderStart; | |
264 | u_char *HeaderEnd; | |
265 | int FakeArg = *BufLenP; | |
266 | ||
267 | bufp = Buffer; | |
268 | ||
269 | for (Vars = VarList; Vars; Vars = Vars->next_variable) { | |
270 | ||
271 | /* Build the header for this variable | |
272 | * | |
273 | * Use Maximum size. | |
274 | */ | |
275 | HeaderStart = bufp; | |
276 | HeaderEnd = asn_build_header(HeaderStart, BufLenP, | |
277 | (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), | |
278 | FakeArg); | |
279 | if (HeaderEnd == NULL) | |
280 | return (NULL); | |
281 | ||
282 | /* Now, let's put the Object Identifier into the buffer */ | |
283 | bufp = asn_build_objid(HeaderEnd, BufLenP, | |
284 | (u_char) (ASN_UNIVERSAL | | |
285 | ASN_PRIMITIVE | | |
286 | ASN_OBJECT_ID), | |
287 | Vars->name, Vars->name_length); | |
288 | if (bufp == NULL) | |
289 | return (NULL); | |
290 | ||
291 | /* Now put the data in */ | |
292 | switch (Vars->type) { | |
293 | ||
294 | case ASN_INTEGER: | |
295 | bufp = asn_build_int(bufp, | |
296 | BufLenP, Vars->type, | |
297 | (int *) Vars->val.integer, Vars->val_len); | |
298 | break; | |
299 | ||
300 | case SMI_COUNTER32: | |
301 | case SMI_GAUGE32: | |
302 | /* case SMI_UNSIGNED32: */ | |
303 | case SMI_TIMETICKS: | |
304 | bufp = asn_build_unsigned_int(bufp, BufLenP, | |
305 | Vars->type, | |
306 | (u_int *) Vars->val.integer, Vars->val_len); | |
307 | break; | |
308 | ||
309 | case ASN_OCTET_STR: | |
310 | case SMI_IPADDRESS: | |
311 | case SMI_OPAQUE: | |
312 | bufp = asn_build_string(bufp, BufLenP, Vars->type, | |
313 | Vars->val.string, Vars->val_len); | |
314 | break; | |
315 | ||
316 | case ASN_OBJECT_ID: | |
317 | bufp = asn_build_objid(bufp, BufLenP, Vars->type, | |
318 | (oid *) Vars->val.objid, Vars->val_len / sizeof(oid)); | |
319 | break; | |
320 | ||
321 | case SMI_NOSUCHINSTANCE: | |
322 | case SMI_NOSUCHOBJECT: | |
323 | case SMI_ENDOFMIBVIEW: | |
324 | if (Version == SNMP_VERSION_1) { | |
325 | /* SNMP Version 1 does not support these error codes. */ | |
326 | bufp = asn_build_null(bufp, BufLenP, SMI_NOSUCHOBJECT); | |
327 | } else { | |
328 | bufp = asn_build_exception(bufp, BufLenP, Vars->type); | |
329 | } | |
330 | break; | |
331 | ||
332 | case ASN_NULL: | |
333 | bufp = asn_build_null(bufp, BufLenP, Vars->type); | |
334 | break; | |
335 | ||
336 | case SMI_COUNTER64: | |
337 | snmplib_debug(2, "Unable to encode type SMI_COUNTER64!\n"); | |
338 | /* Fall through */ | |
339 | ||
340 | default: | |
341 | snmp_set_api_error(SNMPERR_UNSUPPORTED_TYPE); | |
342 | return (NULL); | |
343 | } | |
344 | ||
345 | /* ASSERT: bufp should now point to the next valid byte. */ | |
346 | if (bufp == NULL) | |
347 | return (NULL); | |
348 | ||
349 | /* Rebuild the header with the appropriate length */ | |
350 | HeaderEnd = asn_build_header(HeaderStart, &FakeArg, | |
351 | (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), | |
352 | (bufp - HeaderEnd)); | |
353 | ||
354 | /* Returns NULL */ | |
355 | if (HeaderEnd == NULL) | |
356 | return (NULL); | |
357 | ||
358 | } | |
359 | ||
360 | /* or the end of the entire thing */ | |
361 | return (bufp); | |
362 | } | |
363 | ||
364 | /* Parse all Vars from the buffer */ | |
365 | u_char * | |
366 | snmp_var_DecodeVarBind(u_char * Buffer, int *BufLen, | |
367 | struct variable_list ** VarP, | |
368 | int Version) | |
369 | { | |
370 | struct variable_list *Var = NULL, **VarLastP; | |
371 | u_char *bufp, *tmp; | |
372 | u_char VarBindType; | |
373 | u_char *DataPtr; | |
374 | int DataLen; | |
375 | oid TmpBuf[MAX_NAME_LEN]; | |
376 | memset(TmpBuf, 0, MAX_NAME_LEN * sizeof(*TmpBuf)); | |
377 | ||
378 | int AllVarLen = *BufLen; | |
379 | int ThisVarLen = 0; | |
380 | ||
381 | VarLastP = VarP; | |
382 | #if DEBUG_VARS_DECODE | |
383 | printf("VARS: Decoding buffer of length %d\n", *BufLen); | |
384 | #endif | |
385 | ||
386 | /* Now parse the variables */ | |
387 | bufp = asn_parse_header(Buffer, &AllVarLen, &VarBindType); | |
388 | if (bufp == NULL) | |
389 | return (NULL); | |
390 | ||
391 | if (VarBindType != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR)) { | |
392 | snmp_set_api_error(SNMPERR_PDU_PARSE); | |
393 | return (NULL); | |
394 | } | |
395 | #if DEBUG_VARS_DECODE | |
396 | printf("VARS: All Variable length %d\n", AllVarLen); | |
397 | #endif | |
398 | ||
399 | #define PARSE_ERROR { snmp_var_free(Var); return(NULL); } | |
400 | ||
401 | /* We know how long the variable list is. Parse it. */ | |
402 | while ((int) AllVarLen > 0) { | |
403 | ||
404 | /* Create a new variable */ | |
405 | Var = snmp_var_new(NULL, MAX_NAME_LEN); | |
406 | if (Var == NULL) | |
407 | return (NULL); | |
408 | ||
409 | /* Parse the header to find out the length of this variable. */ | |
410 | ThisVarLen = AllVarLen; | |
411 | tmp = asn_parse_header(bufp, &ThisVarLen, &VarBindType); | |
412 | if (tmp == NULL) | |
413 | PARSE_ERROR; | |
414 | ||
415 | /* Now that we know the length , figure out how it relates to | |
416 | * the entire variable list | |
417 | */ | |
418 | AllVarLen = AllVarLen - (ThisVarLen + (tmp - bufp)); | |
419 | bufp = tmp; | |
420 | ||
421 | /* Is it valid? */ | |
422 | if (VarBindType != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR)) { | |
423 | snmp_set_api_error(SNMPERR_PDU_PARSE); | |
424 | PARSE_ERROR; | |
425 | } | |
426 | #if DEBUG_VARS_DECODE | |
427 | printf("VARS: Header type 0x%x (%d bytes left)\n", VarBindType, ThisVarLen); | |
428 | #endif | |
429 | ||
430 | /* Parse the OBJID */ | |
431 | bufp = asn_parse_objid(bufp, &ThisVarLen, &VarBindType, | |
432 | Var->name, &(Var->name_length)); | |
433 | if (bufp == NULL) | |
434 | PARSE_ERROR; | |
435 | ||
436 | if (VarBindType != (u_char) (ASN_UNIVERSAL | | |
437 | ASN_PRIMITIVE | | |
438 | ASN_OBJECT_ID)) { | |
439 | snmp_set_api_error(SNMPERR_PDU_PARSE); | |
440 | PARSE_ERROR; | |
441 | } | |
442 | #if DEBUG_VARS_DECODE | |
443 | printf("VARS: Decoded OBJID (%d bytes). (%d bytes left)\n", | |
444 | Var->name_length, ThisVarLen); | |
445 | #endif | |
446 | ||
447 | /* Keep a pointer to this object */ | |
448 | DataPtr = bufp; | |
449 | DataLen = ThisVarLen; | |
450 | ||
451 | /* find out type of object */ | |
452 | bufp = asn_parse_header(bufp, &ThisVarLen, &(Var->type)); | |
453 | if (bufp == NULL) | |
454 | PARSE_ERROR; | |
455 | ThisVarLen = DataLen; | |
456 | ||
457 | #if DEBUG_VARS_DECODE | |
458 | printf("VARS: Data type %d\n", Var->type); | |
459 | #endif | |
460 | ||
461 | /* Parse the type */ | |
462 | ||
463 | switch ((short) Var->type) { | |
464 | ||
465 | case ASN_INTEGER: | |
466 | Var->val.integer = (int *) xmalloc(sizeof(int)); | |
467 | if (Var->val.integer == NULL) { | |
468 | snmp_set_api_error(SNMPERR_OS_ERR); | |
469 | PARSE_ERROR; | |
470 | } | |
471 | Var->val_len = sizeof(int); | |
472 | bufp = asn_parse_int(DataPtr, &ThisVarLen, | |
473 | &Var->type, (int *) Var->val.integer, | |
474 | Var->val_len); | |
475 | #if DEBUG_VARS_DECODE | |
476 | printf("VARS: Decoded integer '%d' (%d bytes left)\n", | |
477 | *(Var->val.integer), ThisVarLen); | |
478 | #endif | |
479 | break; | |
480 | ||
481 | case SMI_COUNTER32: | |
482 | case SMI_GAUGE32: | |
483 | /* case SMI_UNSIGNED32: */ | |
484 | case SMI_TIMETICKS: | |
485 | Var->val.integer = (int *) xmalloc(sizeof(u_int)); | |
486 | if (Var->val.integer == NULL) { | |
487 | snmp_set_api_error(SNMPERR_OS_ERR); | |
488 | PARSE_ERROR; | |
489 | } | |
490 | Var->val_len = sizeof(u_int); | |
491 | bufp = asn_parse_unsigned_int(DataPtr, &ThisVarLen, | |
492 | &Var->type, (u_int *) Var->val.integer, | |
493 | Var->val_len); | |
494 | #if DEBUG_VARS_DECODE | |
495 | printf("VARS: Decoded timeticks '%d' (%d bytes left)\n", | |
496 | *(Var->val.integer), ThisVarLen); | |
497 | #endif | |
498 | break; | |
499 | ||
500 | case ASN_OCTET_STR: | |
501 | case SMI_IPADDRESS: | |
502 | case SMI_OPAQUE: | |
503 | Var->val_len = *&ThisVarLen; /* String is this at most */ | |
504 | Var->val.string = (u_char *) xmalloc((unsigned) Var->val_len); | |
505 | if (Var->val.string == NULL) { | |
506 | snmp_set_api_error(SNMPERR_OS_ERR); | |
507 | PARSE_ERROR; | |
508 | } | |
509 | bufp = asn_parse_string(DataPtr, &ThisVarLen, | |
510 | &Var->type, Var->val.string, | |
511 | &Var->val_len); | |
512 | #if DEBUG_VARS_DECODE | |
513 | printf("VARS: Decoded string '%s' (length %d) (%d bytes left)\n", | |
514 | (Var->val.string), Var->val_len, ThisVarLen); | |
515 | #endif | |
516 | break; | |
517 | ||
518 | case ASN_OBJECT_ID: | |
519 | Var->val_len = MAX_NAME_LEN; | |
520 | bufp = asn_parse_objid(DataPtr, &ThisVarLen, | |
521 | &Var->type, TmpBuf, &Var->val_len); | |
522 | Var->val_len *= sizeof(oid); | |
523 | Var->val.objid = (oid *) xmalloc((unsigned) Var->val_len); | |
524 | if (Var->val.integer == NULL) { | |
525 | snmp_set_api_error(SNMPERR_OS_ERR); | |
526 | PARSE_ERROR; | |
527 | } | |
528 | /* Only copy if we successfully decoded something */ | |
529 | if (bufp) { | |
530 | memcpy((char *) Var->val.objid, (char *) TmpBuf, Var->val_len); | |
531 | } | |
532 | #if DEBUG_VARS_DECODE | |
533 | printf("VARS: Decoded OBJID (length %d) (%d bytes left)\n", | |
534 | Var->val_len, ThisVarLen); | |
535 | #endif | |
536 | break; | |
537 | ||
538 | case ASN_NULL: | |
539 | case SMI_NOSUCHINSTANCE: | |
540 | case SMI_NOSUCHOBJECT: | |
541 | case SMI_ENDOFMIBVIEW: | |
542 | break; | |
543 | ||
544 | case SMI_COUNTER64: | |
545 | snmplib_debug(2, "Unable to parse type SMI_COUNTER64!\n"); | |
546 | snmp_set_api_error(SNMPERR_UNSUPPORTED_TYPE); | |
547 | PARSE_ERROR; | |
548 | ||
549 | default: | |
550 | snmplib_debug(2, "bad type returned (%x)\n", Var->type); | |
551 | snmp_set_api_error(SNMPERR_PDU_PARSE); | |
552 | PARSE_ERROR; | |
553 | } /* End of var type switch */ | |
554 | ||
555 | if (bufp == NULL) | |
556 | PARSE_ERROR; | |
557 | ||
558 | #if DEBUG_VARS_DECODE | |
559 | printf("VARS: Adding to list.\n"); | |
560 | #endif | |
561 | /* Add variable to the list */ | |
562 | *VarLastP = Var; | |
563 | VarLastP = &(Var->next_variable); | |
564 | } | |
565 | #undef PARSE_ERROR | |
566 | ||
567 | return (bufp); | |
568 | } |