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