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