/src/net-snmp/snmplib/snmp_enum.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | #include <net-snmp/net-snmp-features.h> |
3 | | |
4 | | /* |
5 | | * Portions of this file are copyrighted by: |
6 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
7 | | * Use is subject to license terms specified in the COPYING file |
8 | | * distributed with the Net-SNMP package. |
9 | | */ |
10 | | |
11 | | #ifdef HAVE_STDLIB_H |
12 | | #include <stdlib.h> |
13 | | #endif |
14 | | #include <stdio.h> |
15 | | #ifdef HAVE_STRING_H |
16 | | #include <string.h> |
17 | | #else |
18 | | #include <strings.h> |
19 | | #endif |
20 | | |
21 | | #include <sys/types.h> |
22 | | |
23 | | #include <net-snmp/types.h> |
24 | | #include <net-snmp/config_api.h> |
25 | | |
26 | | #include <net-snmp/library/snmp_enum.h> |
27 | | #include <net-snmp/library/tools.h> |
28 | | #include <net-snmp/library/system.h> /* strcasecmp() */ |
29 | | #include <net-snmp/library/snmp_assert.h> |
30 | | |
31 | | netsnmp_feature_child_of(snmp_enum_all, libnetsnmp); |
32 | | |
33 | | netsnmp_feature_child_of(se_find_free_value_in_slist, snmp_enum_all); |
34 | | netsnmp_feature_child_of(snmp_enum_store_list, snmp_enum_all); |
35 | | netsnmp_feature_child_of(snmp_enum_store_slist, snmp_enum_all); |
36 | | netsnmp_feature_child_of(snmp_enum_clear, snmp_enum_all); |
37 | | |
38 | | struct snmp_enum_list_str { |
39 | | char *name; |
40 | | struct snmp_enum_list *list; |
41 | | struct snmp_enum_list_str *next; |
42 | | }; |
43 | | |
44 | | static struct snmp_enum_list ***snmp_enum_lists; |
45 | | unsigned int current_maj_num; |
46 | | unsigned int current_min_num; |
47 | | static struct snmp_enum_list_str *sliststorage; |
48 | | |
49 | | static void |
50 | | free_enum_list(struct snmp_enum_list *list); |
51 | | |
52 | | int |
53 | | init_snmp_enum(const char *type) |
54 | 2.62k | { |
55 | 2.62k | int i; |
56 | | |
57 | 2.62k | if (NULL != snmp_enum_lists) |
58 | 0 | return SE_OK; |
59 | | |
60 | 2.62k | snmp_enum_lists = (struct snmp_enum_list ***) |
61 | 2.62k | calloc(1, sizeof(struct snmp_enum_list **) * SE_MAX_IDS); |
62 | 2.62k | if (!snmp_enum_lists) |
63 | 0 | return SE_NOMEM; |
64 | 2.62k | current_maj_num = SE_MAX_IDS; |
65 | | |
66 | 15.7k | for (i = 0; i < SE_MAX_IDS; i++) { |
67 | 13.1k | if (!snmp_enum_lists[i]) |
68 | 13.1k | snmp_enum_lists[i] = (struct snmp_enum_list **) |
69 | 13.1k | calloc(1, sizeof(struct snmp_enum_list *) * SE_MAX_SUBIDS); |
70 | 13.1k | if (!snmp_enum_lists[i]) |
71 | 0 | return SE_NOMEM; |
72 | 13.1k | } |
73 | 2.62k | current_min_num = SE_MAX_SUBIDS; |
74 | | |
75 | 2.62k | register_const_config_handler(type, "enum", se_read_conf, NULL, NULL); |
76 | 2.62k | return SE_OK; |
77 | 2.62k | } |
78 | | |
79 | | int |
80 | | se_store_in_list(struct snmp_enum_list *new_list, |
81 | | unsigned int major, unsigned int minor) |
82 | 0 | { |
83 | 0 | int ret = SE_OK; |
84 | |
|
85 | 0 | if (major > current_maj_num || minor > current_min_num) { |
86 | | /* |
87 | | * XXX: realloc |
88 | | */ |
89 | 0 | return SE_NOMEM; |
90 | 0 | } |
91 | 0 | netsnmp_assert(NULL != snmp_enum_lists); |
92 | |
|
93 | 0 | if (snmp_enum_lists[major][minor] != NULL) |
94 | 0 | ret = SE_ALREADY_THERE; |
95 | |
|
96 | 0 | snmp_enum_lists[major][minor] = new_list; |
97 | |
|
98 | 0 | return ret; |
99 | 0 | } |
100 | | |
101 | | void |
102 | | se_read_conf(const char *word, const char *cptr) |
103 | 0 | { |
104 | 0 | int major, minor; |
105 | 0 | int value; |
106 | 0 | const char *cp, *cp2; |
107 | 0 | char e_name[BUFSIZ]; |
108 | 0 | char e_enum[ BUFSIZ]; |
109 | |
|
110 | 0 | if (!cptr || *cptr=='\0') |
111 | 0 | return; |
112 | | |
113 | | /* |
114 | | * Extract the first token |
115 | | * (which should be the name of the list) |
116 | | */ |
117 | 0 | cp = copy_nword_const(cptr, e_name, sizeof(e_name)); |
118 | 0 | cp = skip_white_const(cp); |
119 | 0 | if (!cp || *cp=='\0') |
120 | 0 | return; |
121 | | |
122 | | |
123 | | /* |
124 | | * Add each remaining enumeration to the list, |
125 | | * using the appropriate style interface |
126 | | */ |
127 | 0 | if (sscanf(e_name, "%d:%d", &major, &minor) == 2) { |
128 | | /* |
129 | | * Numeric major/minor style |
130 | | */ |
131 | 0 | while (1) { |
132 | 0 | cp = copy_nword_const(cp, e_enum, sizeof(e_enum)); |
133 | 0 | if (sscanf(e_enum, "%d:", &value) != 1) { |
134 | 0 | break; |
135 | 0 | } |
136 | 0 | cp2 = e_enum; |
137 | 0 | while (*(cp2++) != ':') |
138 | 0 | ; |
139 | 0 | se_add_pair(major, minor, strdup(cp2), value); |
140 | 0 | if (!cp) |
141 | 0 | break; |
142 | 0 | } |
143 | 0 | } else { |
144 | | /* |
145 | | * Named enumeration |
146 | | */ |
147 | 0 | while (1) { |
148 | 0 | cp = copy_nword_const(cp, e_enum, sizeof(e_enum)); |
149 | 0 | if (sscanf(e_enum, "%d:", &value) != 1) { |
150 | 0 | break; |
151 | 0 | } |
152 | 0 | cp2 = e_enum; |
153 | 0 | while (*(cp2++) != ':') |
154 | 0 | ; |
155 | 0 | se_add_pair_to_slist(e_name, strdup(cp2), value); |
156 | 0 | if (!cp) |
157 | 0 | break; |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | void |
163 | | se_store_enum_list(struct snmp_enum_list *new_list, |
164 | | const char *token, const char *type) |
165 | 0 | { |
166 | 0 | struct snmp_enum_list *listp = new_list; |
167 | 0 | char line[2048]; |
168 | 0 | char buf[512]; |
169 | 0 | int len; |
170 | |
|
171 | 0 | snprintf(line, sizeof(line), "enum %s", token); |
172 | 0 | while (listp) { |
173 | 0 | snprintf(buf, sizeof(buf), " %d:%s", listp->value, listp->label); |
174 | | /* |
175 | | * Calculate the space left in the buffer. |
176 | | * If this is not sufficient to include the next enum, |
177 | | * then save the line so far, and start again. |
178 | | */ |
179 | 0 | len = sizeof(line) - strlen(line); |
180 | 0 | if ((int)strlen(buf) > len) { |
181 | 0 | read_config_store(type, line); |
182 | 0 | snprintf(line, sizeof(line), "enum %s", token); |
183 | 0 | len = sizeof(line) - strlen(line); |
184 | 0 | } |
185 | |
|
186 | 0 | strncat(line, buf, len); |
187 | 0 | listp = listp->next; |
188 | 0 | } |
189 | |
|
190 | 0 | read_config_store(type, line); |
191 | 0 | } |
192 | | |
193 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_LIST |
194 | | void |
195 | | se_store_list(unsigned int major, unsigned int minor, const char *type) |
196 | 0 | { |
197 | 0 | char token[32]; |
198 | |
|
199 | 0 | snprintf(token, sizeof(token), "%d:%d", major, minor); |
200 | 0 | se_store_enum_list(se_find_list(major, minor), token, type); |
201 | 0 | } |
202 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_LIST */ |
203 | | |
204 | | struct snmp_enum_list * |
205 | | se_find_list(unsigned int major, unsigned int minor) |
206 | 0 | { |
207 | 0 | if (major > current_maj_num || minor > current_min_num) |
208 | 0 | return NULL; |
209 | 0 | netsnmp_assert(NULL != snmp_enum_lists); |
210 | |
|
211 | 0 | return snmp_enum_lists[major][minor]; |
212 | 0 | } |
213 | | |
214 | | int |
215 | | se_find_value_in_list(struct snmp_enum_list *list, const char *label) |
216 | 0 | { |
217 | 0 | if (!list) |
218 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
219 | 0 | while (list) { |
220 | 0 | if (strcmp(list->label, label) == 0) |
221 | 0 | return (list->value); |
222 | 0 | list = list->next; |
223 | 0 | } |
224 | | |
225 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
226 | 0 | } |
227 | | |
228 | | int |
229 | | se_find_casevalue_in_list(struct snmp_enum_list *list, const char *label) |
230 | 0 | { |
231 | 0 | if (!list) |
232 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
233 | 0 | while (list) { |
234 | 0 | if (strcasecmp(list->label, label) == 0) |
235 | 0 | return (list->value); |
236 | 0 | list = list->next; |
237 | 0 | } |
238 | | |
239 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
240 | 0 | } |
241 | | |
242 | | int |
243 | | se_find_free_value_in_list(struct snmp_enum_list *list) |
244 | 0 | { |
245 | 0 | int max_value = 0; |
246 | 0 | if (!list) |
247 | 0 | return SE_DNE; |
248 | | |
249 | 0 | for (;list; list=list->next) { |
250 | 0 | if (max_value < list->value) |
251 | 0 | max_value = list->value; |
252 | 0 | } |
253 | 0 | return max_value+1; |
254 | 0 | } |
255 | | |
256 | | int |
257 | | se_find_value(unsigned int major, unsigned int minor, const char *label) |
258 | 0 | { |
259 | 0 | return se_find_value_in_list(se_find_list(major, minor), label); |
260 | 0 | } |
261 | | |
262 | | int |
263 | | se_find_free_value(unsigned int major, unsigned int minor) |
264 | 0 | { |
265 | 0 | return se_find_free_value_in_list(se_find_list(major, minor)); |
266 | 0 | } |
267 | | |
268 | | char * |
269 | | se_find_label_in_list(struct snmp_enum_list *list, int value) |
270 | 0 | { |
271 | 0 | if (!list) |
272 | 0 | return NULL; |
273 | 0 | while (list) { |
274 | 0 | if (list->value == value) |
275 | 0 | return (list->label); |
276 | 0 | list = list->next; |
277 | 0 | } |
278 | 0 | return NULL; |
279 | 0 | } |
280 | | |
281 | | char * |
282 | | se_find_label(unsigned int major, unsigned int minor, int value) |
283 | 0 | { |
284 | 0 | return se_find_label_in_list(se_find_list(major, minor), value); |
285 | 0 | } |
286 | | |
287 | | int |
288 | | se_add_pair_to_list(struct snmp_enum_list **list, char *label, int value) |
289 | 147k | { |
290 | 147k | struct snmp_enum_list *lastnode = NULL, *tmp; |
291 | | |
292 | 147k | if (!list) |
293 | 0 | return SE_DNE; |
294 | | |
295 | 147k | tmp = *list; |
296 | 667k | while (tmp) { |
297 | 530k | if (tmp->value == value) { |
298 | 10.5k | free(label); |
299 | 10.5k | return (SE_ALREADY_THERE); |
300 | 10.5k | } |
301 | 520k | lastnode = tmp; |
302 | 520k | tmp = tmp->next; |
303 | 520k | } |
304 | | |
305 | 136k | if (lastnode) { |
306 | 115k | lastnode->next = SNMP_MALLOC_STRUCT(snmp_enum_list); |
307 | 115k | lastnode = lastnode->next; |
308 | 115k | } else { |
309 | 21.0k | (*list) = SNMP_MALLOC_STRUCT(snmp_enum_list); |
310 | 21.0k | lastnode = (*list); |
311 | 21.0k | } |
312 | 136k | if (!lastnode) { |
313 | 0 | free(label); |
314 | 0 | return (SE_NOMEM); |
315 | 0 | } |
316 | 136k | lastnode->label = label; |
317 | 136k | lastnode->value = value; |
318 | 136k | lastnode->next = NULL; |
319 | 136k | return (SE_OK); |
320 | 136k | } |
321 | | |
322 | | int |
323 | | se_add_pair(unsigned int major, unsigned int minor, char *label, int value) |
324 | 0 | { |
325 | 0 | struct snmp_enum_list *list = se_find_list(major, minor); |
326 | 0 | int created = (list) ? 1 : 0; |
327 | 0 | int ret = se_add_pair_to_list(&list, label, value); |
328 | 0 | if (!created) |
329 | 0 | se_store_in_list(list, major, minor); |
330 | 0 | return ret; |
331 | 0 | } |
332 | | |
333 | | /* |
334 | | * remember a list of enums based on a lookup name. |
335 | | */ |
336 | | static struct snmp_enum_list ** |
337 | | se_find_slist_ptr(const char *listname) |
338 | 147k | { |
339 | 147k | struct snmp_enum_list_str *sptr; |
340 | 147k | if (!listname) |
341 | 0 | return NULL; |
342 | | |
343 | 220k | for (sptr = sliststorage; sptr != NULL; sptr = sptr->next) |
344 | 199k | if (sptr->name && strcmp(sptr->name, listname) == 0) |
345 | 126k | return &sptr->list; |
346 | | |
347 | 21.0k | return NULL; |
348 | 147k | } |
349 | | |
350 | | struct snmp_enum_list * |
351 | | se_find_slist(const char *listname) |
352 | 147k | { |
353 | 147k | struct snmp_enum_list **ptr = se_find_slist_ptr(listname); |
354 | 147k | return ptr ? *ptr : NULL; |
355 | 147k | } |
356 | | |
357 | | char * |
358 | | se_find_label_in_slist(const char *listname, int value) |
359 | 0 | { |
360 | 0 | return (se_find_label_in_list(se_find_slist(listname), value)); |
361 | 0 | } |
362 | | |
363 | | int |
364 | | se_find_value_in_slist(const char *listname, const char *label) |
365 | 0 | { |
366 | 0 | return (se_find_value_in_list(se_find_slist(listname), label)); |
367 | 0 | } |
368 | | |
369 | | int |
370 | | se_find_casevalue_in_slist(const char *listname, const char *label) |
371 | 0 | { |
372 | 0 | return (se_find_casevalue_in_list(se_find_slist(listname), label)); |
373 | 0 | } |
374 | | |
375 | | #ifndef NETSNMP_FEATURE_REMOVE_SE_FIND_FREE_VALUE_IN_SLIST |
376 | | int |
377 | | se_find_free_value_in_slist(const char *listname) |
378 | 0 | { |
379 | 0 | return (se_find_free_value_in_list(se_find_slist(listname))); |
380 | 0 | } |
381 | | #endif /* NETSNMP_FEATURE_REMOVE_SE_FIND_FREE_VALUE_IN_SLIST */ |
382 | | |
383 | | int |
384 | | se_add_pair_to_slist(const char *listname, char *label, int value) |
385 | 147k | { |
386 | 147k | struct snmp_enum_list *list = se_find_slist(listname); |
387 | 147k | int created = (list) ? 1 : 0; |
388 | 147k | int ret = se_add_pair_to_list(&list, label, value); |
389 | | |
390 | 147k | if (!created) { |
391 | 21.0k | struct snmp_enum_list_str *sptr = |
392 | 21.0k | SNMP_MALLOC_STRUCT(snmp_enum_list_str); |
393 | 21.0k | if (!sptr) { |
394 | 0 | free_enum_list(list); |
395 | 0 | return SE_NOMEM; |
396 | 0 | } |
397 | 21.0k | sptr->next = sliststorage; |
398 | 21.0k | sptr->name = strdup(listname); |
399 | 21.0k | if (!sptr->name) { |
400 | 0 | free(sptr); |
401 | 0 | free_enum_list(list); |
402 | 0 | return SE_NOMEM; |
403 | 0 | } |
404 | 21.0k | sptr->list = list; |
405 | 21.0k | sliststorage = sptr; |
406 | 21.0k | } |
407 | 147k | return ret; |
408 | 147k | } |
409 | | |
410 | | static void |
411 | | free_enum_list(struct snmp_enum_list *list) |
412 | 21.0k | { |
413 | 21.0k | struct snmp_enum_list *next; |
414 | | |
415 | 157k | while (list) { |
416 | 136k | next = list->next; |
417 | 136k | SNMP_FREE(list->label); |
418 | 136k | SNMP_FREE(list); |
419 | 136k | list = next; |
420 | 136k | } |
421 | 21.0k | } |
422 | | |
423 | | void |
424 | | clear_snmp_enum(void) |
425 | 5.25k | { |
426 | 5.25k | struct snmp_enum_list_str *sptr = sliststorage, *next = NULL; |
427 | 5.25k | int i, j; |
428 | | |
429 | 26.2k | while (sptr != NULL) { |
430 | 21.0k | next = sptr->next; |
431 | 21.0k | free_enum_list(sptr->list); |
432 | 21.0k | SNMP_FREE(sptr->name); |
433 | 21.0k | SNMP_FREE(sptr); |
434 | 21.0k | sptr = next; |
435 | 21.0k | } |
436 | 5.25k | sliststorage = NULL; |
437 | | |
438 | 5.25k | if (snmp_enum_lists) { |
439 | 15.7k | for (i = 0; i < SE_MAX_IDS; i++) { |
440 | 13.1k | if (snmp_enum_lists[i]) { |
441 | 433k | for (j = 0; j < SE_MAX_SUBIDS; j++) { |
442 | 420k | if (snmp_enum_lists[i][j]) |
443 | 0 | free_enum_list(snmp_enum_lists[i][j]); |
444 | 420k | } |
445 | 13.1k | SNMP_FREE(snmp_enum_lists[i]); |
446 | 13.1k | } |
447 | 13.1k | } |
448 | 2.62k | SNMP_FREE(snmp_enum_lists); |
449 | 2.62k | } |
450 | 5.25k | } |
451 | | |
452 | | void |
453 | | se_clear_list(struct snmp_enum_list **list) |
454 | 0 | { |
455 | 0 | struct snmp_enum_list *this_entry, *next_entry; |
456 | |
|
457 | 0 | if (!list) |
458 | 0 | return; |
459 | | |
460 | 0 | this_entry = *list; |
461 | 0 | while (this_entry) { |
462 | 0 | next_entry = this_entry->next; |
463 | 0 | SNMP_FREE(this_entry->label); |
464 | 0 | SNMP_FREE(this_entry); |
465 | 0 | this_entry = next_entry; |
466 | 0 | } |
467 | 0 | *list = NULL; |
468 | 0 | return; |
469 | 0 | } |
470 | | |
471 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_SLIST |
472 | | void |
473 | | se_store_slist(const char *listname, const char *type) |
474 | 0 | { |
475 | 0 | struct snmp_enum_list *list = se_find_slist(listname); |
476 | 0 | se_store_enum_list(list, listname, type); |
477 | 0 | } |
478 | | |
479 | | int |
480 | | se_store_slist_callback(int majorID, int minorID, |
481 | | void *serverargs, void *clientargs) |
482 | 0 | { |
483 | 0 | char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
484 | 0 | NETSNMP_DS_LIB_APPTYPE); |
485 | 0 | se_store_slist((char *)clientargs, appname); |
486 | 0 | return SNMPERR_SUCCESS; |
487 | 0 | } |
488 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_SLIST */ |
489 | | |
490 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_CLEAR |
491 | | void |
492 | | se_clear_slist(const char *listname) |
493 | 0 | { |
494 | 0 | se_clear_list(se_find_slist_ptr(listname)); |
495 | 0 | } |
496 | | |
497 | | void |
498 | | se_clear_all_lists(void) |
499 | 0 | { |
500 | 0 | struct snmp_enum_list_str *sptr = NULL; |
501 | |
|
502 | 0 | for (sptr = sliststorage; sptr != NULL; sptr = sptr->next) |
503 | 0 | se_clear_list(&(sptr->list)); |
504 | 0 | } |
505 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_CLEAR */ |