1 /*
2  * agent_registry.c
3  */
4 /* Portions of this file are subject to the following copyright(s).  See
5  * the Net-SNMP's COPYING file for more details and other copyrights
6  * that may apply:
7  */
8 /*
9  * Portions of this file are copyrighted by:
10  * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
11  * Use is subject to license terms specified in the COPYING file
12  * distributed with the Net-SNMP package.
13  *
14  * Portions of this file are copyrighted by:
15  * Copyright (c) 2016 VMware, Inc. All rights reserved.
16  * Use is subject to license terms specified in the COPYING file
17  * distributed with the Net-SNMP package.
18  */
19 /** @defgroup agent_registry Registry of MIB subtrees, modules, sessions, etc
20  *     Maintain a registry of MIB subtrees, together with related information
21  *     regarding MIB modules, sessions, etc
22  *   @ingroup agent
23  *
24  * @{
25  */
26 
27 #define IN_SNMP_VARS_C
28 
29 #include <net-snmp/net-snmp-config.h>
30 #include <net-snmp/net-snmp-features.h>
31 
32 #include <signal.h>
33 #if HAVE_STRING_H
34 #include <string.h>
35 #endif
36 #if HAVE_STDLIB_H
37 #include <stdlib.h>
38 #endif
39 #include <sys/types.h>
40 #include <stdio.h>
41 #include <fcntl.h>
42 #if TIME_WITH_SYS_TIME
43 # include <sys/time.h>
44 # include <time.h>
45 #else
46 # if HAVE_SYS_TIME_H
47 #  include <sys/time.h>
48 # else
49 #  include <time.h>
50 # endif
51 #endif
52 #if HAVE_NETINET_IN_H
53 #include <netinet/in.h>
54 #endif
55 
56 #include <net-snmp/net-snmp-includes.h>
57 #include <net-snmp/library/snmp_assert.h>
58 #include <net-snmp/agent/net-snmp-agent-includes.h>
59 #include <net-snmp/agent/agent_callbacks.h>
60 
61 #include "snmpd.h"
62 #include "agent_global_vars.h"
63 #include "mibgroup/struct.h"
64 #include <net-snmp/agent/old_api.h>
65 #include <net-snmp/agent/null.h>
66 #include <net-snmp/agent/table.h>
67 #include <net-snmp/agent/table_iterator.h>
68 #include <net-snmp/agent/agent_index.h>
69 #include <net-snmp/agent/agent_registry.h>
70 
71 #ifdef USING_AGENTX_SUBAGENT_MODULE
72 #include "agentx/subagent.h"
73 #include "agentx/client.h"
74 #endif
75 
76 netsnmp_feature_child_of(agent_registry_all, libnetsnmpagent);
77 
78 netsnmp_feature_child_of(unregister_mib_table_row, agent_registry_all);
79 
80 /** @defgroup agent_lookup_cache Lookup cache, storing the registered OIDs.
81  *     Maintain the cache used for locating sub-trees and OIDs.
82  *   @ingroup agent_registry
83  *
84  * @{
85  */
86 
87 /**  Lookup cache - default size.*/
88 #define SUBTREE_DEFAULT_CACHE_SIZE 8
89 /**  Lookup cache - max acceptable size.*/
90 #define SUBTREE_MAX_CACHE_SIZE     32
91 int lookup_cache_size = 0; /*enabled later after registrations are loaded */
92 
93 typedef struct lookup_cache_s {
94    netsnmp_subtree *next;
95    netsnmp_subtree *previous;
96 } lookup_cache;
97 
98 typedef struct lookup_cache_context_s {
99    char *context;
100    struct lookup_cache_context_s *next;
101    int thecachecount;
102    int currentpos;
103    lookup_cache cache[SUBTREE_MAX_CACHE_SIZE];
104 } lookup_cache_context;
105 
106 static lookup_cache_context *thecontextcache = NULL;
107 
108 /** Set the lookup cache size for optimized agent registration performance.
109  * Note that it is only used by master agent - sub-agent doesn't need the cache.
110  * The rough guide is that the cache size should be equal to the maximum
111  * number of simultaneous managers you expect to talk to the agent (M) times 80%
112  * (or so, he says randomly) the average number (N) of varbinds you
113  * expect to receive in a given request for a manager.  ie, M times N.
114  * Bigger does NOT necessarily mean better.  Certainly 16 should be an
115  * upper limit.  32 is the hard coded limit.
116  *
117  * @param newsize set to the maximum size of a cache for a given
118  * context.  Set to 0 to completely disable caching, or to -1 to set
119  * to the default cache size (8), or to a number of your chosing.  The
120  */
121 void
netsnmp_set_lookup_cache_size(int newsize)122 netsnmp_set_lookup_cache_size(int newsize) {
123     if (newsize < 0)
124         lookup_cache_size = SUBTREE_DEFAULT_CACHE_SIZE;
125     else if (newsize < SUBTREE_MAX_CACHE_SIZE)
126         lookup_cache_size = newsize;
127     else
128         lookup_cache_size = SUBTREE_MAX_CACHE_SIZE;
129 }
130 
131 /** Retrieves the current value of the lookup cache size
132  *  Should be called from master agent only - sub-agent doesn't need the cache.
133  *
134  *  @return the current lookup cache size
135  */
136 int
netsnmp_get_lookup_cache_size(void)137 netsnmp_get_lookup_cache_size(void) {
138     return lookup_cache_size;
139 }
140 
141 /** Returns lookup cache entry for the context of given name.
142  *
143  *  @param context Name of the context. Name is case sensitive.
144  *
145  *  @return the lookup cache context
146  */
147 NETSNMP_STATIC_INLINE lookup_cache_context *
get_context_lookup_cache(const char * context)148 get_context_lookup_cache(const char *context) {
149     lookup_cache_context *ptr;
150     if (!context)
151         context = "";
152 
153     for(ptr = thecontextcache; ptr; ptr = ptr->next) {
154         if (strcmp(ptr->context, context) == 0)
155             break;
156     }
157     if (!ptr) {
158         if (netsnmp_subtree_find_first(context)) {
159             ptr = SNMP_MALLOC_TYPEDEF(lookup_cache_context);
160             ptr->next = thecontextcache;
161             ptr->context = strdup(context);
162             thecontextcache = ptr;
163         } else {
164             return NULL;
165         }
166     }
167     return ptr;
168 }
169 
170 /** Adds an entry to the Lookup Cache under specified context name.
171  *
172  *  @param context  Name of the context. Name is case sensitive.
173  *
174  *  @param next     Next subtree item.
175  *
176  *  @param previous Previous subtree item.
177  */
178 NETSNMP_STATIC_INLINE void
lookup_cache_add(const char * context,netsnmp_subtree * next,netsnmp_subtree * previous)179 lookup_cache_add(const char *context,
180                  netsnmp_subtree *next, netsnmp_subtree *previous) {
181     lookup_cache_context *cptr;
182 
183     if ((cptr = get_context_lookup_cache(context)) == NULL)
184         return;
185 
186     if (cptr->thecachecount < lookup_cache_size)
187         cptr->thecachecount++;
188 
189     cptr->cache[cptr->currentpos].next = next;
190     cptr->cache[cptr->currentpos].previous = previous;
191 
192     if (++cptr->currentpos >= lookup_cache_size)
193         cptr->currentpos = 0;
194 }
195 
196 /** @private
197  *  Replaces next and previous pointer in given Lookup Cache.
198  *
199  *  @param ptr      Lookup Cache pointer.
200  *
201  *  @param next     Next subtree item.
202  *
203  *  @param previous Previous subtree item.
204  */
205 NETSNMP_STATIC_INLINE void
lookup_cache_replace(lookup_cache * ptr,netsnmp_subtree * next,netsnmp_subtree * previous)206 lookup_cache_replace(lookup_cache *ptr,
207                      netsnmp_subtree *next, netsnmp_subtree *previous) {
208 
209     ptr->next = next;
210     ptr->previous = previous;
211 }
212 
213 /** Finds an entry in the Lookup Cache.
214  *
215  *  @param context  Case sensitive name of the context.
216  *
217  *  @param name     The OID we're searching for.
218  *
219  *  @param name_len Number of sub-ids (single integers) in the OID.
220  *
221  *  @param retcmp   Value set to snmp_oid_compare() call result.
222  *                  The value, if set, is always nonnegative.
223  *
224  *  @return gives Lookup Cache entry, or NULL if not found.
225  *
226  *  @see snmp_oid_compare()
227  */
228 NETSNMP_STATIC_INLINE lookup_cache *
lookup_cache_find(const char * context,const oid * name,size_t name_len,int * retcmp)229 lookup_cache_find(const char *context, const oid *name, size_t name_len,
230                   int *retcmp) {
231     lookup_cache_context *cptr;
232     lookup_cache *ret = NULL;
233     int cmp;
234     int i;
235 
236     if ((cptr = get_context_lookup_cache(context)) == NULL)
237         return NULL;
238 
239     for(i = 0; i < cptr->thecachecount && i < lookup_cache_size; i++) {
240         if (cptr->cache[i].previous->start_a)
241             cmp = snmp_oid_compare(name, name_len,
242                                    cptr->cache[i].previous->start_a,
243                                    cptr->cache[i].previous->start_len);
244         else
245             cmp = 1;
246         if (cmp >= 0) {
247             *retcmp = cmp;
248             ret = &(cptr->cache[i]);
249         }
250     }
251     return ret;
252 }
253 
254 /** @private
255  *  Clears cache count and position in Lookup Cache.
256  */
257 NETSNMP_STATIC_INLINE void
invalidate_lookup_cache(const char * context)258 invalidate_lookup_cache(const char *context) {
259     lookup_cache_context *cptr;
260     if ((cptr = get_context_lookup_cache(context)) != NULL) {
261         cptr->thecachecount = 0;
262         cptr->currentpos = 0;
263     }
264 }
265 
266 void
clear_lookup_cache(void)267 clear_lookup_cache(void) {
268 
269     lookup_cache_context *ptr = NULL, *next = NULL;
270 
271     ptr = thecontextcache;
272     while (ptr) {
273 	next = ptr->next;
274 	SNMP_FREE(ptr->context);
275 	SNMP_FREE(ptr);
276 	ptr = next;
277     }
278     thecontextcache = NULL; /* !!! */
279 }
280 
281 /**  @} */
282 /* End of Lookup cache code */
283 
284 /** @defgroup agent_context_cache Context cache, storing the OIDs under their contexts.
285  *     Maintain the cache used for locating sub-trees registered under different contexts.
286  *   @ingroup agent_registry
287  *
288  * @{
289  */
290 subtree_context_cache *context_subtrees = NULL;
291 
292 /** Returns the top element of context subtrees cache.
293  *  Use it if you wish to sweep through the cache elements.
294  *  Note that the return may be NULL (cache may be empty).
295  *
296  *  @return pointer to topmost context subtree cache element.
297  */
298 subtree_context_cache *
get_top_context_cache(void)299 get_top_context_cache(void)
300 {
301     return context_subtrees;
302 }
303 
304 /** Finds the first subtree registered under given context.
305  *
306  *  @param context_name Text name of the context we're searching for.
307  *
308  *  @return pointer to the first subtree element, or NULL if not found.
309  */
310 netsnmp_subtree *
netsnmp_subtree_find_first(const char * context_name)311 netsnmp_subtree_find_first(const char *context_name)
312 {
313     subtree_context_cache *ptr;
314 
315     if (!context_name) {
316         context_name = "";
317     }
318 
319     DEBUGMSGTL(("subtree", "looking for subtree for context: \"%s\"\n",
320 		context_name));
321     for (ptr = context_subtrees; ptr != NULL; ptr = ptr->next) {
322         if (ptr->context_name != NULL &&
323 	    strcmp(ptr->context_name, context_name) == 0) {
324             DEBUGMSGTL(("subtree", "found one for: \"%s\"\n", context_name));
325             return ptr->first_subtree;
326         }
327     }
328     DEBUGMSGTL(("subtree", "didn't find a subtree for context: \"%s\"\n",
329 		context_name));
330     return NULL;
331 }
332 
333 /** Adds the subtree to Context Cache under given context name.
334  *
335  *  @param context_name Text name of the context we're adding.
336  *
337  *  @param new_tree The subtree to be added.
338  *
339  *  @return copy of the new_tree pointer, or NULL if cannot add.
340  */
341 netsnmp_subtree *
add_subtree(netsnmp_subtree * new_tree,const char * context_name)342 add_subtree(netsnmp_subtree *new_tree, const char *context_name)
343 {
344     subtree_context_cache *ptr = SNMP_MALLOC_TYPEDEF(subtree_context_cache);
345 
346     if (!context_name) {
347         context_name = "";
348     }
349 
350     if (!ptr) {
351         return NULL;
352     }
353 
354     DEBUGMSGTL(("subtree", "adding subtree for context: \"%s\"\n",
355 		context_name));
356 
357     ptr->next = context_subtrees;
358     ptr->first_subtree = new_tree;
359     ptr->context_name = strdup(context_name);
360     context_subtrees = ptr;
361 
362     return ptr->first_subtree;
363 }
364 
365 void
netsnmp_remove_subtree(netsnmp_subtree * tree)366 netsnmp_remove_subtree(netsnmp_subtree *tree)
367 {
368     subtree_context_cache *ptr;
369 
370     if (!tree->prev) {
371         for (ptr = context_subtrees; ptr; ptr = ptr->next)
372             if (ptr->first_subtree == tree)
373                 break;
374         netsnmp_assert(ptr);
375         if (ptr)
376             ptr->first_subtree = tree->next;
377     } else
378         tree->prev->next = tree->next;
379 
380     if (tree->next)
381         tree->next->prev = tree->prev;
382 }
383 
384 /** Replaces first subtree registered under given context name.
385  *  Overwrites a subtree pointer in Context Cache for the context name.
386  *  The previous subtree pointer is lost. If there's no subtree
387  *  under the supplied name, then a new cache item is created.
388  *
389  *  @param new_tree     The new subtree to be set.
390  *
391  *  @param context_name Text name of the context we're replacing.
392  *                      It is case sensitive.
393  *
394  * @return copy of the new_tree pointer, or NULL on error.
395  */
396 netsnmp_subtree *
netsnmp_subtree_replace_first(netsnmp_subtree * new_tree,const char * context_name)397 netsnmp_subtree_replace_first(netsnmp_subtree *new_tree,
398 			      const char *context_name)
399 {
400     subtree_context_cache *ptr;
401     if (!context_name) {
402         context_name = "";
403     }
404     for (ptr = context_subtrees; ptr != NULL; ptr = ptr->next) {
405         if (ptr->context_name != NULL &&
406 	    strcmp(ptr->context_name, context_name) == 0) {
407             ptr->first_subtree = new_tree;
408             return ptr->first_subtree;
409         }
410     }
411     return add_subtree(new_tree, context_name);
412 }
413 
414 
415 void clear_subtree (netsnmp_subtree *sub);
416 
417 /** Completely clears both the Context cache and the Lookup cache.
418  */
419 void
clear_context(void)420 clear_context(void) {
421 
422     subtree_context_cache *ptr = NULL, *next = NULL;
423     netsnmp_subtree *t, *u;
424 
425     DEBUGMSGTL(("agent_registry", "clear context\n"));
426 
427     ptr = get_top_context_cache();
428     while (ptr) {
429 	next = ptr->next;
430 
431 	for (t = ptr->first_subtree; t; t = u) {
432             u = t->next;
433 	    clear_subtree(t);
434 	}
435 
436         free(NETSNMP_REMOVE_CONST(char*, ptr->context_name));
437         SNMP_FREE(ptr);
438 
439 	ptr = next;
440     }
441     context_subtrees = NULL; /* !!! */
442     clear_lookup_cache();
443 }
444 
445 /**  @} */
446 /* End of Context cache code */
447 
448 /** @defgroup agent_mib_subtree Maintaining MIB subtrees.
449  *     Maintaining MIB nodes and subtrees.
450  *   @ingroup agent_registry
451  *
452  * @{
453  */
454 
455 static void register_mib_detach_node(netsnmp_subtree *s);
456 
457 /** Frees single subtree item.
458  *  Deallocated memory for given netsnmp_subtree item, including
459  *  Handle Registration structure stored inside this item.
460  *  After calling this function, the pointer is invalid
461  *  and should be set to NULL.
462  *
463  *  @param a The subtree item to dispose.
464  */
465 void
netsnmp_subtree_free(netsnmp_subtree * a)466 netsnmp_subtree_free(netsnmp_subtree *a)
467 {
468   if (a != NULL) {
469     if (a->variables != NULL && netsnmp_oid_equals(a->name_a, a->namelen,
470 					     a->start_a, a->start_len) == 0) {
471       SNMP_FREE(a->variables);
472     }
473     SNMP_FREE(a->name_a);
474     a->namelen = 0;
475     SNMP_FREE(a->start_a);
476     a->start_len = 0;
477     SNMP_FREE(a->end_a);
478     a->end_len = 0;
479     SNMP_FREE(a->label_a);
480     netsnmp_handler_registration_free(a->reginfo);
481     a->reginfo = NULL;
482     SNMP_FREE(a);
483   }
484 }
485 
486 /** Creates deep copy of a subtree item.
487  *  Duplicates all properties stored in the structure, including
488  *  Handle Registration structure stored inside the item.
489  *
490  *  @param a The subtree item to copy.
491  *
492  *  @return deep copy of the subtree item, or NULL on error.
493  */
494 netsnmp_subtree *
netsnmp_subtree_deepcopy(netsnmp_subtree * a)495 netsnmp_subtree_deepcopy(netsnmp_subtree *a)
496 {
497   netsnmp_subtree *b = (netsnmp_subtree *)calloc(1, sizeof(netsnmp_subtree));
498 
499   if (b != NULL) {
500     memcpy(b, a, sizeof(netsnmp_subtree));
501     b->name_a  = snmp_duplicate_objid(a->name_a,  a->namelen);
502     b->start_a = snmp_duplicate_objid(a->start_a, a->start_len);
503     b->end_a   = snmp_duplicate_objid(a->end_a,   a->end_len);
504     b->label_a = strdup(a->label_a);
505 
506     if (b->name_a == NULL || b->start_a == NULL ||
507 	b->end_a  == NULL || b->label_a == NULL) {
508       netsnmp_subtree_free(b);
509       return NULL;
510     }
511 
512     if (a->variables != NULL) {
513       b->variables = (struct variable *)malloc(a->variables_len *
514 					       a->variables_width);
515       if (b->variables != NULL) {
516 	memcpy(b->variables, a->variables,a->variables_len*a->variables_width);
517       } else {
518 	netsnmp_subtree_free(b);
519 	return NULL;
520       }
521     }
522 
523     if (a->reginfo != NULL) {
524       b->reginfo = netsnmp_handler_registration_dup(a->reginfo);
525       if (b->reginfo == NULL) {
526 	netsnmp_subtree_free(b);
527 	return NULL;
528       }
529     }
530   }
531   return b;
532 }
533 
534 /** @private
535  *  Replaces next subtree pointer in given subtree.
536  */
537 NETSNMP_STATIC_INLINE void
netsnmp_subtree_change_next(netsnmp_subtree * ptr,netsnmp_subtree * thenext)538 netsnmp_subtree_change_next(netsnmp_subtree *ptr, netsnmp_subtree *thenext)
539 {
540     ptr->next = thenext;
541     if (thenext)
542         netsnmp_oid_compare_ll(ptr->start_a,
543                                ptr->start_len,
544                                thenext->start_a,
545                                thenext->start_len,
546                                &thenext->oid_off);
547 }
548 
549 /** @private
550  *  Replaces previous subtree pointer in given subtree.
551  */
552 NETSNMP_STATIC_INLINE void
netsnmp_subtree_change_prev(netsnmp_subtree * ptr,netsnmp_subtree * theprev)553 netsnmp_subtree_change_prev(netsnmp_subtree *ptr, netsnmp_subtree *theprev)
554 {
555     ptr->prev = theprev;
556     if (theprev)
557         netsnmp_oid_compare_ll(theprev->start_a,
558                                theprev->start_len,
559                                ptr->start_a,
560                                ptr->start_len,
561                                &ptr->oid_off);
562 }
563 
564 netsnmp_feature_child_of(netsnmp_subtree_compare,netsnmp_unused);
565 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_SUBTREE_COMPARE
566 /** Compares OIDs of given subtrees.
567  *
568  *  @param ap,bp Pointers to the subtrees to be compared.
569  *
570  *  @return OIDs lexicographical comparison result.
571  *
572  *  @see snmp_oid_compare()
573  */
574 int
netsnmp_subtree_compare(const netsnmp_subtree * ap,const netsnmp_subtree * bp)575 netsnmp_subtree_compare(const netsnmp_subtree *ap, const netsnmp_subtree *bp)
576 {
577     return snmp_oid_compare(ap->name_a, ap->namelen, bp->name_a, bp->namelen);
578 }
579 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_SUBTREE_COMPARE */
580 
581 /** Joins the given subtree with the current tree.
582  *  Trees are joined and the one supplied as parameter is freed.
583  *
584  *  @param root The subtree to be merged with current subtree.
585  *              Do not use the pointer after joining - it may be invalid.
586  */
587 void
netsnmp_subtree_join(netsnmp_subtree * root)588 netsnmp_subtree_join(netsnmp_subtree *root)
589 {
590     netsnmp_subtree *s, *tmp, *c, *d;
591 
592     while (root != NULL) {
593         s = root->next;
594         while (s != NULL && root->reginfo == s->reginfo) {
595             tmp = s->next;
596             DEBUGMSGTL(("subtree", "root start "));
597             DEBUGMSGOID(("subtree", root->start_a, root->start_len));
598             DEBUGMSG(("subtree", " (original end "));
599             DEBUGMSGOID(("subtree", root->end_a, root->end_len));
600             DEBUGMSG(("subtree", ")\n"));
601             DEBUGMSGTL(("subtree", "  JOINING to "));
602             DEBUGMSGOID(("subtree", s->start_a, s->start_len));
603 
604 	    SNMP_FREE(root->end_a);
605 	    root->end_a   = s->end_a;
606             root->end_len = s->end_len;
607 	    s->end_a      = NULL;
608 
609             for (c = root; c != NULL; c = c->children) {
610                 netsnmp_subtree_change_next(c, s->next);
611             }
612             for (c = s; c != NULL; c = c->children) {
613                 netsnmp_subtree_change_prev(c, root);
614             }
615             DEBUGMSG(("subtree", " so new end "));
616             DEBUGMSGOID(("subtree", root->end_a, root->end_len));
617             DEBUGMSG(("subtree", "\n"));
618             /*
619              * Probably need to free children too?
620              */
621             for (c = s->children; c != NULL; c = d) {
622                 d = c->children;
623                 netsnmp_subtree_free(c);
624             }
625             netsnmp_subtree_free(s);
626             s = tmp;
627         }
628         root = root->next;
629     }
630 }
631 
632 
633 /** Split the subtree into two at the specified point.
634  *  Subtrees of the given OID and separated and formed into the
635  *  returned subtree.
636  *
637  *  @param current The element at which splitting is started.
638  *
639  *  @param name The OID we'd like to split.
640  *
641  *  @param name_len Length of the OID.
642  *
643  *  @return head of the new (second) subtree.
644  */
645 netsnmp_subtree *
netsnmp_subtree_split(netsnmp_subtree * current,oid name[],int name_len)646 netsnmp_subtree_split(netsnmp_subtree *current, oid name[], int name_len)
647 {
648     struct variable *vp = NULL;
649     netsnmp_subtree *new_sub, *ptr;
650     int i = 0, rc = 0, rc2 = 0;
651     size_t common_len = 0;
652     char *cp;
653     oid *tmp_a, *tmp_b;
654 
655     if (snmp_oid_compare(name, name_len, current->end_a, current->end_len)>0) {
656 	/* Split comes after the end of this subtree */
657         return NULL;
658     }
659 
660     new_sub = netsnmp_subtree_deepcopy(current);
661     if (new_sub == NULL) {
662         return NULL;
663     }
664 
665     /*  Set up the point of division.  */
666     tmp_a = snmp_duplicate_objid(name, name_len);
667     if (tmp_a == NULL) {
668 	netsnmp_subtree_free(new_sub);
669 	return NULL;
670     }
671     tmp_b = snmp_duplicate_objid(name, name_len);
672     if (tmp_b == NULL) {
673 	netsnmp_subtree_free(new_sub);
674 	SNMP_FREE(tmp_a);
675 	return NULL;
676     }
677 
678     SNMP_FREE(current->end_a);
679     current->end_a = tmp_a;
680     current->end_len = name_len;
681     if (new_sub->start_a != NULL) {
682 	SNMP_FREE(new_sub->start_a);
683     }
684     new_sub->start_a = tmp_b;
685     new_sub->start_len = name_len;
686 
687     /*  Split the variables between the two new subtrees.  */
688     i = current->variables_len;
689     current->variables_len = 0;
690 
691     for (vp = current->variables; i > 0; i--) {
692 	/*  Note that the variable "name" field omits the prefix common to the
693 	    whole registration, hence the strange comparison here.  */
694 
695 	rc = snmp_oid_compare(vp->name, vp->namelen,
696 			      name     + current->namelen,
697 			      name_len - current->namelen);
698 
699         if (name_len - current->namelen > vp->namelen) {
700             common_len = vp->namelen;
701         } else {
702             common_len = name_len - current->namelen;
703         }
704 
705         rc2 = snmp_oid_compare(vp->name, common_len,
706                                name + current->namelen, common_len);
707 
708         if (rc >= 0) {
709             break;  /* All following variables belong to the second subtree */
710 	}
711 
712         current->variables_len++;
713         if (rc2 < 0) {
714             new_sub->variables_len--;
715             cp = (char *) new_sub->variables;
716             new_sub->variables = (struct variable *)(cp +
717 						     new_sub->variables_width);
718         }
719         vp = (struct variable *) ((char *) vp + current->variables_width);
720     }
721 
722     /* Delegated trees should retain their variables regardless */
723     if (current->variables_len > 0 &&
724         IS_DELEGATED((u_char) current->variables[0].type)) {
725         new_sub->variables_len = 1;
726         new_sub->variables = current->variables;
727     }
728 
729     /* Propogate this split down through any children */
730     if (current->children) {
731         new_sub->children = netsnmp_subtree_split(current->children,
732 						  name, name_len);
733     }
734 
735     /* Retain the correct linking of the list */
736     for (ptr = current; ptr != NULL; ptr = ptr->children) {
737         netsnmp_subtree_change_next(ptr, new_sub);
738     }
739     for (ptr = new_sub; ptr != NULL; ptr = ptr->children) {
740         netsnmp_subtree_change_prev(ptr, current);
741     }
742     for (ptr = new_sub->next; ptr != NULL; ptr=ptr->children) {
743         netsnmp_subtree_change_prev(ptr, new_sub);
744     }
745 
746     return new_sub;
747 }
748 
749 /** Loads the subtree under given context name.
750  *
751  *  @param new_sub The subtree to be loaded into current subtree.
752  *
753  *  @param context_name Text name of the context we're searching for.
754  *
755  *  @return gives MIB_REGISTERED_OK on success, error code otherwise.
756  */
757 int
netsnmp_subtree_load(netsnmp_subtree * new_sub,const char * context_name)758 netsnmp_subtree_load(netsnmp_subtree *new_sub, const char *context_name)
759 {
760     netsnmp_subtree *tree1, *tree2;
761     netsnmp_subtree *prev, *next;
762 
763     if (new_sub == NULL) {
764         return MIB_REGISTERED_OK;       /* Degenerate case */
765     }
766 
767     if (!netsnmp_subtree_find_first(context_name)) {
768         static int inloop = 0;
769         if (!inloop) {
770             oid ccitt[1]           = { 0 };
771             oid iso[1]             = { 1 };
772             oid joint_ccitt_iso[1] = { 2 };
773             inloop = 1;
774             netsnmp_register_null_context(snmp_duplicate_objid(ccitt, 1), 1,
775                                           context_name);
776             netsnmp_register_null_context(snmp_duplicate_objid(iso, 1), 1,
777                                           context_name);
778             netsnmp_register_null_context(snmp_duplicate_objid(joint_ccitt_iso, 1),
779                                           1, context_name);
780             inloop = 0;
781         }
782     }
783 
784     /*  Find the subtree that contains the start of the new subtree (if
785 	any)...*/
786 
787     tree1 = netsnmp_subtree_find(new_sub->start_a, new_sub->start_len,
788 				 NULL, context_name);
789 
790     /*  ... and the subtree that follows the new one (NULL implies this is the
791 	final region covered).  */
792 
793     if (tree1 == NULL) {
794 	tree2 = netsnmp_subtree_find_next(new_sub->start_a, new_sub->start_len,
795 					  NULL, context_name);
796     } else {
797 	tree2 = tree1->next;
798     }
799 
800     /*  Handle new subtrees that start in virgin territory.  */
801 
802     if (tree1 == NULL) {
803         /*netsnmp_subtree *new2 = NULL;*/
804 	/*  Is there any overlap with later subtrees?  */
805 	if (tree2 && snmp_oid_compare(new_sub->end_a, new_sub->end_len,
806 				      tree2->start_a, tree2->start_len) > 0) {
807 	    /*new2 =*/
808             netsnmp_subtree_split(new_sub, tree2->start_a, tree2->start_len);
809 	}
810 
811 	/*  Link the new subtree (less any overlapping region) with the list of
812 	    existing registrations.  */
813 
814 	if (tree2) {
815             netsnmp_subtree_change_prev(new_sub, tree2->prev);
816             netsnmp_subtree_change_prev(tree2, new_sub);
817 	} else {
818             netsnmp_subtree_change_prev(new_sub,
819                                         netsnmp_subtree_find_prev(new_sub->start_a,
820                                                                   new_sub->start_len, NULL, context_name));
821 
822 	    if (new_sub->prev) {
823                 netsnmp_subtree_change_next(new_sub->prev, new_sub);
824 	    } else {
825 		netsnmp_subtree_replace_first(new_sub, context_name);
826 	    }
827 
828             netsnmp_subtree_change_next(new_sub, tree2);
829 
830 #if 0
831             /* The code below cannot be reached which is why it has been
832                surrounded with #if 0 / #endif. */
833 	    /* If there was any overlap, recurse to merge in the overlapping
834 	       region (including anything that may follow the overlap).  */
835 	    if (new2) {
836 		return netsnmp_subtree_load(new2, context_name);
837 	    }
838 #endif
839 	}
840     } else {
841 	/*  If the new subtree starts *within* an existing registration
842 	    (rather than at the same point as it), then split the existing
843 	    subtree at this point.  */
844 
845 	if (netsnmp_oid_equals(new_sub->start_a, new_sub->start_len,
846 			     tree1->start_a,   tree1->start_len) != 0) {
847 	    tree1 = netsnmp_subtree_split(tree1, new_sub->start_a,
848 					  new_sub->start_len);
849 	}
850 
851         if (tree1 == NULL) {
852             return MIB_REGISTRATION_FAILED;
853 	}
854 
855 	/*  Now consider the end of this existing subtree:
856 
857 	    If it matches the new subtree precisely,
858 	            simply merge the new one into the list of children
859 
860 	    If it includes the whole of the new subtree,
861 		    split it at the appropriate point, and merge again
862 
863 	    If the new subtree extends beyond this existing region,
864 	            split it, and recurse to merge the two parts.  */
865 
866 	switch (snmp_oid_compare(new_sub->end_a, new_sub->end_len,
867                                  tree1->end_a, tree1->end_len)) {
868 
869 	case -1:
870 	    /*  Existing subtree contains new one.  */
871 	    netsnmp_subtree_split(tree1, new_sub->end_a, new_sub->end_len);
872 	    /* Fall Through */
873 
874 	case  0:
875 	    /*  The two trees match precisely.  */
876 
877 	    /*  Note: This is the only point where the original registration
878 	        OID ("name") is used.  */
879 
880 	    prev = NULL;
881 	    next = tree1;
882 
883 	    while (next && next->namelen > new_sub->namelen) {
884 		prev = next;
885 		next = next->children;
886 	    }
887 
888 	    while (next && next->namelen == new_sub->namelen &&
889 		   next->priority < new_sub->priority ) {
890 		prev = next;
891 		next = next->children;
892 	    }
893 
894 	    if (next && (next->namelen  == new_sub->namelen) &&
895 		(next->priority == new_sub->priority)) {
896                 if (new_sub->namelen != 1) {    /* ignore root OID dups */
897                     size_t          out_len = 0;
898                     size_t          buf_len = 0;
899                     char           *buf = NULL;
900                     int             buf_overflow = 0;
901 
902                     netsnmp_sprint_realloc_objid((u_char **) &buf, &buf_len, &out_len,
903                                                  1, &buf_overflow,
904                                                  new_sub->start_a,
905                                                  new_sub->start_len);
906                     snmp_log(LOG_ERR,
907                              "duplicate registration: MIB modules %s and %s (oid %s%s).\n",
908                              next->label_a, new_sub->label_a,
909                              buf ? buf : "",
910                              buf_overflow ? " [TRUNCATED]" : "");
911                     free(buf);
912                 }
913 		return MIB_DUPLICATE_REGISTRATION;
914 	    }
915 
916 	    if (prev) {
917 		prev->children    = new_sub;
918 		new_sub->children = next;
919                 netsnmp_subtree_change_prev(new_sub, prev->prev);
920                 netsnmp_subtree_change_next(new_sub, prev->next);
921 	    } else {
922 		new_sub->children = next;
923                 netsnmp_subtree_change_prev(new_sub, next->prev);
924                 netsnmp_subtree_change_next(new_sub, next->next);
925 
926 		for (next = new_sub->next; next != NULL;next = next->children){
927                     netsnmp_subtree_change_prev(next, new_sub);
928 		}
929 
930 		for (prev = new_sub->prev; prev != NULL;prev = prev->children){
931                     netsnmp_subtree_change_next(prev, new_sub);
932 		}
933 	    }
934 	    break;
935 
936 	case  1:
937 	    /*  New subtree contains the existing one.  */
938             {
939                 netsnmp_subtree *new2 =
940                     netsnmp_subtree_split(new_sub, tree1->end_a,tree1->end_len);
941                 int res = netsnmp_subtree_load(new_sub, context_name);
942                 if (res != MIB_REGISTERED_OK) {
943                     netsnmp_remove_subtree(new2);
944                     netsnmp_subtree_free(new2);
945                     return res;
946                 }
947                 return netsnmp_subtree_load(new2, context_name);
948             }
949         }
950     }
951     return 0;
952 }
953 
954 /** Free the given subtree and all its children.
955  *
956  *  @param sub Subtree branch to be cleared and freed.
957  *             After the call, this pointer is invalid
958  *             and should be set to NULL.
959  */
960 void
clear_subtree(netsnmp_subtree * sub)961 clear_subtree (netsnmp_subtree *sub) {
962 
963     netsnmp_subtree *c;
964 
965     if (sub == NULL)
966 	return;
967 
968     for(c = sub; c;) {
969         sub = c;
970         c = c->children;
971         netsnmp_subtree_free(sub);
972     }
973 
974 }
975 
976 netsnmp_subtree *
netsnmp_subtree_find_prev(const oid * name,size_t len,netsnmp_subtree * subtree,const char * context_name)977 netsnmp_subtree_find_prev(const oid *name, size_t len, netsnmp_subtree *subtree,
978 			  const char *context_name)
979 {
980     lookup_cache *lookup_cache = NULL;
981     netsnmp_subtree *myptr = NULL, *previous = NULL;
982     int cmp = 1;
983     size_t ll_off = 0;
984 
985     if (subtree) {
986         myptr = subtree;
987     } else {
988 	/* look through everything */
989         if (lookup_cache_size) {
990             lookup_cache = lookup_cache_find(context_name, name, len, &cmp);
991             if (lookup_cache) {
992                 myptr = lookup_cache->next;
993                 previous = lookup_cache->previous;
994             }
995             if (!myptr)
996                 myptr = netsnmp_subtree_find_first(context_name);
997         } else {
998             myptr = netsnmp_subtree_find_first(context_name);
999         }
1000     }
1001 
1002     /*
1003      * this optimization causes a segfault on sf cf alpha-linux1.
1004      * ifdef out until someone figures out why and fixes it. xxx-rks 20051117
1005      */
1006 #ifndef __alpha
1007 #define WTEST_OPTIMIZATION 1
1008 #endif
1009 #ifdef WTEST_OPTIMIZATION
1010     DEBUGMSGTL(("wtest","oid in: "));
1011     DEBUGMSGOID(("wtest", name, len));
1012     DEBUGMSG(("wtest","\n"));
1013 #endif
1014     for (; myptr != NULL; previous = myptr, myptr = myptr->next) {
1015 #ifdef WTEST_OPTIMIZATION
1016         /* Compare the incoming oid with the linked list.  If we have
1017            results of previous compares, its faster to make sure the
1018            length we differed in the last check is greater than the
1019            length between this pointer and the last then we don't need
1020            to actually perform a comparison */
1021         DEBUGMSGTL(("wtest","oid cmp: "));
1022         DEBUGMSGOID(("wtest", myptr->start_a, myptr->start_len));
1023         DEBUGMSG(("wtest","  --- off = %lu, in off = %lu test = %d\n",
1024                   (unsigned long)myptr->oid_off, (unsigned long)ll_off,
1025                   !(ll_off && myptr->oid_off &&
1026                     myptr->oid_off > ll_off)));
1027         if (!(ll_off && myptr->oid_off && myptr->oid_off > ll_off) &&
1028             netsnmp_oid_compare_ll(name, len,
1029                                    myptr->start_a, myptr->start_len,
1030                                    &ll_off) < 0) {
1031 #else
1032         if (snmp_oid_compare(name, len, myptr->start_a, myptr->start_len) < 0) {
1033 #endif
1034             if (lookup_cache_size && previous && cmp) {
1035                 if (lookup_cache) {
1036                     lookup_cache_replace(lookup_cache, myptr, previous);
1037                 } else {
1038                     lookup_cache_add(context_name, myptr, previous);
1039                 }
1040             }
1041             return previous;
1042         }
1043     }
1044     return previous;
1045 }
1046 
1047 netsnmp_subtree *
1048 netsnmp_subtree_find_next(const oid *name, size_t len,
1049 			  netsnmp_subtree *subtree, const char *context_name)
1050 {
1051     netsnmp_subtree *myptr = NULL;
1052 
1053     myptr = netsnmp_subtree_find_prev(name, len, subtree, context_name);
1054 
1055     if (myptr != NULL) {
1056         myptr = myptr->next;
1057         while (myptr != NULL && (myptr->variables == NULL ||
1058 				 myptr->variables_len == 0)) {
1059             myptr = myptr->next;
1060         }
1061         return myptr;
1062     } else if (subtree != NULL && snmp_oid_compare(name, len,
1063 				   subtree->start_a, subtree->start_len) < 0) {
1064         return subtree;
1065     } else {
1066         return NULL;
1067     }
1068 }
1069 
1070 netsnmp_subtree *
1071 netsnmp_subtree_find(const oid *name, size_t len, netsnmp_subtree *subtree,
1072 		     const char *context_name)
1073 {
1074     netsnmp_subtree *myptr;
1075 
1076     myptr = netsnmp_subtree_find_prev(name, len, subtree, context_name);
1077     if (myptr && myptr->end_a &&
1078         snmp_oid_compare(name, len, myptr->end_a, myptr->end_len)<0) {
1079         return myptr;
1080     }
1081 
1082     return NULL;
1083 }
1084 
1085 /**  @} */
1086 /* End of Subtrees maintaining code */
1087 
1088 /** @defgroup agent_mib_registering Registering and unregistering MIB subtrees.
1089  *     Adding and removing MIB nodes to the database under their contexts.
1090  *   @ingroup agent_registry
1091  *
1092  * @{
1093  */
1094 
1095 
1096 /** Registers a MIB handler.
1097  *
1098  *  @param moduleName
1099  *  @param var
1100  *  @param varsize
1101  *  @param numvars
1102  *  @param  mibloc
1103  *  @param mibloclen
1104  *  @param priority
1105  *  @param range_subid
1106  *  @param range_ubound
1107  *  @param  ss
1108  *  @param context
1109  *  @param timeout
1110  *  @param flags
1111  *  @param reginfo Registration handler structure.
1112  *                 In a case of failure, it will be freed.
1113  *  @param perform_callback
1114  *
1115  *  @return gives MIB_REGISTERED_OK or MIB_* error code.
1116  *
1117  *  @see netsnmp_register_handler()
1118  *  @see register_agentx_list()
1119  *  @see netsnmp_handler_registration_free()
1120  */
1121 int
1122 netsnmp_register_mib(const char *moduleName,
1123                      struct variable *var,
1124                      size_t varsize,
1125                      size_t numvars,
1126                      oid * mibloc,
1127                      size_t mibloclen,
1128                      int priority,
1129                      int range_subid,
1130                      oid range_ubound,
1131                      netsnmp_session * ss,
1132                      const char *context,
1133                      int timeout,
1134                      int flags,
1135                      netsnmp_handler_registration *reginfo,
1136                      int perform_callback)
1137 {
1138     netsnmp_subtree *subtree, *sub2;
1139     int             res;
1140     struct register_parameters reg_parms;
1141     int old_lookup_cache_val = netsnmp_get_lookup_cache_size();
1142 
1143     if (moduleName == NULL ||
1144         mibloc     == NULL) {
1145         /* Shouldn't happen ??? */
1146         netsnmp_handler_registration_free(reginfo);
1147         return MIB_REGISTRATION_FAILED;
1148     }
1149     subtree = (netsnmp_subtree *)calloc(1, sizeof(netsnmp_subtree));
1150     if (subtree == NULL) {
1151         netsnmp_handler_registration_free(reginfo);
1152         return MIB_REGISTRATION_FAILED;
1153     }
1154 
1155     DEBUGMSGTL(("register_mib", "registering \"%s\" at ", moduleName));
1156     DEBUGMSGOIDRANGE(("register_mib", mibloc, mibloclen, range_subid,
1157                       range_ubound));
1158     DEBUGMSG(("register_mib", " with context \"%s\"\n",
1159               SNMP_STRORNULL(context)));
1160 
1161     /*
1162      * verify that the passed context is equal to the context
1163      * in the reginfo.
1164      * (which begs the question, why do we have both? It appears that the
1165      *  reginfo item didn't appear til 5.2)
1166      */
1167     if( ((NULL == context) && (NULL != reginfo->contextName)) ||
1168         ((NULL != context) && (NULL == reginfo->contextName)) ||
1169         ( ((NULL != context) && (NULL != reginfo->contextName)) &&
1170           (0 != strcmp(context, reginfo->contextName))) ) {
1171         snmp_log(LOG_WARNING,"context passed during registration does not "
1172                  "equal the reginfo contextName! ('%s' != '%s')\n",
1173                  context, reginfo->contextName);
1174         netsnmp_assert(!"register context == reginfo->contextName"); /* always false */
1175     }
1176 
1177     /*  Create the new subtree node being registered.  */
1178 
1179     subtree->reginfo = reginfo;
1180     subtree->name_a  = snmp_duplicate_objid(mibloc, mibloclen);
1181     subtree->start_a = snmp_duplicate_objid(mibloc, mibloclen);
1182     subtree->end_a   = snmp_duplicate_objid(mibloc, mibloclen);
1183     subtree->label_a = strdup(moduleName);
1184     if (subtree->name_a == NULL || subtree->start_a == NULL ||
1185 	subtree->end_a  == NULL || subtree->label_a == NULL) {
1186 	netsnmp_subtree_free(subtree); /* also frees reginfo */
1187 	return MIB_REGISTRATION_FAILED;
1188     }
1189     subtree->namelen   = (u_char)mibloclen;
1190     subtree->start_len = (u_char)mibloclen;
1191     subtree->end_len   = (u_char)mibloclen;
1192     subtree->end_a[mibloclen - 1]++;
1193 
1194     if (var != NULL) {
1195 	subtree->variables = (struct variable *)malloc(varsize*numvars);
1196 	if (subtree->variables == NULL) {
1197 	    netsnmp_subtree_free(subtree); /* also frees reginfo */
1198 	    return MIB_REGISTRATION_FAILED;
1199 	}
1200 	memcpy(subtree->variables, var, numvars*varsize);
1201 	subtree->variables_len = numvars;
1202 	subtree->variables_width = varsize;
1203     }
1204     subtree->priority = priority;
1205     subtree->timeout = timeout;
1206     subtree->range_subid = range_subid;
1207     subtree->range_ubound = range_ubound;
1208     subtree->session = ss;
1209     subtree->flags = (u_char)flags;    /*  used to identify instance oids  */
1210     subtree->flags |= SUBTREE_ATTACHED;
1211     subtree->global_cacheid = reginfo->global_cacheid;
1212 
1213     netsnmp_set_lookup_cache_size(0);
1214     res = netsnmp_subtree_load(subtree, context);
1215 
1216     /*  If registering a range, use the first subtree as a template for the
1217 	rest of the range.  */
1218 
1219     if (res == MIB_REGISTERED_OK && range_subid != 0) {
1220         int i;
1221 	for (i = mibloc[range_subid - 1] + 1; i <= (int)range_ubound; i++) {
1222 	    sub2 = netsnmp_subtree_deepcopy(subtree);
1223 
1224 	    if (sub2 == NULL) {
1225                 unregister_mib_context(mibloc, mibloclen, priority,
1226                                        range_subid, range_ubound, context);
1227                 netsnmp_set_lookup_cache_size(old_lookup_cache_val);
1228                 invalidate_lookup_cache(context);
1229                 return MIB_REGISTRATION_FAILED;
1230             }
1231 
1232             sub2->name_a[range_subid - 1]  = i;
1233             sub2->start_a[range_subid - 1] = i;
1234             sub2->end_a[range_subid - 1]   = i;     /* XXX - ???? */
1235             if (range_subid == (int)mibloclen) {
1236                 ++sub2->end_a[range_subid - 1];
1237             }
1238             sub2->flags |= SUBTREE_ATTACHED;
1239             sub2->global_cacheid = reginfo->global_cacheid;
1240             /* FRQ This is essential for requests to succeed! */
1241             sub2->reginfo->rootoid[range_subid - 1]  = i;
1242 
1243             res = netsnmp_subtree_load(sub2, context);
1244             if (res != MIB_REGISTERED_OK) {
1245                 unregister_mib_context(mibloc, mibloclen, priority,
1246                                        range_subid, range_ubound, context);
1247                 netsnmp_remove_subtree(sub2);
1248 		netsnmp_subtree_free(sub2);
1249                 netsnmp_set_lookup_cache_size(old_lookup_cache_val);
1250                 invalidate_lookup_cache(context);
1251                 return res;
1252             }
1253         }
1254     } else if (res == MIB_DUPLICATE_REGISTRATION ||
1255                res == MIB_REGISTRATION_FAILED) {
1256         netsnmp_set_lookup_cache_size(old_lookup_cache_val);
1257         invalidate_lookup_cache(context);
1258         netsnmp_subtree_free(subtree);
1259         return res;
1260     }
1261 
1262     /*
1263      * mark the MIB as detached, if there's no master agent present as of now
1264      */
1265     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1266 			       NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
1267         if (main_session == NULL) {
1268             register_mib_detach_node(subtree);
1269 	}
1270     }
1271 
1272     if (res == MIB_REGISTERED_OK && perform_callback) {
1273         memset(&reg_parms, 0x0, sizeof(reg_parms));
1274         reg_parms.name = mibloc;
1275         reg_parms.namelen = mibloclen;
1276         reg_parms.priority = priority;
1277         reg_parms.range_subid = range_subid;
1278         reg_parms.range_ubound = range_ubound;
1279         reg_parms.timeout = timeout;
1280         reg_parms.flags = (u_char) flags;
1281         reg_parms.session = ss;
1282         reg_parms.reginfo = reginfo;
1283         reg_parms.contextName = context;
1284         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1285                             SNMPD_CALLBACK_REGISTER_OID, &reg_parms);
1286     }
1287 
1288     netsnmp_set_lookup_cache_size(old_lookup_cache_val);
1289     invalidate_lookup_cache(context);
1290     return res;
1291 }
1292 
1293 /** @private
1294  *  Reattach a particular node.
1295  */
1296 static void
1297 register_mib_reattach_node(netsnmp_subtree *s)
1298 {
1299     if ((s != NULL) && (s->namelen > 1) && !(s->flags & SUBTREE_ATTACHED)) {
1300         struct register_parameters reg_parms;
1301         /*
1302          * only do registrations that are not the top level nodes
1303          */
1304         memset(&reg_parms, 0x0, sizeof(reg_parms));
1305 
1306         /*
1307          * XXX: do this better
1308          */
1309         reg_parms.name = s->name_a;
1310         reg_parms.namelen = s->namelen;
1311         reg_parms.priority = s->priority;
1312         reg_parms.range_subid = s->range_subid;
1313         reg_parms.range_ubound = s->range_ubound;
1314         reg_parms.timeout = s->timeout;
1315         reg_parms.flags = s->flags;
1316         reg_parms.session = s->session;
1317         reg_parms.reginfo = s->reginfo;
1318         /* XXX: missing in subtree: reg_parms.contextName = s->context; */
1319         if ((NULL != s->reginfo) && (NULL != s->reginfo->contextName))
1320             reg_parms.contextName = s->reginfo->contextName;
1321         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1322                             SNMPD_CALLBACK_REGISTER_OID, &reg_parms);
1323         s->flags |= SUBTREE_ATTACHED;
1324     }
1325 }
1326 
1327 /** Call callbacks to reattach all our nodes.
1328  */
1329 void
1330 register_mib_reattach(void)
1331 {
1332     netsnmp_subtree *s, *t;
1333     subtree_context_cache *ptr;
1334 
1335     for (ptr = context_subtrees; ptr; ptr = ptr->next) {
1336         for (s = ptr->first_subtree; s != NULL; s = s->next) {
1337             register_mib_reattach_node(s);
1338             for (t = s->children; t != NULL; t = t->children) {
1339                 register_mib_reattach_node(t);
1340             }
1341         }
1342     }
1343 }
1344 
1345 /** @private
1346  *  Mark a node as detached.
1347  *
1348  *  @param s The note to be marked
1349  */
1350 static void
1351 register_mib_detach_node(netsnmp_subtree *s)
1352 {
1353     if (s != NULL) {
1354         s->flags = s->flags & ~SUBTREE_ATTACHED;
1355     }
1356 }
1357 
1358 /** Mark all our registered OIDs as detached.
1359  *  This is only really useful for subagent protocols, when
1360  *  a connection is lost or the subagent is being shut down.
1361  */
1362 void
1363 register_mib_detach(void)
1364 {
1365     netsnmp_subtree *s, *t;
1366     subtree_context_cache *ptr;
1367     for (ptr = context_subtrees; ptr; ptr = ptr->next) {
1368         for (s = ptr->first_subtree; s != NULL; s = s->next) {
1369             register_mib_detach_node(s);
1370             for (t = s->children; t != NULL; t = t->children) {
1371                 register_mib_detach_node(t);
1372             }
1373         }
1374     }
1375 }
1376 
1377 /** Register a new module into the MIB database, with all possible custom options
1378  *
1379  *  @param  moduleName Text name of the module.
1380  *                     The given name will be used to identify the module
1381  *                     inside the agent.
1382  *
1383  *  @param  var        Array of variables to be registered in the module.
1384  *
1385  *  @param  varsize    Size of a single variable in var array.
1386  *                     The size is normally equal to sizeof(struct variable),
1387  *                     but if we wish to use shorter (or longer) OIDs, then we
1388  *                     could use different variant of the variable structure.
1389  *
1390  *  @param  numvars    Number of variables in the var array.
1391  *                     This is how many variables the function will try to register.
1392  *
1393  *  @param  mibloc     Base OID of the module.
1394  *                     All OIDs in var array should be sub-oids of the base OID.
1395  *
1396  *  @param  mibloclen  Length of the base OID.
1397  *                     Number of integers making up the base OID.
1398  *
1399  *  @param  priority   Registration priority.
1400  *                     Used to achieve a desired configuration when different
1401  *                     sessions register identical or overlapping regions.
1402  *                     Primarily used with AgentX subagent registrations.
1403  *
1404  *  @param range_subid If non-zero, the module is registered against a range
1405  *                     of OIDs, with this parameter identifying the relevant
1406  *                     subidentifier - see RFC 2741 for details.
1407  *                     Typically used to register a single row of a table.
1408  *                     If zero, then register the module against the full OID subtree.
1409  *
1410  *  @param range_ubound The end of the range being registered (see RFC 2741)
1411  *                     If range_subid is zero, then this parameter is ignored.
1412  *
1413  *  @param ss
1414  *  @param context
1415  *  @param timeout
1416  *  @param flags
1417  *
1418  *  @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
1419  *
1420  *  @see register_mib()
1421  *  @see register_mib_priority()
1422  *  @see register_mib_range()
1423  *  @see unregister_mib()
1424  */
1425 int
1426 register_mib_context(const char *moduleName,
1427                      const struct variable *var,
1428                      size_t varsize,
1429                      size_t numvars,
1430                      const oid * mibloc,
1431                      size_t mibloclen,
1432                      int priority,
1433                      int range_subid,
1434                      oid range_ubound,
1435                      netsnmp_session * ss,
1436                      const char *context, int timeout, int flags)
1437 {
1438     return netsnmp_register_old_api(moduleName, var, varsize, numvars,
1439                                     mibloc, mibloclen, priority,
1440                                     range_subid, range_ubound, ss, context,
1441                                     timeout, flags);
1442 }
1443 
1444 /** Register a new module into the MIB database, as being responsible
1445  *   for a range of OIDs (typically a single row of a table).
1446  *
1447  *  @param  moduleName Text name of the module.
1448  *                     The given name will be used to identify the module
1449  *                     inside the agent.
1450  *
1451  *  @param  var        Array of variables to be registered in the module.
1452  *
1453  *  @param  varsize    Size of a single variable in var array.
1454  *                     The size is normally equal to sizeof(struct variable),
1455  *                     but if we wish to use shorter (or longer) OIDs, then we
1456  *                     could use different variant of the variable structure.
1457  *
1458  *  @param  numvars    Number of variables in the var array.
1459  *                     This is how many variables the function will try to register.
1460  *
1461  *  @param  mibloc     Base OID of the module.
1462  *                     All OIDs in var array should be sub-oids of the base OID.
1463  *
1464  *  @param  mibloclen  Length of the base OID.
1465  *                     Number of integers making up the base OID.
1466  *
1467  *  @param  priority   Registration priority.
1468  *                     Used to achieve a desired configuration when different
1469  *                     sessions register identical or overlapping regions.
1470  *                     Primarily used with AgentX subagent registrations.
1471  *
1472  *  @param range_subid If non-zero, the module is registered against a range
1473  *                     of OIDs, with this parameter identifying the relevant
1474  *                     subidentifier - see RFC 2741 for details.
1475  *                     Typically used to register a single row of a table.
1476  *                     If zero, then register the module against the full OID subtree.
1477  *
1478  *  @param range_ubound The end of the range being registered (see RFC 2741)
1479  *                     If range_subid is zero, then this parameter is ignored.
1480  *
1481  *  @param ss
1482  *
1483  *  @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
1484  *
1485  *  @see register_mib()
1486  *  @see register_mib_priority()
1487  *  @see register_mib_context()
1488  *  @see unregister_mib()
1489  */
1490 int
1491 register_mib_range(const char *moduleName,
1492                    const struct variable *var,
1493                    size_t varsize,
1494                    size_t numvars,
1495                    const oid * mibloc,
1496                    size_t mibloclen,
1497                    int priority,
1498                    int range_subid, oid range_ubound, netsnmp_session * ss)
1499 {
1500     return register_mib_context(moduleName, var, varsize, numvars,
1501                                 mibloc, mibloclen, priority,
1502                                 range_subid, range_ubound, ss, "", -1, 0);
1503 }
1504 
1505 /** Register a new module into the MIB database, with a non-default priority
1506  *
1507  *  @param  moduleName Text name of the module.
1508  *                     The given name will be used to identify the module
1509  *                     inside the agent.
1510  *
1511  *  @param  var        Array of variables to be registered in the module.
1512  *
1513  *  @param  varsize    Size of a single variable in var array.
1514  *                     The size is normally equal to sizeof(struct variable),
1515  *                     but if we wish to use shorter (or longer) OIDs, then we
1516  *                     could use different variant of the variable structure.
1517  *
1518  *  @param  numvars    Number of variables in the var array.
1519  *                     This is how many variables the function will try to register.
1520  *
1521  *  @param  mibloc     Base OID of the module.
1522  *                     All OIDs in var array should be sub-oids of the base OID.
1523  *
1524  *  @param  mibloclen  Length of the base OID.
1525  *                     Number of integers making up the base OID.
1526  *
1527  *  @param  priority   Registration priority.
1528  *                     Used to achieve a desired configuration when different
1529  *                     sessions register identical or overlapping regions.
1530  *                     Primarily used with AgentX subagent registrations.
1531  *
1532  *  @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
1533  *
1534  *  @see register_mib()
1535  *  @see register_mib_range()
1536  *  @see register_mib_context()
1537  *  @see unregister_mib()
1538  */
1539 int
1540 register_mib_priority(const char *moduleName,
1541                       const struct variable *var,
1542                       size_t varsize,
1543                       size_t numvars,
1544                       const oid * mibloc, size_t mibloclen, int priority)
1545 {
1546     return register_mib_range(moduleName, var, varsize, numvars,
1547                               mibloc, mibloclen, priority, 0, 0, NULL);
1548 }
1549 
1550 /** Register a new module into the MIB database, using default priority and context
1551  *
1552  *  @param  moduleName Text name of the module.
1553  *                     The given name will be used to identify the module
1554  *                     inside the agent.
1555  *
1556  *  @param  var        Array of variables to be registered in the module.
1557  *
1558  *  @param  varsize    Size of a single variable in var array.
1559  *                     The size is normally equal to sizeof(struct variable),
1560  *                     but if we wish to use shorter (or longer) OIDs, then we
1561  *                     could use different variant of the variable structure.
1562  *
1563  *  @param  numvars    Number of variables in the var array.
1564  *                     This is how many variables the function will try to register.
1565  *
1566  *  @param  mibloc     Base OID of the module.
1567  *                     All OIDs in var array should be sub-oids of the base OID.
1568  *
1569  *  @param  mibloclen  Length of the base OID.
1570  *                     Number of integers making up the base OID.
1571  *
1572  *  @return gives SNMPERR_SUCCESS or SNMPERR_* error code.
1573  *
1574  *  @see register_mib_priority()
1575  *  @see register_mib_range()
1576  *  @see register_mib_context()
1577  *  @see unregister_mib()
1578  */
1579 int
1580 register_mib(const char *moduleName,
1581              const struct variable *var,
1582              size_t varsize,
1583              size_t numvars, const oid * mibloc, size_t mibloclen)
1584 {
1585     return register_mib_priority(moduleName, var, varsize, numvars,
1586                                  mibloc, mibloclen, DEFAULT_MIB_PRIORITY);
1587 }
1588 
1589 /** @private
1590  *  Unloads a subtree from MIB tree.
1591  *
1592  *  @param  sub     The sub-tree which is being removed.
1593  *
1594  *  @param  prev    Previous entry, before the unloaded one.
1595  *
1596  *  @param  context Name of the context which is being removed.
1597  *
1598  *  @see unregister_mib_context()
1599  */
1600 void
1601 netsnmp_subtree_unload(netsnmp_subtree *sub, netsnmp_subtree *prev, const char *context)
1602 {
1603     netsnmp_subtree *ptr;
1604 
1605     DEBUGMSGTL(("register_mib", "unload("));
1606     if (sub != NULL) {
1607         DEBUGMSGOID(("register_mib", sub->start_a, sub->start_len));
1608     } else {
1609         DEBUGMSG(("register_mib", "[NIL]"));
1610         return;
1611     }
1612     DEBUGMSG(("register_mib", ", "));
1613     if (prev != NULL) {
1614         DEBUGMSGOID(("register_mib", prev->start_a, prev->start_len));
1615     } else {
1616         DEBUGMSG(("register_mib", "[NIL]"));
1617     }
1618     DEBUGMSG(("register_mib", ")\n"));
1619 
1620     if (prev != NULL) {         /* non-leading entries are easy */
1621         prev->children = sub->children;
1622         invalidate_lookup_cache(context);
1623         return;
1624     }
1625     /*
1626      * otherwise, we need to amend our neighbours as well
1627      */
1628 
1629     if (sub->children == NULL) {        /* just remove this node completely */
1630         for (ptr = sub->prev; ptr; ptr = ptr->children) {
1631             netsnmp_subtree_change_next(ptr, sub->next);
1632         }
1633         for (ptr = sub->next; ptr; ptr = ptr->children) {
1634             netsnmp_subtree_change_prev(ptr, sub->prev);
1635         }
1636 
1637 	if (sub->prev == NULL) {
1638 	    netsnmp_subtree_replace_first(sub->next, context);
1639 	}
1640 
1641     } else {
1642         for (ptr = sub->prev; ptr; ptr = ptr->children)
1643             netsnmp_subtree_change_next(ptr, sub->children);
1644         for (ptr = sub->next; ptr; ptr = ptr->children)
1645             netsnmp_subtree_change_prev(ptr, sub->children);
1646 
1647 	if (sub->prev == NULL) {
1648 	    netsnmp_subtree_replace_first(sub->children, context);
1649 	}
1650     }
1651     invalidate_lookup_cache(context);
1652 }
1653 
1654 /**
1655  * Unregisters a module registered against a given OID (or range) in a specified context.
1656  * Typically used when a module has multiple contexts defined.
1657  * The parameters priority, range_subid, range_ubound and context
1658  * should match those used to register the module originally.
1659  *
1660  * @param name  the specific OID to unregister if it conatins the associated
1661  *              context.
1662  *
1663  * @param len   the length of the OID, use  OID_LENGTH macro.
1664  *
1665  * @param priority  a value between 1 and 255, used to achieve a desired
1666  *                  configuration when different sessions register identical or
1667  *                  overlapping regions.  Subagents with no particular
1668  *                  knowledge of priority should register with the default
1669  *                  value of 127.
1670  *
1671  * @param range_subid  permits specifying a range in place of one of a subtree
1672  *                     sub-identifiers.  When this value is zero, no range is
1673  *                     being specified.
1674  *
1675  * @param range_ubound  the upper bound of a sub-identifier's range.
1676  *                      This field is present only if range_subid is not 0.
1677  *
1678  * @param context  a context name that has been created
1679  *
1680  * @return gives MIB_UNREGISTERED_OK or MIB_* error code.
1681  *
1682  * @see unregister_mib()
1683  * @see unregister_mib_priority()
1684  * @see unregister_mib_range()
1685  */
1686 int
1687 unregister_mib_context(oid * name, size_t len, int priority,
1688                        int range_subid, oid range_ubound,
1689                        const char *context)
1690 {
1691     netsnmp_subtree *list, *myptr = NULL;
1692     netsnmp_subtree *prev, *child, *next; /* loop through children */
1693     struct register_parameters reg_parms;
1694     int old_lookup_cache_val = netsnmp_get_lookup_cache_size();
1695     int unregistering = 1;
1696     int orig_subid_val = -1;
1697 
1698     netsnmp_set_lookup_cache_size(0);
1699 
1700     if ((range_subid > 0) &&  ((size_t)range_subid <= len))
1701         orig_subid_val = name[range_subid-1];
1702 
1703     while(unregistering){
1704         DEBUGMSGTL(("register_mib", "unregistering "));
1705         DEBUGMSGOIDRANGE(("register_mib", name, len, range_subid, range_ubound));
1706         DEBUGMSG(("register_mib", "\n"));
1707 
1708         list = netsnmp_subtree_find(name, len, netsnmp_subtree_find_first(context),
1709                     context);
1710         if (list == NULL) {
1711             return MIB_NO_SUCH_REGISTRATION;
1712         }
1713 
1714         for (child = list, prev = NULL; child != NULL;
1715             prev = child, child = child->children) {
1716             if (netsnmp_oid_equals(child->name_a, child->namelen, name, len) == 0 &&
1717                 child->priority == priority) {
1718                 break;              /* found it */
1719              }
1720         }
1721 
1722         if (child == NULL) {
1723             return MIB_NO_SUCH_REGISTRATION;
1724         }
1725 
1726         netsnmp_subtree_unload(child, prev, context);
1727         myptr = child;              /* remember this for later */
1728 
1729         /*
1730         *  Now handle any occurances in the following subtrees,
1731         *      as a result of splitting this range.  Due to the
1732         *      nature of the way such splits work, the first
1733         *      subtree 'slice' that doesn't refer to the given
1734         *      name marks the end of the original region.
1735         *
1736         *  This should also serve to register ranges.
1737         */
1738 
1739         for (list = myptr->next; list != NULL; list = next) {
1740             next = list->next; /* list gets freed sometimes; cache next */
1741             for (child = list, prev = NULL; child != NULL;
1742                 prev = child, child = child->children) {
1743                 if ((netsnmp_oid_equals(child->name_a, child->namelen,
1744                     name, len) == 0) &&
1745             (child->priority == priority)) {
1746                     netsnmp_subtree_unload(child, prev, context);
1747                     netsnmp_subtree_free(child);
1748                     break;
1749                 }
1750             }
1751             if (child == NULL)      /* Didn't find the given name */
1752                 break;
1753         }
1754 
1755         /* Maybe we are in a range... */
1756         if (orig_subid_val != -1){
1757             if (++name[range_subid-1] >= orig_subid_val+range_ubound)
1758                 {
1759                 unregistering=0;
1760                 name[range_subid-1] = orig_subid_val;
1761                 }
1762         }
1763         else {
1764             unregistering=0;
1765         }
1766     }
1767 
1768     memset(&reg_parms, 0x0, sizeof(reg_parms));
1769     reg_parms.name = name;
1770     reg_parms.namelen = len;
1771     reg_parms.priority = priority;
1772     reg_parms.range_subid = range_subid;
1773     reg_parms.range_ubound = range_ubound;
1774     reg_parms.flags = 0x00;     /*  this is okay I think  */
1775     reg_parms.contextName = context;
1776     snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1777                         SNMPD_CALLBACK_UNREGISTER_OID, &reg_parms);
1778 
1779     netsnmp_subtree_free(myptr);
1780     netsnmp_set_lookup_cache_size(old_lookup_cache_val);
1781     invalidate_lookup_cache(context);
1782     return MIB_UNREGISTERED_OK;
1783 }
1784 
1785 #ifndef NETSNMP_FEATURE_REMOVE_UNREGISTER_MIB_TABLE_ROW
1786 int
1787 netsnmp_unregister_mib_table_row(oid * name, size_t len, int priority,
1788                                  int var_subid, oid range_ubound,
1789                                  const char *context)
1790 {
1791     netsnmp_subtree *list, *myptr, *futureptr;
1792     netsnmp_subtree *prev, *child;       /* loop through children */
1793     struct register_parameters reg_parms;
1794     oid             range_lbound = name[var_subid - 1];
1795 
1796     DEBUGMSGTL(("register_mib", "unregistering "));
1797     DEBUGMSGOIDRANGE(("register_mib", name, len, var_subid, range_ubound));
1798     DEBUGMSG(("register_mib", "\n"));
1799 
1800     for (; name[var_subid - 1] <= range_ubound; name[var_subid - 1]++) {
1801         list = netsnmp_subtree_find(name, len,
1802 				netsnmp_subtree_find_first(context), context);
1803 
1804         if (list == NULL) {
1805             continue;
1806         }
1807 
1808         for (child = list, prev = NULL; child != NULL;
1809              prev = child, child = child->children) {
1810 
1811             if (netsnmp_oid_equals(child->name_a, child->namelen,
1812 				 name, len) == 0 &&
1813 		(child->priority == priority)) {
1814                 break;          /* found it */
1815             }
1816         }
1817 
1818         if (child == NULL) {
1819             continue;
1820         }
1821 
1822         netsnmp_subtree_unload(child, prev, context);
1823         myptr = child;          /* remember this for later */
1824 
1825         for (list = myptr->next; list != NULL; list = futureptr) {
1826             /* remember the next spot in the list in case we free this node */
1827             futureptr = list->next;
1828 
1829             /* check each child */
1830             for (child = list, prev = NULL; child != NULL;
1831                  prev = child, child = child->children) {
1832 
1833                 if (netsnmp_oid_equals(child->name_a, child->namelen,
1834 				      name, len) == 0 &&
1835                     (child->priority == priority)) {
1836                     netsnmp_subtree_unload(child, prev, context);
1837                     netsnmp_subtree_free(child);
1838                     break;
1839                 }
1840             }
1841 
1842             /* XXX: wjh: not sure why we're bailing here */
1843             if (child == NULL) {        /* Didn't find the given name */
1844                 break;
1845             }
1846         }
1847         netsnmp_subtree_free(myptr);
1848     }
1849 
1850     name[var_subid - 1] = range_lbound;
1851     memset(&reg_parms, 0x0, sizeof(reg_parms));
1852     reg_parms.name = name;
1853     reg_parms.namelen = len;
1854     reg_parms.priority = priority;
1855     reg_parms.range_subid = var_subid;
1856     reg_parms.range_ubound = range_ubound;
1857     reg_parms.flags = 0x00;     /*  this is okay I think  */
1858     reg_parms.contextName = context;
1859     snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1860                         SNMPD_CALLBACK_UNREGISTER_OID, &reg_parms);
1861 
1862     return 0;
1863 }
1864 #endif /* NETSNMP_FEATURE_REMOVE_UNREGISTER_MIB_TABLE_ROW */
1865 
1866 /**
1867  * Unregisters a module registered against a given OID (or range) in the default context.
1868  * Typically used when a module has multiple contexts defined.
1869  * The parameters priority, range_subid, and range_ubound should
1870  * match those used to register the module originally.
1871  *
1872  * @param name  the specific OID to unregister if it conatins the associated
1873  *              context.
1874  *
1875  * @param len   the length of the OID, use  OID_LENGTH macro.
1876  *
1877  * @param priority  a value between 1 and 255, used to achieve a desired
1878  *                  configuration when different sessions register identical or
1879  *                  overlapping regions.  Subagents with no particular
1880  *                  knowledge of priority should register with the default
1881  *                  value of 127.
1882  *
1883  * @param range_subid  permits specifying a range in place of one of a subtree
1884  *                     sub-identifiers.  When this value is zero, no range is
1885  *                     being specified.
1886  *
1887  * @param range_ubound  the upper bound of a sub-identifier's range.
1888  *                      This field is present only if range_subid is not 0.
1889  *
1890  * @return gives MIB_UNREGISTERED_OK or MIB_* error code.
1891  *
1892  * @see unregister_mib()
1893  * @see unregister_mib_priority()
1894  * @see unregister_mib_context()
1895  */
1896 int
1897 unregister_mib_range(oid * name, size_t len, int priority,
1898                      int range_subid, oid range_ubound)
1899 {
1900     return unregister_mib_context(name, len, priority, range_subid,
1901                                   range_ubound, "");
1902 }
1903 
1904 /**
1905  * Unregisters a module registered against a given OID at the specified priority.
1906  * The priority parameter should match that used to register the module originally.
1907  *
1908  * @param name  the specific OID to unregister if it conatins the associated
1909  *              context.
1910  *
1911  * @param len   the length of the OID, use  OID_LENGTH macro.
1912  *
1913  * @param priority  a value between 1 and 255, used to achieve a desired
1914  *                  configuration when different sessions register identical or
1915  *                  overlapping regions.  Subagents with no particular
1916  *                  knowledge of priority should register with the default
1917  *                  value of 127.
1918  *
1919  * @return gives MIB_UNREGISTERED_OK or MIB_* error code.
1920  *
1921  * @see unregister_mib()
1922  * @see unregister_mib_range()
1923  * @see unregister_mib_context()
1924  */
1925 int
1926 unregister_mib_priority(oid * name, size_t len, int priority)
1927 {
1928     return unregister_mib_range(name, len, priority, 0, 0);
1929 }
1930 
1931 /**
1932  * Unregisters a module registered against a given OID at the default priority.
1933  *
1934  * @param name  the specific OID to unregister if it conatins the associated
1935  *              context.
1936  *
1937  * @param len   the length of the OID, use  OID_LENGTH macro.
1938  *
1939  * @return gives MIB_UNREGISTERED_OK or MIB_* error code.
1940  *
1941  * @see unregister_mib_priority()
1942  * @see unregister_mib_context()
1943  * @see unregister_mib_range()
1944  * @see unregister_agentx_list()
1945  */
1946 int
1947 unregister_mib(oid * name, size_t len)
1948 {
1949     return unregister_mib_priority(name, len, DEFAULT_MIB_PRIORITY);
1950 }
1951 
1952 /** Unregisters subtree of OIDs bounded to given session.
1953  *
1954  *  @param ss Session which OIDs will be removed from tree.
1955  *
1956  *  @see unregister_mib()
1957  *  @see unregister_agentx_list()
1958  */
1959 void
1960 unregister_mibs_by_session(netsnmp_session * ss)
1961 {
1962     netsnmp_subtree *list, *list2;
1963     netsnmp_subtree *child, *prev, *next_child;
1964     struct register_parameters rp;
1965     subtree_context_cache *contextptr;
1966 
1967     DEBUGMSGTL(("register_mib", "unregister_mibs_by_session(%p) ctxt \"%s\"\n",
1968 		ss, (ss && ss->contextName) ? ss->contextName : "[NIL]"));
1969 
1970     for (contextptr = get_top_context_cache(); contextptr != NULL;
1971          contextptr = contextptr->next) {
1972         for (list = contextptr->first_subtree; list != NULL; list = list2) {
1973             list2 = list->next;
1974 
1975             for (child = list, prev = NULL; child != NULL; child = next_child){
1976                 next_child = child->children;
1977 
1978                 if (((!ss || ss->flags & SNMP_FLAGS_SUBSESSION) &&
1979 		     child->session == ss) ||
1980                     (!(!ss || ss->flags & SNMP_FLAGS_SUBSESSION) && child->session &&
1981                      child->session->subsession == ss)) {
1982 
1983                     memset(&rp,0x0,sizeof(rp));
1984                     rp.name = child->name_a;
1985 		    child->name_a = NULL;
1986                     rp.namelen = child->namelen;
1987                     rp.priority = child->priority;
1988                     rp.range_subid = child->range_subid;
1989                     rp.range_ubound = child->range_ubound;
1990                     rp.timeout = child->timeout;
1991                     rp.flags = child->flags;
1992                     if ((NULL != child->reginfo) &&
1993                         (NULL != child->reginfo->contextName))
1994                         rp.contextName = child->reginfo->contextName;
1995 
1996                     if (child->reginfo != NULL) {
1997                         /*
1998                          * Don't let's free the session pointer just yet!
1999                          */
2000                         child->reginfo->handler->myvoid = NULL;
2001                         netsnmp_handler_registration_free(child->reginfo);
2002 			child->reginfo = NULL;
2003                     }
2004 
2005                     netsnmp_subtree_unload(child, prev, contextptr->context_name);
2006                     netsnmp_subtree_free(child);
2007 
2008                     snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
2009                                         SNMPD_CALLBACK_UNREGISTER_OID, &rp);
2010 		    SNMP_FREE(rp.name);
2011                 } else {
2012                     prev = child;
2013                 }
2014             }
2015         }
2016         netsnmp_subtree_join(contextptr->first_subtree);
2017     }
2018 }
2019 
2020 /** Determines if given PDU is allowed to see (or update) a given OID.
2021  *
2022  * @param name    The OID to check access for.
2023  *                On return, this parameter holds the OID actually matched
2024  *
2025  * @param namelen Number of sub-identifiers in the OID.
2026  *                On return, this parameter holds the length of the matched OID
2027  *
2028  * @param pdu     PDU requesting access to the OID.
2029  *
2030  * @param type    ANS.1 type of the value at given OID.
2031  *                (Used for catching SNMPv1 requests for SMIv2-only objects)
2032  *
2033  * @return gives VACM_SUCCESS if the OID is in the PDU, otherwise error code.
2034  */
2035 int
2036 in_a_view(oid *name, size_t *namelen, netsnmp_pdu *pdu, int type)
2037 {
2038     struct view_parameters view_parms;
2039 
2040     if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) {
2041 	/* Enable bypassing of view-based access control */
2042         return VACM_SUCCESS;
2043     }
2044 
2045     /*
2046      * check for v1 and counter64s, since snmpv1 doesn't support it
2047      */
2048 #ifndef NETSNMP_DISABLE_SNMPV1
2049     if (pdu->version == SNMP_VERSION_1 && type == ASN_COUNTER64) {
2050         return VACM_NOTINVIEW;
2051     }
2052 #endif
2053 
2054     view_parms.pdu = pdu;
2055     view_parms.name = name;
2056     if (namelen != NULL) {
2057         view_parms.namelen = *namelen;
2058     } else {
2059         view_parms.namelen = 0;
2060     }
2061     view_parms.errorcode = 0;
2062     view_parms.check_subtree = 0;
2063 
2064     switch (pdu->version) {
2065 #ifndef NETSNMP_DISABLE_SNMPV1
2066     case SNMP_VERSION_1:
2067 #endif
2068 #ifndef NETSNMP_DISABLE_SNMPV2C
2069     case SNMP_VERSION_2c:
2070 #endif
2071    case SNMP_VERSION_3:
2072         NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
2073         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
2074                             SNMPD_CALLBACK_ACM_CHECK, &view_parms);
2075         return view_parms.errorcode;
2076     }
2077   unsupported_version:
2078     return VACM_NOSECNAME;
2079 }
2080 
2081 /** Determines if the given PDU request could potentially succeed.
2082  *  (Preliminary, OID-independent validation)
2083  *
2084  * @param pdu     PDU requesting access
2085  *
2086  * @return gives VACM_SUCCESS   if the entire MIB tree is accessible
2087  *               VACM_NOTINVIEW if the entire MIB tree is inaccessible
2088  *               VACM_SUBTREE_UNKNOWN if some portions are accessible
2089  *               other codes may returned on error
2090  */
2091 int
2092 check_access(netsnmp_pdu *pdu)
2093 {                               /* IN - pdu being checked */
2094     struct view_parameters view_parms;
2095     view_parms.pdu = pdu;
2096     view_parms.name = NULL;
2097     view_parms.namelen = 0;
2098     view_parms.errorcode = 0;
2099     view_parms.check_subtree = 0;
2100 
2101     if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) {
2102 	/* Enable bypassing of view-based access control */
2103         return 0;
2104     }
2105 
2106     switch (pdu->version) {
2107 #ifndef NETSNMP_DISABLE_SNMPV1
2108     case SNMP_VERSION_1:
2109 #endif
2110 #ifndef NETSNMP_DISABLE_SNMPV2C
2111     case SNMP_VERSION_2c:
2112 #endif
2113     case SNMP_VERSION_3:
2114         NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
2115         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
2116                             SNMPD_CALLBACK_ACM_CHECK_INITIAL, &view_parms);
2117         return view_parms.errorcode;
2118     }
2119   unsupported_version:
2120     return 1;
2121 }
2122 
2123 /** Determines if the given PDU request could potentially access
2124  *   the specified MIB subtree
2125  *
2126  * @param pdu     PDU requesting access
2127  *
2128  * @param name    The OID to check access for.
2129  *
2130  * @param namelen Number of sub-identifiers in the OID.
2131  *
2132  * @return gives VACM_SUCCESS   if the entire MIB tree is accessible
2133  *               VACM_NOTINVIEW if the entire MIB tree is inaccessible
2134  *               VACM_SUBTREE_UNKNOWN if some portions are accessible
2135  *               other codes may returned on error
2136  */
2137 int
2138 netsnmp_acm_check_subtree(netsnmp_pdu *pdu, oid *name, size_t namelen)
2139 {                               /* IN - pdu being checked */
2140     struct view_parameters view_parms;
2141     view_parms.pdu = pdu;
2142     view_parms.name = name;
2143     view_parms.namelen = namelen;
2144     view_parms.errorcode = 0;
2145     view_parms.check_subtree = 1;
2146 
2147     if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) {
2148 	/* Enable bypassing of view-based access control */
2149         return 0;
2150     }
2151 
2152     switch (pdu->version) {
2153 #ifndef NETSNMP_DISABLE_SNMPV1
2154     case SNMP_VERSION_1:
2155 #endif
2156 #ifndef NETSNMP_DISABLE_SNMPV2C
2157     case SNMP_VERSION_2c:
2158 #endif
2159     case SNMP_VERSION_3:
2160         NETSNMP_RUNTIME_PROTOCOL_CHECK(pdu->version,unsupported_version);
2161         snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
2162                             SNMPD_CALLBACK_ACM_CHECK_SUBTREE, &view_parms);
2163         return view_parms.errorcode;
2164     }
2165   unsupported_version:
2166     return 1;
2167 }
2168 
2169 netsnmp_feature_child_of(get_session_for_oid,netsnmp_unused);
2170 #ifndef NETSNMP_FEATURE_REMOVE_GET_SESSION_FOR_OID
2171 netsnmp_session *
2172 get_session_for_oid(const oid *name, size_t len, const char *context_name)
2173 {
2174     netsnmp_subtree *myptr;
2175 
2176     myptr = netsnmp_subtree_find_prev(name, len,
2177 				      netsnmp_subtree_find_first(context_name),
2178 				      context_name);
2179 
2180     while (myptr && myptr->variables == NULL) {
2181         myptr = myptr->next;
2182     }
2183 
2184     if (myptr == NULL) {
2185         return NULL;
2186     } else {
2187         return myptr->session;
2188     }
2189 }
2190 #endif /* NETSNMP_FEATURE_REMOVE_GET_SESSION_FOR_OID */
2191 
2192 void
2193 setup_tree(void)
2194 {
2195     oid ccitt[1]           = { 0 };
2196     oid iso[1]             = { 1 };
2197     oid joint_ccitt_iso[1] = { 2 };
2198 
2199 #ifdef USING_AGENTX_SUBAGENT_MODULE
2200     int role =  netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
2201 				       NETSNMP_DS_AGENT_ROLE);
2202 
2203     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
2204 			   MASTER_AGENT);
2205 #endif
2206 
2207     /*
2208      * we need to have the oid's in the heap, that we can *free* it for every case,
2209      * thats the purpose of the duplicate_objid's
2210      */
2211     netsnmp_register_null(snmp_duplicate_objid(ccitt, 1), 1);
2212     netsnmp_register_null(snmp_duplicate_objid(iso, 1), 1);
2213     netsnmp_register_null(snmp_duplicate_objid(joint_ccitt_iso, 1), 1);
2214 
2215 #ifdef USING_AGENTX_SUBAGENT_MODULE
2216     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
2217 			   role);
2218 #endif
2219 }
2220 
2221 int
2222 remove_tree_entry (oid *name, size_t len) {
2223 
2224     netsnmp_subtree *sub = NULL;
2225 
2226     if ((sub = netsnmp_subtree_find(name, len, NULL, "")) == NULL) {
2227 	return MIB_NO_SUCH_REGISTRATION;
2228     }
2229 
2230     return unregister_mib_context(name, len, sub->priority,
2231 				  sub->range_subid, sub->range_ubound, "");
2232 
2233 }
2234 
2235 
2236 void
2237 shutdown_tree(void) {
2238     oid ccitt[1]           = { 0 };
2239     oid iso[1]             = { 1 };
2240     oid joint_ccitt_iso[1] = { 2 };
2241 
2242     DEBUGMSGTL(("agent_registry", "shut down tree\n"));
2243 
2244     remove_tree_entry(joint_ccitt_iso, 1);
2245     remove_tree_entry(iso, 1);
2246     remove_tree_entry(ccitt, 1);
2247 
2248 }
2249 
2250 void
2251 dump_registry(void)
2252 {
2253     struct variable *vp = NULL;
2254     netsnmp_subtree *myptr, *myptr2;
2255     u_char *s = NULL, *e = NULL, *v = NULL;
2256     size_t sl = 256, el = 256, vl = 256, sl_o = 0, el_o = 0, vl_o = 0;
2257     int i = 0;
2258 
2259     if ((s = (u_char *) calloc(sl, 1)) != NULL &&
2260         (e = (u_char *) calloc(sl, 1)) != NULL &&
2261         (v = (u_char *) calloc(sl, 1)) != NULL) {
2262 
2263         subtree_context_cache *ptr;
2264         for (ptr = context_subtrees; ptr; ptr = ptr->next) {
2265             printf("Subtrees for Context: %s\n", ptr->context_name);
2266             for (myptr = ptr->first_subtree; myptr != NULL;
2267                  myptr = myptr->next) {
2268                 sl_o = el_o = vl_o = 0;
2269 
2270                 if (!sprint_realloc_objid(&s, &sl, &sl_o, 1,
2271                                           myptr->start_a,
2272                                           myptr->start_len)) {
2273                     break;
2274                 }
2275                 if (!sprint_realloc_objid(&e, &el, &el_o, 1,
2276                                           myptr->end_a,
2277 					  myptr->end_len)) {
2278                     break;
2279                 }
2280 
2281                 if (myptr->variables) {
2282                     printf("%02x ( %s - %s ) [", myptr->flags, s, e);
2283                     for (i = 0, vp = myptr->variables;
2284                          i < myptr->variables_len; i++) {
2285                         vl_o = 0;
2286                         if (!sprint_realloc_objid
2287                             (&v, &vl, &vl_o, 1, vp->name, vp->namelen)) {
2288                             break;
2289                         }
2290                         printf("%s, ", v);
2291                         vp = (struct variable *) ((char *) vp +
2292                                                   myptr->variables_width);
2293                     }
2294                     printf("]\n");
2295                 } else {
2296                     printf("%02x   %s - %s  \n", myptr->flags, s, e);
2297                 }
2298                 for (myptr2 = myptr; myptr2 != NULL;
2299                      myptr2 = myptr2->children) {
2300                     if (myptr2->label_a && myptr2->label_a[0]) {
2301                         if (strcmp(myptr2->label_a, "old_api") == 0) {
2302                             struct variable *vp =
2303                                 (struct variable*)myptr2->reginfo->handler->myvoid;
2304 
2305                             if (!sprint_realloc_objid(&s, &sl, &sl_o, 1,
2306                                                  vp->name, vp->namelen)) {
2307                                 continue;
2308                             }
2309                             printf("\t%s[%s] %p var %s\n", myptr2->label_a,
2310                                    myptr2->reginfo->handlerName ?
2311                                    myptr2->reginfo->handlerName : "no-name",
2312                                    myptr2->reginfo, s);
2313                         } else {
2314                             printf("\t%s %s %p\n", myptr2->label_a,
2315                                    myptr2->reginfo->handlerName ?
2316                                    myptr2->reginfo->handlerName : "no-handler-name",
2317                                    myptr2->reginfo);
2318                         }
2319                     }
2320                 }
2321             }
2322         }
2323     }
2324 
2325     SNMP_FREE(s);
2326     SNMP_FREE(e);
2327     SNMP_FREE(v);
2328 
2329     dump_idx_registry();
2330 }
2331 
2332 /**  @} */
2333 /* End of MIB registration code */
2334 
2335 
2336 netsnmp_feature_child_of(register_signal, netsnmp_unused);
2337 #ifndef NETSNMP_FEATURE_REMOVE_REGISTER_SIGNAL
2338 
2339 /** @defgroup agent_signals POSIX signals support for agents.
2340  *     Registering and unregistering signal handlers.
2341  *   @ingroup agent_registry
2342  *
2343  * @{
2344  */
2345 
2346 int             external_signal_scheduled[NUM_EXTERNAL_SIGS];
2347 void            (*external_signal_handler[NUM_EXTERNAL_SIGS]) (int);
2348 
2349 #ifndef WIN32
2350 
2351 /*
2352  * TODO: add agent_SIGXXX_handler functions and `case SIGXXX: ...' lines
2353  *       below for every single that might be handled by register_signal().
2354  */
2355 
2356 RETSIGTYPE
2357 agent_SIGCHLD_handler(int sig)
2358 {
2359     external_signal_scheduled[SIGCHLD]++;
2360 #ifndef HAVE_SIGACTION
2361     /*
2362      * signal() sucks. It *might* have SysV semantics, which means that
2363      * * a signal handler is reset once it gets called. Ensure that it
2364      * * remains active.
2365      */
2366     signal(SIGCHLD, agent_SIGCHLD_handler);
2367 #endif
2368 }
2369 
2370 /** Registers a POSIX Signal handler.
2371  *  Implements the signal registering process for POSIX and non-POSIX
2372  *  systems. Also, unifies the way signals work.
2373  *  Note that the signal handler should register itself again with
2374  *  signal() call before end of execution to prevent possible problems.
2375  *
2376  *  @param sig POSIX Signal ID number, as defined in signal.h.
2377  *
2378  *  @param func New signal handler function.
2379  *
2380  *  @return value is SIG_REGISTERED_OK for success and
2381  *        SIG_REGISTRATION_FAILED if the registration can't
2382  *        be handled.
2383  */
2384 int
2385 register_signal(int sig, void (*func) (int))
2386 {
2387 
2388     switch (sig) {
2389 #if defined(SIGCHLD)
2390     case SIGCHLD:
2391 #ifdef HAVE_SIGACTION
2392         {
2393             static struct sigaction act;
2394             act.sa_handler = agent_SIGCHLD_handler;
2395             sigemptyset(&act.sa_mask);
2396             act.sa_flags = 0;
2397             sigaction(SIGCHLD, &act, NULL);
2398         }
2399 #else
2400         signal(SIGCHLD, agent_SIGCHLD_handler);
2401 #endif
2402         break;
2403 #endif
2404     default:
2405         snmp_log(LOG_CRIT,
2406                  "register_signal: signal %d cannot be handled\n", sig);
2407         return SIG_REGISTRATION_FAILED;
2408     }
2409 
2410     external_signal_handler[sig] = func;
2411     external_signal_scheduled[sig] = 0;
2412 
2413     DEBUGMSGTL(("register_signal", "registered signal %d\n", sig));
2414     return SIG_REGISTERED_OK;
2415 }
2416 
2417 /** Unregisters a POSIX Signal handler.
2418  *
2419  *  @param sig POSIX Signal ID number, as defined in signal.h.
2420  *
2421  *  @return value is SIG_UNREGISTERED_OK for success, or error code.
2422  */
2423 int
2424 unregister_signal(int sig)
2425 {
2426     signal(sig, SIG_DFL);
2427     DEBUGMSGTL(("unregister_signal", "unregistered signal %d\n", sig));
2428     return SIG_UNREGISTERED_OK;
2429 }
2430 
2431 #endif                          /* !WIN32 */
2432 
2433 /**  @} */
2434 /* End of signals support code */
2435 
2436 #endif /* NETSNMP_FEATURE_REMOVE_REGISTER_SIGNAL */
2437 
2438 /**  @} */
2439 
2440