Mercurial > ecos
diff packages/net/snmp/agent/current/src/agent_registry.c @ 103:95f3e12a6327 ecos-sw-2000-06-23
Merge from eCos master repository on 2000-06-23-16:41:10-BST
| author | jlarmour |
|---|---|
| date | Fri, 23 Jun 2000 17:06:31 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/snmp/agent/current/src/agent_registry.c @@ -0,0 +1,1460 @@ +//========================================================================== +// +// ./agent/current/src/agent_registry.c +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####UCDSNMPCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from the UCD-SNMP +// project, <http://ucd-snmp.ucdavis.edu/> from the University of +// California at Davis, which was originally based on the Carnegie Mellon +// University SNMP implementation. Portions of this software are therefore +// covered by the appropriate copyright disclaimers included herein. +// +// The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2" +// ------------------------------------------- +// +//####UCDSNMPCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt +// Contributors: hmt +// Date: 2000-05-30 +// Purpose: Port of UCD-SNMP distribution to eCos. +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +/******************************************************************** + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + Derivative Work - +Copyright 1996, 1998, 1999, 2000 The Regents of the University of California + + All Rights Reserved + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies and +that both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU and The Regents of +the University of California not be used in advertising or publicity +pertaining to distribution of the software without specific written +permission. + +CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR +THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*********************************************************************/ +/* + * agent_registry.c + * + * Maintain a registry of MIB subtrees, together + * with related information regarding mibmodule, sessions, etc + */ + +#define IN_SNMP_VARS_C + +#include <config.h> +#if HAVE_STRING_H +#include <string.h> +#endif +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <sys/types.h> +#include <stdio.h> +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#if HAVE_WINSOCK_H +#include <winsock.h> +#endif +#if TIME_WITH_SYS_TIME +# ifdef WIN32 +# include <sys/timeb.h> +# else +# include <sys/time.h> +# endif +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include "mibincl.h" +#include "snmp_client.h" +#include "default_store.h" +#include "ds_agent.h" +#include "callback.h" +#include "agent_callbacks.h" +#include "agent_registry.h" +#include "snmp_alarm.h" + +#include "snmpd.h" +#include "mibgroup/struct.h" +#include "mib_module_includes.h" + +#ifdef USING_AGENTX_SUBAGENT_MODULE +#include "agentx/subagent.h" +#include "agentx/client.h" +#endif + + +struct snmp_index { + struct variable_list varbind; /* or pointer to var_list ? */ + struct snmp_session *session; /* NULL implies unused ? */ + struct snmp_index *next_oid; + struct snmp_index *prev_oid; + struct snmp_index *next_idx; +} *snmp_index_head = NULL; +struct subtree *subtrees; + +int tree_compare(const struct subtree *ap, const struct subtree *bp) +{ + return snmp_oid_compare(ap->name,ap->namelen,bp->name,bp->namelen); +} + + + + /* + * Split the subtree into two at the specified point, + * returning the new (second) subtree + */ +struct subtree * +split_subtree(struct subtree *current, oid name[], int name_len ) +{ + struct subtree *new_sub, *ptr; + int i; + char *cp; + + if ( snmp_oid_compare(name, name_len, + current->end, current->end_len) > 0 ) + return NULL; /* Split comes after the end of this subtree */ + + new_sub = (struct subtree *)malloc(sizeof(struct subtree)); + if ( new_sub == NULL ) + return NULL; + memcpy(new_sub, current, sizeof(struct subtree)); + + /* Set up the point of division */ + memcpy(current->end, name, name_len*sizeof(oid)); + memcpy(new_sub->start, name, name_len*sizeof(oid)); + current->end_len = name_len; + new_sub->start_len = name_len; + + /* + * Split the variables between the two new subtrees + */ + i = current->variables_len; + current->variables_len = 0; + + for ( ; i > 0 ; i-- ) { + /* Note that the variable "name" field omits + the prefix common to the whole registration, + hence the strange comparison here */ + if ( snmp_oid_compare( new_sub->variables[0].name, + new_sub->variables[0].namelen, + name + current->namelen, + name_len - current->namelen ) >= 0 ) + break; /* All following variables belong to the second subtree */ + + current->variables_len++; + new_sub->variables_len--; + cp = (char *)new_sub->variables; + new_sub->variables = (struct variable *)(cp + new_sub->variables_width); + } + + /* Delegated trees should retain their variables regardless */ + if ( current->variables_len > 0 && + IS_DELEGATED((u_char)current->variables[0].type)) { + new_sub->variables_len = 1; + new_sub->variables = current->variables; + } + + /* Propogate this split down through any children */ + if ( current->children ) + new_sub->children = split_subtree(current->children, name, name_len); + + /* Retain the correct linking of the list */ + for ( ptr = current ; ptr != NULL ; ptr=ptr->children ) + ptr->next = new_sub; + for ( ptr = new_sub ; ptr != NULL ; ptr=ptr->children ) + ptr->prev = current; + for ( ptr = new_sub->next ; ptr != NULL ; ptr=ptr->children ) + ptr->prev = new_sub; + + return new_sub; +} + +int +load_subtree( struct subtree *new_sub ) +{ + struct subtree *tree1, *tree2, *new2; + struct subtree *prev, *next; + int res; + + if ( new_sub == NULL ) + return MIB_REGISTERED_OK; /* Degenerate case */ + + /* + * Find the subtree that contains the start of + * the new subtree (if any)... + */ + tree1 = find_subtree( new_sub->start, new_sub->start_len, NULL ); + /* + * ...and the subtree that follows the new one + * (NULL implies this is the final region covered) + */ + if ( tree1 == NULL ) + tree2 = find_subtree_next( new_sub->start, new_sub->start_len, NULL ); + else + tree2 = tree1->next; + + + /* + * Handle new subtrees that start in virgin territory. + */ + if ( tree1 == NULL ) { + new2 = NULL; + /* Is there any overlap with later subtrees ? */ + if ( tree2 && snmp_oid_compare( new_sub->end, new_sub->end_len, + tree2->start, tree2->start_len ) > 0 ) + new2 = split_subtree( new_sub, tree2->start, tree2->start_len ); + + /* + * Link the new subtree (less any overlapping region) + * with the list of existing registrations + */ + if ( tree2 ) { + new_sub->prev = tree2->prev; + tree2->prev = new_sub; + } + else + new_sub->prev = find_subtree_previous( new_sub->start, new_sub->start_len, NULL ); + + if ( new_sub->prev ) + new_sub->prev->next = new_sub; + else + subtrees = new_sub; + + new_sub->next = tree2; + + /* + * If there was any overlap, + * recurse to merge in the overlapping region + * (including anything that may follow the overlap) + */ + if ( new2 ) + return load_subtree( new2 ); + } + + else { + /* + * If the new subtree starts *within* an existing registration + * (rather than at the same point as it), then split the + * existing subtree at this point. + */ + if ( snmp_oid_compare( new_sub->start, new_sub->start_len, + tree1->start, tree1->start_len) != 0 ) + tree1 = split_subtree( tree1, new_sub->start, new_sub->start_len); + if ( tree1 == NULL ) + return MIB_REGISTRATION_FAILED; + + /* Now consider the end of this existing subtree: + * If it matches the new subtree precisely, + * simply merge the new one into the list of children + * If it includes the whole of the new subtree, + * split it at the appropriate point, and merge again + * + * If the new subtree extends beyond this existing region, + * split it, and recurse to merge the two parts. + */ + + switch ( snmp_oid_compare( new_sub->end, new_sub->end_len, + tree1->end, tree1->end_len)) { + + case -1: /* Existing subtree contains new one */ + (void) split_subtree( tree1, + new_sub->end, new_sub->end_len); + /* Fall Through */ + + case 0: /* The two trees match precisely */ + /* + * Note: This is the only point where the original + * registration OID ("name") is used + */ + prev = NULL; + next = tree1; + while ( next && next->namelen > new_sub->namelen ) { + prev = next; + next = next->children; + } + while ( next && next->namelen == new_sub->namelen && + next->priority < new_sub->priority ) { + prev = next; + next = next->children; + } + if ( next && next->namelen == new_sub->namelen && + next->priority == new_sub->priority ) + return MIB_DUPLICATE_REGISTRATION; + + if ( prev ) { + new_sub->children = next; + prev->children = new_sub; + new_sub->prev = prev->prev; + new_sub->next = prev->next; + } + else { + new_sub->children = next; + new_sub->prev = next->prev; + new_sub->next = next->next; + + for ( next = new_sub->next ; + next != NULL ; + next = next->children ) + next->prev = new_sub; + + for ( prev = new_sub->prev ; + prev != NULL ; + prev = prev->children ) + prev->next = new_sub; + } + break; + + case 1: /* New subtree contains the existing one */ + new2 = split_subtree( new_sub, + tree1->end, tree1->end_len); + res = load_subtree( new_sub ); + if ( res != MIB_REGISTERED_OK ) + return res; + return load_subtree( new2 ); + + } + + } + return 0; +} + + +int +register_mib_range(const char *moduleName, + struct variable *var, + size_t varsize, + size_t numvars, + oid *mibloc, + size_t mibloclen, + int priority, + int range_subid, + oid range_ubound, + struct snmp_session *ss) +{ + struct subtree *subtree, *sub2; + int res, i; + struct register_parameters reg_parms; + + subtree = (struct subtree *) malloc(sizeof(struct subtree)); + if ( subtree == NULL ) + return MIB_REGISTRATION_FAILED; + memset(subtree, 0, sizeof(struct subtree)); + + DEBUGMSGTL(("register_mib", "registering \"%s\" at ", moduleName)); + DEBUGMSGOID(("register_mib", mibloc, mibloclen)); + DEBUGMSG(("register_mib","\n")); + + /* + * Create the new subtree node being registered + */ + memcpy(subtree->name, mibloc, mibloclen*sizeof(oid)); + subtree->namelen = (u_char) mibloclen; + memcpy(subtree->start, mibloc, mibloclen*sizeof(oid)); + subtree->start_len = (u_char) mibloclen; + memcpy(subtree->end, mibloc, mibloclen*sizeof(oid)); + subtree->end[ mibloclen-1 ]++; /* XXX - or use 'variables' info ? */ + subtree->end_len = (u_char) mibloclen; + memcpy(subtree->label, moduleName, strlen(moduleName)+1); + if ( var ) { + subtree->variables = (struct variable *) malloc(varsize*numvars); + memcpy(subtree->variables, var, numvars*varsize); + subtree->variables_len = numvars; + subtree->variables_width = varsize; + } + subtree->priority = priority; + subtree->session = ss; + res = load_subtree(subtree); + + /* + * If registering a range, + * use the first subtree as a template + * for the rest of the range + */ + if (( res == MIB_REGISTERED_OK ) && ( range_subid != 0 )) { + for ( i = mibloc[range_subid-1] +1 ; i < (int)range_ubound ; i++ ) { + sub2 = (struct subtree *) malloc(sizeof(struct subtree)); + if ( sub2 == NULL ) { + unregister_mib_range( mibloc, mibloclen, priority, + range_subid, range_ubound); + return MIB_REGISTRATION_FAILED; + } + memcpy( sub2, subtree, sizeof(struct subtree)); + sub2->start[range_subid-1] = i; + sub2->end[ range_subid-1] = i; /* XXX - ???? */ + res = load_subtree(sub2); + if ( res != MIB_REGISTERED_OK ) { + unregister_mib_range( mibloc, mibloclen, priority, + range_subid, range_ubound); + return MIB_REGISTRATION_FAILED; + } + } + } + + + reg_parms.name = mibloc; + reg_parms.namelen = mibloclen; + reg_parms.priority = priority; + reg_parms.range_subid = range_subid; + reg_parms.range_ubound = range_ubound; + snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_REGISTER_OID, + ®_parms); + + return res; +} + +int +register_mib_priority(const char *moduleName, + struct variable *var, + size_t varsize, + size_t numvars, + oid *mibloc, + size_t mibloclen, + int priority) +{ + return register_mib_range( moduleName, var, varsize, numvars, + mibloc, mibloclen, priority, 0, 0, NULL ); +} + +int +register_mib(const char *moduleName, + struct variable *var, + size_t varsize, + size_t numvars, + oid *mibloc, + size_t mibloclen) +{ + return register_mib_priority( moduleName, var, varsize, numvars, + mibloc, mibloclen, DEFAULT_MIB_PRIORITY ); +} + + +void +unload_subtree( struct subtree *sub, struct subtree *prev) +{ + struct subtree *ptr; + + if ( prev != NULL ) { /* non-leading entries are easy */ + prev->children = sub->children; + return; + } + /* otherwise, we need to amend our neighbours as well */ + + if ( sub->children == NULL) { /* just remove this node completely */ + for (ptr = sub->prev ; ptr ; ptr=ptr->children ) + ptr->next = sub->next; + for (ptr = sub->next ; ptr ; ptr=ptr->children ) + ptr->prev = sub->prev; + return; + } + else { + for (ptr = sub->prev ; ptr ; ptr=ptr->children ) + ptr->next = sub->children; + for (ptr = sub->next ; ptr ; ptr=ptr->children ) + ptr->prev = sub->children; + return; + } +} + +int +unregister_mib_range( oid *name, size_t len, int priority, + int range_subid, oid range_ubound) +{ + struct subtree *list, *myptr; + struct subtree *prev, *child; /* loop through children */ + struct register_parameters reg_parms; + + list = find_subtree( name, len, subtrees ); + if ( list == NULL ) + return MIB_NO_SUCH_REGISTRATION; + + for ( child=list, prev=NULL; child != NULL; + prev=child, child=child->children ) { + if (( snmp_oid_compare( child->name, child->namelen, name, len) == 0 ) + && ( child->priority == priority )) + break; /* found it */ + } + if ( child == NULL ) + return MIB_NO_SUCH_REGISTRATION; + + unload_subtree( child, prev ); + myptr = child; /* remember this for later */ + + /* + * Now handle any occurances in the following subtrees, + * as a result of splitting this range. Due to the + * nature of the way such splits work, the first + * subtree 'slice' that doesn't refer to the given + * name marks the end of the original region. + * + * This should also serve to register ranges. + */ + + for ( list = myptr->next ; list != NULL ; list=list->next ) { + for ( child=list, prev=NULL; child != NULL; + prev=child, child=child->children ) { + if (( snmp_oid_compare( child->name, child->namelen, + name, len) == 0 ) + && ( child->priority == priority )) { + + unload_subtree( child, prev ); + free_subtree( child ); + break; + } + } + if ( child == NULL ) /* Didn't find the given name */ + break; + } + free_subtree( myptr ); + + reg_parms.name = name; + reg_parms.namelen = len; + reg_parms.priority = priority; + reg_parms.range_subid = range_subid; + reg_parms.range_ubound = range_ubound; + snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_UNREGISTER_OID, + ®_parms); + + return MIB_UNREGISTERED_OK; +} + +int +unregister_mib_priority(oid *name, size_t len, int priority) +{ + return unregister_mib_range( name, len, priority, 0, 0 ); +} + +int +unregister_mib(oid *name, + size_t len) +{ + return unregister_mib_priority( name, len, DEFAULT_MIB_PRIORITY ); +} + +void +unregister_mibs_by_session (struct snmp_session *ss) +{ + struct subtree *list, *list2; + struct subtree *child, *prev, *next_child; + + for( list = subtrees; list != NULL; list = list2) { + list2 = list->next; + for ( child=list, prev=NULL; child != NULL; child=next_child ) { + + next_child = child->children; + if (( (ss->flags & SNMP_FLAGS_SUBSESSION) && child->session == ss ) || + (!(ss->flags & SNMP_FLAGS_SUBSESSION) && + child->session->subsession == ss )) { + unload_subtree( child, prev ); + free_subtree( child ); + } + else + prev = child; + } + } +} + + +struct subtree * +free_subtree(struct subtree *st) +{ + struct subtree *ret = NULL; + if ((snmp_oid_compare(st->name, st->namelen, st->start, st->start_len) == 0) + && (st->variables != NULL)) + free(st->variables); + if (st->next != NULL) + ret = st->next; + free(st); + return ret; +} + +/* in_a_view: determines if a given snmp_pdu is allowed to see a + given name/namelen OID pointer + name IN - name of var, OUT - name matched + nameLen IN -number of sub-ids in name, OUT - subid-is in matched name + pi IN - relevant auth info re PDU + cvp IN - relevant auth info re mib module +*/ + +int +in_a_view(oid *name, /* IN - name of var, OUT - name matched */ + size_t *namelen, /* IN -number of sub-ids in name*/ + struct snmp_pdu *pdu, /* IN - relevant auth info re PDU */ + int type) /* IN - variable type being checked */ +{ + + struct view_parameters view_parms; + view_parms.pdu = pdu; + view_parms.name = name; + if (namelen) + view_parms.namelen = *namelen; + else + view_parms.namelen = 0; + view_parms.errorcode = 0; + + if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) + return 0; /* Enable bypassing of view-based access control */ + + /* check for v1 and counter64s, since snmpv1 doesn't support it */ + if (pdu->version == SNMP_VERSION_1 && type == ASN_COUNTER64) + return 5; + switch (pdu->version) { + case SNMP_VERSION_1: + case SNMP_VERSION_2c: + case SNMP_VERSION_3: + snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_ACM_CHECK, + &view_parms); + return view_parms.errorcode; + } + return 1; +} + +/* in_a_view: determines if a given snmp_pdu is ever going to be allowed to do + anynthing or if it's not going to ever be authenticated. */ +int +check_access(struct snmp_pdu *pdu) /* IN - pdu being checked */ +{ + struct view_parameters view_parms; + view_parms.pdu = pdu; + view_parms.name = 0; + view_parms.namelen = 0; + view_parms.errorcode = 0; + + if (pdu->flags & UCD_MSG_FLAG_ALWAYS_IN_VIEW) + return 0; /* Enable bypassing of view-based access control */ + + switch (pdu->version) { + case SNMP_VERSION_1: + case SNMP_VERSION_2c: + case SNMP_VERSION_3: + snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, + SNMPD_CALLBACK_ACM_CHECK_INITIAL, + &view_parms); + return view_parms.errorcode; + } + return 1; +} + +/* lexicographical compare two object identifiers. + * Returns -1 if name1 < name2, + * 0 if name1 = name2, or name1 matches name2 for length of name2 + * 1 if name1 > name2 + * + * Note: snmp_oid_compare checks len2 before last return. + */ +int +compare_tree(const oid *in_name1, + size_t len1, + const oid *in_name2, + size_t len2) +{ + register int len, res; + register const oid * name1 = in_name1; + register const oid * name2 = in_name2; + + /* len = minimum of len1 and len2 */ + if (len1 < len2) + len = len1; + else + len = len2; + /* find first non-matching OID */ + while(len-- > 0){ + res = *(name1++) - *(name2++); + if (res < 0) + return -1; + if (res > 0) + return 1; + } + /* both OIDs equal up to length of shorter OID */ + if (len1 < len2) + return -1; + + /* name1 matches name2 for length of name2, or they are equal */ + return 0; +} + +struct subtree *find_subtree_previous(oid *name, + size_t len, + struct subtree *subtree) +{ + struct subtree *myptr, *previous = NULL; + + if ( subtree ) + myptr = subtree; + else + myptr = subtrees; /* look through everything */ + + for( ; myptr != NULL; previous = myptr, myptr = myptr->next) { + if (snmp_oid_compare(name, len, myptr->start, myptr->start_len) < 0) + return previous; + } + return previous; +} + +struct subtree *find_subtree_next(oid *name, + size_t len, + struct subtree *subtree) +{ + struct subtree *myptr = NULL; + + myptr = find_subtree_previous(name, len, subtree); + if ( myptr != NULL ) { + myptr = myptr->next; + while ( myptr && (myptr->variables == NULL || myptr->variables_len == 0) ) + myptr = myptr->next; + return myptr; + } + else if (subtree && snmp_oid_compare(name, len, subtree->start, subtree->start_len) < 0) + return subtree; + else + return NULL; +} + +struct subtree *find_subtree(oid *name, + size_t len, + struct subtree *subtree) +{ + struct subtree *myptr; + + myptr = find_subtree_previous(name, len, subtree); + if (myptr && snmp_oid_compare(name, len, myptr->end, myptr->end_len) < 0) + return myptr; + + return NULL; +} + +struct snmp_session *get_session_for_oid( oid *name, size_t len) +{ + struct subtree *myptr; + + myptr = find_subtree_previous(name, len, subtrees); + while ( myptr && myptr->variables == NULL ) + myptr = myptr->next; + + if ( myptr == NULL ) + return NULL; + else + return myptr->session; +} + + + +static struct subtree root_subtrees[] = { + { { 0 }, 1 }, /* ccitt */ + { { 1 }, 1 }, /* iso */ + { { 2 }, 1 } /* joint-ccitt-iso */ +}; + + +void setup_tree (void) +{ +#ifdef USING_AGENTX_SUBAGENT_MODULE + int role; + + role = ds_get_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE); + ds_set_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE, MASTER_AGENT); +#endif + + register_mib("", NULL, 0, 0, + root_subtrees[0].name, root_subtrees[0].namelen); + register_mib("", NULL, 0, 0, + root_subtrees[1].name, root_subtrees[1].namelen); + register_mib("", NULL, 0, 0, + root_subtrees[2].name, root_subtrees[2].namelen); + + /* Support for 'static' subtrees (subtrees_old) has now been dropped */ + + /* No longer necessary to sort the mib tree - this is inherent in + the construction of the subtree structure */ + +#ifdef USING_AGENTX_SUBAGENT_MODULE + ds_set_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE, role); +#endif +} + + /* + * Initial support for index allocation + */ +extern struct snmp_session *main_session; + +char * +register_string_index( oid *name, size_t name_len, char *cp ) +{ + struct variable_list varbind, *res; + + memset( &varbind, 0, sizeof(struct variable_list)); + varbind.type = ASN_OCTET_STR; + snmp_set_var_objid( &varbind, name, name_len ); + if ( cp != ANY_STRING_INDEX ) { + snmp_set_var_value( &varbind, (u_char *)cp, strlen(cp) ); + res = register_index( &varbind, ALLOCATE_THIS_INDEX, main_session ); + } + else + res = register_index( &varbind, ALLOCATE_ANY_INDEX, main_session ); + + if ( res == NULL ) + return NULL; + else + return (char *)res->val.string; +} + +int +register_int_index( oid *name, size_t name_len, int val ) +{ + struct variable_list varbind, *res; + + memset( &varbind, 0, sizeof(struct variable_list)); + varbind.type = ASN_INTEGER; + snmp_set_var_objid( &varbind, name, name_len ); + varbind.val.string = varbind.buf; + if ( val != ANY_INTEGER_INDEX ) { + varbind.val_len = sizeof(long); + *varbind.val.integer = val; + res = register_index( &varbind, ALLOCATE_THIS_INDEX, main_session ); + } + else + res = register_index( &varbind, ALLOCATE_ANY_INDEX, main_session ); + + if ( res == NULL ) + return -1; + else + return *res->val.integer; +} + +struct variable_list * +register_oid_index( oid *name, size_t name_len, + oid *value, size_t value_len ) +{ + struct variable_list varbind; + + memset( &varbind, 0, sizeof(struct variable_list)); + varbind.type = ASN_OBJECT_ID; + snmp_set_var_objid( &varbind, name, name_len ); + if ( value != ANY_OID_INDEX ) { + snmp_set_var_value( &varbind, (u_char*)value, value_len*sizeof(oid) ); + return( register_index( &varbind, ALLOCATE_THIS_INDEX, main_session )); + } + else + return( register_index( &varbind, ALLOCATE_ANY_INDEX, main_session )); +} + +struct variable_list* +register_index(struct variable_list *varbind, int flags, struct snmp_session *ss ) +{ + struct snmp_index *new_index, *idxptr, *idxptr2; + struct snmp_index *prev_oid_ptr, *prev_idx_ptr; + int res, res2, i; + +#if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING) + if (ds_get_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE) == SUB_AGENT ) + return( agentx_register_index( ss, varbind, flags )); +#endif + /* Look for the requested OID entry */ + prev_oid_ptr = NULL; + prev_idx_ptr = NULL; + res = 1; + res2 = 1; + for( idxptr = snmp_index_head ; idxptr != NULL; + prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) { + if ((res = snmp_oid_compare(varbind->name, varbind->name_length, + idxptr->varbind.name, + idxptr->varbind.name_length)) <= 0 ) + break; + } + + /* Found the OID - now look at the registered indices */ + if ( res == 0 && idxptr ) { + if ( varbind->type != idxptr->varbind.type ) + return NULL; /* wrong type */ + + /* + * If we've been asked for an arbitrary new value, + * then find the end of the list. + * If we've been asked for any arbitrary value, + * then look for an unused entry, and use that. + * If there aren't any, continue as for new. + * Otherwise, locate the given value in the (sorted) + * list of already allocated values + */ + if ( flags & ALLOCATE_ANY_INDEX ) { + for(idxptr2 = idxptr ; idxptr2 != NULL; + prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) { + if ( flags == ALLOCATE_ANY_INDEX && idxptr2->session == NULL ) { + idxptr2->session = ss ; + return &idxptr2->varbind; + } + } + } + else { + for(idxptr2 = idxptr ; idxptr2 != NULL; + prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) { + switch ( varbind->type ) { + case ASN_INTEGER: + res2 = (*varbind->val.integer - *idxptr2->varbind.val.integer); + break; + case ASN_OCTET_STR: + i = SNMP_MIN(varbind->val_len, idxptr2->varbind.val_len); + res2 = memcmp(varbind->val.string, idxptr2->varbind.val.string, i); + break; + case ASN_OBJECT_ID: + res2 = snmp_oid_compare(varbind->val.objid, varbind->val_len/sizeof(oid), + idxptr2->varbind.val.objid, + idxptr2->varbind.val_len/sizeof(oid)); + break; + default: + return NULL; /* wrong type */ + } + if ( res2 <= 0 ) + break; + } + if ( res2 == 0 ) + return NULL; /* duplicate value */ + } + } + + /* + * OK - we've now located where the new entry needs to + * be fitted into the index registry tree + * To recap: + * 'prev_oid_ptr' points to the head of the OID index + * list prior to this one. If this is null, then + * it means that this is the first OID in the list. + * 'idxptr' points either to the head of this OID list, + * or the next OID (if this is a new OID request) + * These can be distinguished by the value of 'res'. + * + * 'prev_idx_ptr' points to the index entry that sorts + * immediately prior to the requested value (if any). + * If an arbitrary value is required, then this will + * point to the last allocated index. + * If this pointer is null, then either this is a new + * OID request, or the requested value is the first + * in the list. + * 'idxptr2' points to the next sorted index (if any) + * but is not actually needed any more. + * + * Clear? Good! + * I hope you've been paying attention. + * There'll be a test later :-) + */ + + /* + * We proceed by creating the new entry + * (by copying the entry provided) + */ + new_index = (struct snmp_index *)malloc( sizeof( struct snmp_index )); + if (new_index == NULL) + return NULL; + if (snmp_clone_var( varbind, &new_index->varbind ) != 0 ) { + free( new_index ); + return NULL; + } + new_index->session = ss; + + if ( varbind->type == ASN_OCTET_STR && flags == ALLOCATE_THIS_INDEX ) + new_index->varbind.val.string[new_index->varbind.val_len] = 0; + + /* + * If we've been given a value, then we can use that, but + * otherwise, we need to create a new value for this entry. + * Note that ANY_INDEX and NEW_INDEX are both covered by this + * test (since NEW_INDEX & ANY_INDEX = ANY_INDEX, remember?) + */ + if ( flags & ALLOCATE_ANY_INDEX ) { + if ( prev_idx_ptr ) { + if ( snmp_clone_var( &prev_idx_ptr->varbind, &new_index->varbind ) != 0 ) { + free( new_index ); + return NULL; + } + } + else + new_index->varbind.val.string = new_index->varbind.buf; + + switch ( varbind->type ) { + case ASN_INTEGER: + if ( prev_idx_ptr ) { + (*new_index->varbind.val.integer)++; + } + else + *(new_index->varbind.val.integer) = 1; + new_index->varbind.val_len = sizeof(long); + break; + case ASN_OCTET_STR: + if ( prev_idx_ptr ) { + i = new_index->varbind.val_len-1; + while ( new_index->varbind.buf[ i ] == 'z' ) { + new_index->varbind.buf[ i ] = 'a'; + i--; + if ( i < 0 ) { + i = new_index->varbind.val_len; + new_index->varbind.buf[ i ] = 'a'; + new_index->varbind.buf[ i+1 ] = 0; + } + } + new_index->varbind.buf[ i ]++; + } + else + strcpy((char *)new_index->varbind.buf, "aaaa"); + new_index->varbind.val_len = strlen((char *)new_index->varbind.buf); + break; + case ASN_OBJECT_ID: + if ( prev_idx_ptr ) { + i = prev_idx_ptr->varbind.val_len/sizeof(oid) -1; + while ( new_index->varbind.val.objid[ i ] == 255 ) { + new_index->varbind.val.objid[ i ] = 1; + i--; + if ( i == 0 && new_index->varbind.val.objid[0] == 2 ) { + new_index->varbind.val.objid[ 0 ] = 1; + i = new_index->varbind.val_len/sizeof(oid); + new_index->varbind.val.objid[ i ] = 0; + new_index->varbind.val_len += sizeof(oid); + } + } + new_index->varbind.val.objid[ i ]++; + } + else { + /* If the requested OID name is small enough, + * append another OID (1) and use this as the + * default starting value for new indexes. + */ + if ( (varbind->name_length+1) * sizeof(oid) <= 40 ) { + for ( i = 0 ; i < (int)varbind->name_length ; i++ ) + new_index->varbind.val.objid[i] = varbind->name[i]; + new_index->varbind.val.objid[varbind->name_length] = 1; + new_index->varbind.val_len = + (varbind->name_length+1) * sizeof(oid); + } + else { + /* Otherwise use '.1.1.1.1...' */ + i = 40/sizeof(oid); + if ( i > 4 ) + i = 4; + new_index->varbind.val_len = i * (sizeof(oid)); + for (i-- ; i>=0 ; i-- ) + new_index->varbind.val.objid[i] = 1; + } + } + break; + default: + free( new_index ); + return NULL; /* Index type not supported */ + } + } + + /* + * Right - we've set up the new entry. + * All that remains is to link it into the tree. + * There are a number of possible cases here, + * so watch carefully. + */ + if ( prev_idx_ptr ) { + new_index->next_idx = prev_idx_ptr->next_idx; + new_index->next_oid = prev_idx_ptr->next_oid; + prev_idx_ptr->next_idx = new_index; + } + else { + if ( res == 0 && idxptr ) { + new_index->next_idx = idxptr; + new_index->next_oid = idxptr->next_oid; + } + else { + new_index->next_idx = NULL; + new_index->next_oid = idxptr; + } + + if ( prev_oid_ptr ) { + while ( prev_oid_ptr ) { + prev_oid_ptr->next_oid = new_index; + prev_oid_ptr = prev_oid_ptr->next_idx; + } + } + else + snmp_index_head = new_index; + } + return &new_index->varbind; +} + + /* + * Release an allocated index, + * to allow it to be used elsewhere + */ +int +release_index(struct variable_list *varbind) +{ + return( unregister_index( varbind, TRUE, NULL )); +} + + /* + * Completely remove an allocated index, + * due to errors in the registration process. + */ +int +remove_index(struct variable_list *varbind, struct snmp_session *ss) +{ + return( unregister_index( varbind, FALSE, ss )); +} + +void +unregister_index_by_session(struct snmp_session *ss) +{ + struct snmp_index *idxptr, *idxptr2; + for(idxptr = snmp_index_head ; idxptr != NULL; idxptr = idxptr->next_oid) + for(idxptr2 = idxptr ; idxptr2 != NULL; idxptr2 = idxptr2->next_idx) + if ( idxptr2->session == ss ) + idxptr2->session = NULL; +} + + +int +unregister_index(struct variable_list *varbind, int remember, struct snmp_session *ss) +{ + struct snmp_index *idxptr, *idxptr2; + struct snmp_index *prev_oid_ptr, *prev_idx_ptr; + int res, res2, i; + +#if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING) + if (ds_get_boolean(DS_APPLICATION_ID, DS_AGENT_ROLE) == SUB_AGENT ) + return( agentx_unregister_index( ss, varbind )); +#endif + /* Look for the requested OID entry */ + prev_oid_ptr = NULL; + prev_idx_ptr = NULL; + res = 1; + res2 = 1; + for( idxptr = snmp_index_head ; idxptr != NULL; + prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) { + if ((res = snmp_oid_compare(varbind->name, varbind->name_length, + idxptr->varbind.name, + idxptr->varbind.name_length)) <= 0 ) + break; + } + + if ( res != 0 ) + return INDEX_ERR_NOT_ALLOCATED; + if ( varbind->type != idxptr->varbind.type ) + return INDEX_ERR_WRONG_TYPE; + + for(idxptr2 = idxptr ; idxptr2 != NULL; + prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) { + i = SNMP_MIN(varbind->val_len, idxptr2->varbind.val_len); + res2 = memcmp(varbind->val.string, idxptr2->varbind.val.string, i); + if ( res2 <= 0 ) + break; + } + if ( res2 != 0 ) + return INDEX_ERR_NOT_ALLOCATED; + if ( ss != idxptr2->session ) + return INDEX_ERR_WRONG_SESSION; + + /* + * If this is a "normal" index unregistration, + * mark the index entry as unused, but leave + * it in situ. This allows differentiation + * between ANY_INDEX and NEW_INDEX + */ + if ( remember ) { + idxptr2->session = NULL; /* Unused index */ + return SNMP_ERR_NOERROR; + } + /* + * If this is a failed attempt to register a + * number of indexes, the successful ones + * must be removed completely. + */ + if ( prev_idx_ptr ) { + prev_idx_ptr->next_idx = idxptr2->next_idx; + } + else if ( prev_oid_ptr ) { + if ( idxptr2->next_idx ) /* Use p_idx_ptr as a temp variable */ + prev_idx_ptr = idxptr2->next_idx; + else + prev_idx_ptr = idxptr2->next_oid; + while ( prev_oid_ptr ) { + prev_oid_ptr->next_oid = prev_idx_ptr; + prev_oid_ptr = prev_oid_ptr->next_idx; + } + } + else { + if ( idxptr2->next_idx ) + snmp_index_head = idxptr2->next_idx; + else + snmp_index_head = idxptr2->next_oid; + } + snmp_free_var( (struct variable_list *)idxptr2 ); + return SNMP_ERR_NOERROR; +} + + +void dump_registry( void ) +{ + struct subtree *myptr, *myptr2; + struct snmp_index *idxptr, *idxptr2; + char start_oid[SPRINT_MAX_LEN]; + char end_oid[SPRINT_MAX_LEN]; + + for( myptr = subtrees ; myptr != NULL; myptr = myptr->next) { + sprint_objid(start_oid, myptr->start, myptr->start_len); + sprint_objid(end_oid, myptr->end, myptr->end_len); + printf("%c %s - %s %c\n", + ( myptr->variables ? ' ' : '(' ), + start_oid, end_oid, + ( myptr->variables ? ' ' : ')' )); + for( myptr2 = myptr ; myptr2 != NULL; myptr2 = myptr2->children) { + if ( myptr2->label && myptr2->label[0] ) + printf("\t%s\n", myptr2->label); + } + } + + if ( snmp_index_head ) + printf("\nIndex Allocations:\n"); + for( idxptr = snmp_index_head ; idxptr != NULL; idxptr = idxptr->next_oid) { + sprint_objid(start_oid, idxptr->varbind.name, idxptr->varbind.name_length); + printf("%s indexes:\n", start_oid); + for( idxptr2 = idxptr ; idxptr2 != NULL; idxptr2 = idxptr2->next_idx) { + switch( idxptr2->varbind.type ) { + case ASN_INTEGER: + printf(" %c %ld %c\n", + ( idxptr2->session ? ' ' : '(' ), + *idxptr2->varbind.val.integer, + ( idxptr2->session ? ' ' : ')' )); + break; + case ASN_OCTET_STR: + printf(" %c %s %c\n", + ( idxptr2->session ? ' ' : '(' ), + idxptr2->varbind.val.string, + ( idxptr2->session ? ' ' : ')' )); + break; + case ASN_OBJECT_ID: + sprint_objid(end_oid, idxptr2->varbind.val.objid, + idxptr2->varbind.val_len/sizeof(oid)); + printf(" %c %s %c\n", + ( idxptr2->session ? ' ' : '(' ), + end_oid, + ( idxptr2->session ? ' ' : ')' )); + break; + default: + printf("unsupported type (%d)\n", + idxptr2->varbind.type); + } + } + } +} + +#ifdef TESTING +struct variable_list varbind; +struct snmp_session main_sess, *main_session=&main_sess; + +void +test_string_register( int n, char *cp ) +{ + varbind.name[4] = n; + if (register_string_index(varbind.name, varbind.name_length, cp) == NULL) + printf("allocating %s failed\n", cp); +} + +void +test_int_register( int n, int val ) +{ + varbind.name[4] = n; + if (register_int_index( varbind.name, varbind.name_length, val ) == -1 ) + printf("allocating %d/%d failed\n", n, val); +} + +void +test_oid_register( int n, int subid ) +{ + struct variable_list *res; + + varbind.name[4] = n; + if ( subid != -1 ) { + varbind.val.objid[5] = subid; + res = register_oid_index(varbind.name, varbind.name_length, + varbind.val.objid, + varbind.val_len/sizeof(oid) ); + } + else + res = register_oid_index(varbind.name, varbind.name_length, NULL, 0); + + if (res == NULL ) + printf("allocating %d/%d failed\n", n, subid); +} + +void +main( int argc, char argv[] ) +{ + oid name[] = { 1, 2, 3, 4, 0 }; + int i; + + memset( &varbind, 0, sizeof(struct variable_list)); + snmp_set_var_objid( &varbind, name, 5 ); + varbind.type = ASN_OCTET_STR; + /* + * Test index structure linking: + * a) sorted by OID + */ + test_string_register( 20, "empty OID" ); + test_string_register( 10, "first OID" ); + test_string_register( 40, "last OID" ); + test_string_register( 30, "middle OID" ); + + /* + * b) sorted by index value + */ + test_string_register( 25, "eee: empty IDX" ); + test_string_register( 25, "aaa: first IDX" ); + test_string_register( 25, "zzz: last IDX" ); + test_string_register( 25, "mmm: middle IDX" ); + printf("This next one should fail....\n"); + test_string_register( 25, "eee: empty IDX" ); /* duplicate */ + printf("done\n"); + + /* + * c) test initial index linking + */ + test_string_register( 5, "eee: empty initial IDX" ); + test_string_register( 5, "aaa: replace initial IDX" ); + + /* + * Did it all work? + */ + dump_registry(); + unregister_index_by_session( main_session ); + /* + * Now test index allocation + * a) integer values + */ + test_int_register( 110, -1 ); /* empty */ + test_int_register( 110, -1 ); /* append */ + test_int_register( 110, 10 ); /* append exact */ + printf("This next one should fail....\n"); + test_int_register( 110, 10 ); /* exact duplicate */ + printf("done\n"); + test_int_register( 110, -1 ); /* append */ + test_int_register( 110, 5 ); /* insert exact */ + + /* + * b) string values + */ + test_string_register( 120, NULL ); /* empty */ + test_string_register( 120, NULL ); /* append */ + test_string_register( 120, "aaaz" ); + test_string_register( 120, NULL ); /* minor rollover */ + test_string_register( 120, "zzzz" ); + test_string_register( 120, NULL ); /* major rollover */ + + /* + * c) OID values + */ + + test_oid_register( 130, -1 ); /* empty */ + test_oid_register( 130, -1 ); /* append */ + + varbind.val_len = varbind.name_length*sizeof(oid); + memcpy( varbind.buf, varbind.name, varbind.val_len); + varbind.val.objid = (oid*) varbind.buf; + varbind.val_len += sizeof(oid); + + test_oid_register( 130, 255 ); /* append exact */ + test_oid_register( 130, -1 ); /* minor rollover */ + test_oid_register( 130, 100 ); /* insert exact */ + printf("This next one should fail....\n"); + test_oid_register( 130, 100 ); /* exact duplicate */ + printf("done\n"); + + varbind.val.objid = (oid*)varbind.buf; + for ( i=0; i<6; i++ ) + varbind.val.objid[i]=255; + varbind.val.objid[0]=1; + test_oid_register( 130, 255 ); /* set up rollover */ + test_oid_register( 130, -1 ); /* medium rollover */ + + for ( i=0; i<6; i++ ) + varbind.val.objid[i]=255; + varbind.val.objid[0]=2; + test_oid_register( 130, 255 ); /* set up rollover */ + test_oid_register( 130, -1 ); /* major rollover */ + + /* + * Did it all work? + */ + dump_registry(); + + /* + * Test the various "invalid" requests + * (unsupported types, mis-matched types, etc) + */ + printf("The rest of these should fail....\n"); + test_oid_register( 110, -1 ); + test_oid_register( 110, 100 ); + test_oid_register( 120, -1 ); + test_oid_register( 120, 100 ); + test_string_register( 110, NULL ); + test_string_register( 110, "aaaa" ); + test_string_register( 130, NULL ); + test_string_register( 130, "aaaa" ); + test_int_register( 120, -1 ); + test_int_register( 120, 1 ); + test_int_register( 130, -1 ); + test_int_register( 130, 1 ); + printf("done - this dump should be the same as before\n"); + dump_registry(); +} +#endif
