/src/net-snmp/snmplib/snmp_enum.c
Line | Count | Source |
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 | | static unsigned int current_maj_num; |
46 | | static 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 | 3.24k | { |
55 | 3.24k | if (NULL != snmp_enum_lists) |
56 | 0 | return SE_OK; |
57 | | |
58 | 3.24k | snmp_enum_lists = calloc(SE_MAX_IDS * SE_MAX_SUBIDS, |
59 | 3.24k | sizeof(*snmp_enum_lists)); |
60 | 3.24k | if (!snmp_enum_lists) |
61 | 0 | return SE_NOMEM; |
62 | 3.24k | current_maj_num = SE_MAX_IDS; |
63 | 3.24k | current_min_num = SE_MAX_SUBIDS; |
64 | | |
65 | 3.24k | register_const_config_handler(type, "enum", se_read_conf, NULL, NULL); |
66 | 3.24k | return SE_OK; |
67 | 3.24k | } |
68 | | |
69 | | void |
70 | | se_read_conf(const char *word, const char *cptr) |
71 | 358 | { |
72 | 358 | int major, minor; |
73 | 358 | int value; |
74 | 358 | const char *cp, *cp2; |
75 | 358 | char e_name[BUFSIZ]; |
76 | 358 | char e_enum[ BUFSIZ]; |
77 | | |
78 | 358 | if (!cptr || *cptr=='\0') |
79 | 10 | return; |
80 | | |
81 | | /* |
82 | | * Extract the first token |
83 | | * (which should be the name of the list) |
84 | | */ |
85 | 348 | cp = copy_nword_const(cptr, e_name, sizeof(e_name)); |
86 | 348 | cp = skip_white_const(cp); |
87 | 348 | if (!cp || *cp=='\0') |
88 | 37 | return; |
89 | | |
90 | | |
91 | | /* |
92 | | * Add each remaining enumeration to the list, |
93 | | * using the appropriate style interface |
94 | | */ |
95 | 311 | if (sscanf(e_name, "%d:%d", &major, &minor) == 2) { |
96 | | /* |
97 | | * Numeric major/minor style |
98 | | */ |
99 | 629 | while (1) { |
100 | 629 | cp = copy_nword_const(cp, e_enum, sizeof(e_enum)); |
101 | 629 | if (sscanf(e_enum, "%d:", &value) != 1) { |
102 | 67 | break; |
103 | 67 | } |
104 | 562 | cp2 = e_enum; |
105 | 3.47k | while (*cp2 != 0 && *cp2++ != ':') |
106 | 2.91k | ; |
107 | 562 | se_add_pair(major, minor, strdup(cp2), value); |
108 | 562 | if (!cp) |
109 | 85 | break; |
110 | 562 | } |
111 | 159 | } else { |
112 | | /* |
113 | | * Named enumeration |
114 | | */ |
115 | 529 | while (1) { |
116 | 529 | cp = copy_nword_const(cp, e_enum, sizeof(e_enum)); |
117 | 529 | if (sscanf(e_enum, "%d:", &value) != 1) { |
118 | 77 | break; |
119 | 77 | } |
120 | 452 | cp2 = e_enum; |
121 | 3.26k | while (*cp2 != 0 && *cp2++ != ':') |
122 | 2.81k | ; |
123 | 452 | se_add_pair_to_slist(e_name, strdup(cp2), value); |
124 | 452 | if (!cp) |
125 | 82 | break; |
126 | 452 | } |
127 | 159 | } |
128 | 311 | } |
129 | | |
130 | | void |
131 | | se_store_enum_list(struct snmp_enum_list *new_list, |
132 | | const char *token, const char *type) |
133 | 0 | { |
134 | 0 | struct snmp_enum_list *listp = new_list; |
135 | 0 | char line[2048]; |
136 | 0 | char buf[512]; |
137 | 0 | int len; |
138 | |
|
139 | 0 | snprintf(line, sizeof(line), "enum %s", token); |
140 | 0 | while (listp) { |
141 | 0 | snprintf(buf, sizeof(buf), " %d:%s", listp->value, listp->label); |
142 | | /* |
143 | | * Calculate the space left in the buffer. |
144 | | * If this is not sufficient to include the next enum, |
145 | | * then save the line so far, and start again. |
146 | | */ |
147 | 0 | len = sizeof(line) - strlen(line); |
148 | 0 | if ((int)strlen(buf) > len) { |
149 | 0 | read_config_store(type, line); |
150 | 0 | snprintf(line, sizeof(line), "enum %s", token); |
151 | 0 | len = sizeof(line) - strlen(line); |
152 | 0 | } |
153 | |
|
154 | 0 | strncat(line, buf, len); |
155 | 0 | listp = listp->next; |
156 | 0 | } |
157 | |
|
158 | 0 | read_config_store(type, line); |
159 | 0 | } |
160 | | |
161 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_LIST |
162 | | void |
163 | | se_store_list(unsigned int major, unsigned int minor, const char *type) |
164 | 0 | { |
165 | 0 | char token[32]; |
166 | |
|
167 | 0 | snprintf(token, sizeof(token), "%d:%d", major, minor); |
168 | 0 | se_store_enum_list(se_find_list(major, minor), token, type); |
169 | 0 | } |
170 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_LIST */ |
171 | | |
172 | | static struct snmp_enum_list ** |
173 | | se_find_list_ptr(unsigned int major, unsigned int minor) |
174 | 519k | { |
175 | 519k | if (major >= current_maj_num || minor >= current_min_num) |
176 | 249 | return NULL; |
177 | 519k | netsnmp_assert(NULL != snmp_enum_lists); |
178 | | |
179 | 519k | return &snmp_enum_lists[major * current_min_num + minor]; |
180 | 519k | } |
181 | | |
182 | | struct snmp_enum_list * |
183 | | se_find_list(unsigned int major, unsigned int minor) |
184 | 0 | { |
185 | 0 | struct snmp_enum_list **p = se_find_list_ptr(major, minor); |
186 | |
|
187 | 0 | return p ? *p : NULL; |
188 | 0 | } |
189 | | |
190 | | int |
191 | | se_find_value_in_list(struct snmp_enum_list *list, const char *label) |
192 | 0 | { |
193 | 0 | if (!list) |
194 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
195 | 0 | while (list) { |
196 | 0 | if (strcmp(list->label, label) == 0) |
197 | 0 | return (list->value); |
198 | 0 | list = list->next; |
199 | 0 | } |
200 | | |
201 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
202 | 0 | } |
203 | | |
204 | | int |
205 | | se_find_casevalue_in_list(struct snmp_enum_list *list, const char *label) |
206 | 0 | { |
207 | 0 | if (!list) |
208 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
209 | 0 | while (list) { |
210 | 0 | if (strcasecmp(list->label, label) == 0) |
211 | 0 | return (list->value); |
212 | 0 | list = list->next; |
213 | 0 | } |
214 | | |
215 | 0 | return SE_DNE; /* XXX: um, no good solution here */ |
216 | 0 | } |
217 | | |
218 | | int |
219 | | se_find_free_value_in_list(struct snmp_enum_list *list) |
220 | 0 | { |
221 | 0 | int max_value = 0; |
222 | 0 | if (!list) |
223 | 0 | return SE_DNE; |
224 | | |
225 | 0 | for (;list; list=list->next) { |
226 | 0 | if (max_value < list->value) |
227 | 0 | max_value = list->value; |
228 | 0 | } |
229 | 0 | return max_value+1; |
230 | 0 | } |
231 | | |
232 | | int |
233 | | se_find_value(unsigned int major, unsigned int minor, const char *label) |
234 | 0 | { |
235 | 0 | return se_find_value_in_list(se_find_list(major, minor), label); |
236 | 0 | } |
237 | | |
238 | | int |
239 | | se_find_free_value(unsigned int major, unsigned int minor) |
240 | 0 | { |
241 | 0 | return se_find_free_value_in_list(se_find_list(major, minor)); |
242 | 0 | } |
243 | | |
244 | | char * |
245 | | se_find_label_in_list(struct snmp_enum_list *list, int value) |
246 | 0 | { |
247 | 0 | if (!list) |
248 | 0 | return NULL; |
249 | 0 | while (list) { |
250 | 0 | if (list->value == value) |
251 | 0 | return (list->label); |
252 | 0 | list = list->next; |
253 | 0 | } |
254 | 0 | return NULL; |
255 | 0 | } |
256 | | |
257 | | char * |
258 | | se_find_label(unsigned int major, unsigned int minor, int value) |
259 | 0 | { |
260 | 0 | return se_find_label_in_list(se_find_list(major, minor), value); |
261 | 0 | } |
262 | | |
263 | | /* |
264 | | * Ownership of 'label' is transferred from the caller to this function. |
265 | | * 'label' is freed if list insertion fails. |
266 | | */ |
267 | | int |
268 | | se_add_pair_to_list(struct snmp_enum_list **list, char *label, int value) |
269 | 182k | { |
270 | 182k | struct snmp_enum_list *lastnode = NULL, *new_node, *tmp; |
271 | | |
272 | 182k | if (!list) { |
273 | 249 | free(label); |
274 | 249 | return SE_DNE; |
275 | 249 | } |
276 | | |
277 | 182k | tmp = *list; |
278 | 826k | while (tmp) { |
279 | 657k | if (tmp->value == value) { |
280 | 13.3k | free(label); |
281 | 13.3k | return (SE_ALREADY_THERE); |
282 | 13.3k | } |
283 | 644k | lastnode = tmp; |
284 | 644k | tmp = tmp->next; |
285 | 644k | } |
286 | | |
287 | 169k | new_node = SNMP_MALLOC_STRUCT(snmp_enum_list); |
288 | 169k | if (!new_node) { |
289 | 0 | free(label); |
290 | 0 | return (SE_NOMEM); |
291 | 0 | } |
292 | | |
293 | 169k | if (lastnode) |
294 | 143k | lastnode->next = new_node; |
295 | 26.1k | else |
296 | 26.1k | *list = new_node; |
297 | 169k | new_node->label = label; |
298 | 169k | new_node->value = value; |
299 | 169k | new_node->next = NULL; |
300 | 169k | return (SE_OK); |
301 | 169k | } |
302 | | |
303 | | int |
304 | | se_add_pair(unsigned int major, unsigned int minor, char *label, int value) |
305 | 562 | { |
306 | 562 | return se_add_pair_to_list(se_find_list_ptr(major, minor), label, value); |
307 | 562 | } |
308 | | |
309 | | /* |
310 | | * remember a list of enums based on a lookup name. |
311 | | */ |
312 | | static struct snmp_enum_list ** |
313 | | se_find_slist_ptr(const char *listname) |
314 | 182k | { |
315 | 182k | struct snmp_enum_list_str *sptr; |
316 | 182k | if (!listname) |
317 | 0 | return NULL; |
318 | | |
319 | 282k | for (sptr = sliststorage; sptr != NULL; sptr = sptr->next) |
320 | 255k | if (sptr->name && strcmp(sptr->name, listname) == 0) |
321 | 156k | return &sptr->list; |
322 | | |
323 | 26.0k | return NULL; |
324 | 182k | } |
325 | | |
326 | | struct snmp_enum_list * |
327 | | se_find_slist(const char *listname) |
328 | 0 | { |
329 | 0 | struct snmp_enum_list **ptr = se_find_slist_ptr(listname); |
330 | 0 | return ptr ? *ptr : NULL; |
331 | 0 | } |
332 | | |
333 | | char * |
334 | | se_find_label_in_slist(const char *listname, int value) |
335 | 0 | { |
336 | 0 | return (se_find_label_in_list(se_find_slist(listname), value)); |
337 | 0 | } |
338 | | |
339 | | int |
340 | | se_find_value_in_slist(const char *listname, const char *label) |
341 | 0 | { |
342 | 0 | return (se_find_value_in_list(se_find_slist(listname), label)); |
343 | 0 | } |
344 | | |
345 | | int |
346 | | se_find_casevalue_in_slist(const char *listname, const char *label) |
347 | 0 | { |
348 | 0 | return (se_find_casevalue_in_list(se_find_slist(listname), label)); |
349 | 0 | } |
350 | | |
351 | | #ifndef NETSNMP_FEATURE_REMOVE_SE_FIND_FREE_VALUE_IN_SLIST |
352 | | int |
353 | | se_find_free_value_in_slist(const char *listname) |
354 | 0 | { |
355 | 0 | return (se_find_free_value_in_list(se_find_slist(listname))); |
356 | 0 | } |
357 | | #endif /* NETSNMP_FEATURE_REMOVE_SE_FIND_FREE_VALUE_IN_SLIST */ |
358 | | |
359 | | /* |
360 | | * Ownership of 'label' is transferred from the caller to this function. |
361 | | * 'label' is freed if list insertion fails. |
362 | | */ |
363 | | int |
364 | | se_add_pair_to_slist(const char *listname, char *label, int value) |
365 | 182k | { |
366 | 182k | struct snmp_enum_list **list_p = se_find_slist_ptr(listname); |
367 | | |
368 | 182k | if (!list_p) { |
369 | 26.0k | struct snmp_enum_list_str *sptr = |
370 | 26.0k | SNMP_MALLOC_STRUCT(snmp_enum_list_str); |
371 | 26.0k | if (!sptr) { |
372 | 0 | free(label); |
373 | 0 | return SE_NOMEM; |
374 | 0 | } |
375 | 26.0k | sptr->next = sliststorage; |
376 | 26.0k | sptr->name = strdup(listname); |
377 | 26.0k | if (!sptr->name) { |
378 | 0 | free(sptr); |
379 | 0 | free(label); |
380 | 0 | return SE_NOMEM; |
381 | 0 | } |
382 | 26.0k | list_p = &sptr->list; |
383 | 26.0k | sliststorage = sptr; |
384 | 26.0k | } |
385 | | |
386 | 182k | return se_add_pair_to_list(list_p, label, value); |
387 | 182k | } |
388 | | |
389 | | static void |
390 | | free_enum_list(struct snmp_enum_list *list) |
391 | 25.9k | { |
392 | 25.9k | struct snmp_enum_list *next; |
393 | | |
394 | 194k | while (list) { |
395 | 168k | next = list->next; |
396 | 168k | SNMP_FREE(list->label); |
397 | 168k | SNMP_FREE(list); |
398 | 168k | list = next; |
399 | 168k | } |
400 | 25.9k | } |
401 | | |
402 | | void |
403 | | clear_snmp_enum(void) |
404 | 6.48k | { |
405 | 6.48k | struct snmp_enum_list_str *sptr = sliststorage, *next = NULL; |
406 | 6.48k | unsigned int major, minor; |
407 | | |
408 | 32.4k | while (sptr != NULL) { |
409 | 25.9k | next = sptr->next; |
410 | 25.9k | free_enum_list(sptr->list); |
411 | 25.9k | SNMP_FREE(sptr->name); |
412 | 25.9k | SNMP_FREE(sptr); |
413 | 25.9k | sptr = next; |
414 | 25.9k | } |
415 | 6.48k | sliststorage = NULL; |
416 | | |
417 | 22.7k | for (major = 0; major < current_maj_num; major++) { |
418 | 535k | for (minor = 0; minor < current_min_num; minor++) { |
419 | 519k | struct snmp_enum_list **list_ptr = se_find_list_ptr(major, minor); |
420 | | |
421 | 519k | if (!list_ptr || !*list_ptr) |
422 | 519k | continue; |
423 | 0 | free_enum_list(*list_ptr); |
424 | 0 | } |
425 | 16.2k | } |
426 | 6.48k | current_maj_num = 0; |
427 | 6.48k | current_min_num = 0; |
428 | 6.48k | SNMP_FREE(snmp_enum_lists); |
429 | 6.48k | } |
430 | | |
431 | | void |
432 | | se_clear_list(struct snmp_enum_list **list) |
433 | 0 | { |
434 | 0 | struct snmp_enum_list *this_entry, *next_entry; |
435 | |
|
436 | 0 | if (!list) |
437 | 0 | return; |
438 | | |
439 | 0 | this_entry = *list; |
440 | 0 | while (this_entry) { |
441 | 0 | next_entry = this_entry->next; |
442 | 0 | SNMP_FREE(this_entry->label); |
443 | 0 | SNMP_FREE(this_entry); |
444 | 0 | this_entry = next_entry; |
445 | 0 | } |
446 | 0 | *list = NULL; |
447 | 0 | return; |
448 | 0 | } |
449 | | |
450 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_SLIST |
451 | | void |
452 | | se_store_slist(const char *listname, const char *type) |
453 | 0 | { |
454 | 0 | struct snmp_enum_list *list = se_find_slist(listname); |
455 | 0 | se_store_enum_list(list, listname, type); |
456 | 0 | } |
457 | | |
458 | | int |
459 | | se_store_slist_callback(int majorID, int minorID, |
460 | | void *serverargs, void *clientargs) |
461 | 0 | { |
462 | 0 | char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
463 | 0 | NETSNMP_DS_LIB_APPTYPE); |
464 | 0 | se_store_slist((char *)clientargs, appname); |
465 | 0 | return SNMPERR_SUCCESS; |
466 | 0 | } |
467 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_STORE_SLIST */ |
468 | | |
469 | | #ifndef NETSNMP_FEATURE_REMOVE_SNMP_ENUM_CLEAR |
470 | | void |
471 | | se_clear_slist(const char *listname) |
472 | 0 | { |
473 | 0 | se_clear_list(se_find_slist_ptr(listname)); |
474 | 0 | } |
475 | | |
476 | | void |
477 | | se_clear_all_lists(void) |
478 | 0 | { |
479 | 0 | struct snmp_enum_list_str *sptr = NULL; |
480 | |
|
481 | 0 | for (sptr = sliststorage; sptr != NULL; sptr = sptr->next) |
482 | 0 | se_clear_list(&(sptr->list)); |
483 | 0 | } |
484 | | #endif /* NETSNMP_FEATURE_REMOVE_SNMP_ENUM_CLEAR */ |