/* Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: */ /* * Portions of this file are copyrighted by: * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #if HAVE_STRING_H #include #endif #include #include #include netsnmp_feature_child_of(agent_handler, libnetsnmpagent) netsnmp_feature_child_of(handler_mark_requests_as_delegated, agent_handler) static netsnmp_mib_handler *_clone_handler(netsnmp_mib_handler *it); /***********************************************************************/ /* * New Handler based API */ /***********************************************************************/ /** @defgroup handler Net-SNMP Agent handler and extensibility API * @ingroup agent * * The basic theory goes something like this: In the past, with the * original mib module api (which derived from the original CMU SNMP * code) the underlying mib modules were passed very little * information (only the truly most basic information about a * request). This worked well at the time but in todays world of * subagents, device instrumentation, low resource consumption, etc, * it just isn't flexible enough. "handlers" are here to fix all that. * * With the rewrite of the agent internals for the net-snmp 5.0 * release, we introduce a modular calling scheme that allows agent * modules to be written in a very flexible manner, and more * importantly allows reuse of code in a decent way (and without the * memory and speed overheads of OO languages like C++). * * Functionally, the notion of what a handler does is the same as the * older api: A handler is @link netsnmp_create_handler() created@endlink and * then @link netsnmp_register_handler() registered@endlink with the main * agent at a given OID in the OID tree and gets called any time a * request is made that it should respond to. You probably should * use one of the convenience helpers instead of doing anything else * yourself though: * * Most importantly, though, is that the handlers are built on the * notion of modularity and reuse. Specifically, rather than do all * the really hard work (like parsing table indexes out of an * incoming oid request) in each module, the API is designed to make * it easy to write "helper" handlers that merely process some aspect * of the request before passing it along to the final handler that * returns the real answer. Most people will want to make use of the * @link instance instance@endlink, @link table table@endlink, @link * table_iterator table_iterator@endlink, @link table_data * table_data@endlink, or @link table_dataset table_dataset@endlink * helpers to make their life easier. These "helpers" interpert * important aspects of the request and pass them on to you. * * For instance, the @link table table@endlink helper is designed to * hand you a list of extracted index values from an incoming * request. THe @link table_iterator table_iterator@endlink helper * is built on top of the table helper, and is designed to help you * iterate through data stored elsewhere (like in a kernel) that is * not in OID lexographical order (ie, don't write your own index/oid * sorting routine, use this helper instead). The beauty of the * @link table_iterator table_iterator helper@endlink, as well as the @link * instance instance@endlink helper is that they take care of the complex * GETNEXT processing entirely for you and hand you everything you * need to merely return the data as if it was a GET request. Much * less code and hair pulling. I've pulled all my hair out to help * you so that only one of us has to be bald. * * @{ */ /** Creates a MIB handler structure. * The new structure is allocated and filled using the given name * and access method. * The returned handler should then be @link netsnmp_register_handler() * registered @endlink. * * @param name is the handler name and is copied then assigned to * netsnmp_mib_handler->handler_name * * @param handler_access_method is a function pointer used as the access * method for this handler registration instance for whatever required * needs. * * @return a pointer to a populated netsnmp_mib_handler struct to be * registered * * @see netsnmp_create_handler_registration() * @see netsnmp_register_handler() */ netsnmp_mib_handler * netsnmp_create_handler(const char *name, Netsnmp_Node_Handler * handler_access_method) { netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler); if (ret) { ret->access_method = handler_access_method; if (NULL != name) { ret->handler_name = strdup(name); if (NULL == ret->handler_name) SNMP_FREE(ret); } } return ret; } /** Creates a MIB handler structure. * The new structure is allocated and filled using the given name, * access function, registration location OID and list of modes that * the handler supports. If modes == 0, then modes will automatically * be set to the default value of only HANDLER_CAN_DEFAULT, which is * by default read-only GET and GETNEXT requests. A hander which supports * sets but not row creation should set us a mode of HANDLER_CAN_SET_ONLY. * @note This ends up calling netsnmp_create_handler(name, handler_access_method) * @param name is the handler name and is copied then assigned to * netsnmp_handler_registration->handlerName. * * @param handler is a function pointer used as the access * method for this handler registration instance for whatever required * needs. * * @param reg_oid is the registration location oid. * * @param reg_oid_len is the length of reg_oid; can use the macro, * OID_LENGTH * * @param modes is used to configure read/write access. If modes == 0, * then modes will automatically be set to the default * value of only HANDLER_CAN_DEFAULT, which is by default read-only GET * and GETNEXT requests. The other two mode options are read only, * HANDLER_CAN_RONLY, and read/write, HANDLER_CAN_RWRITE. * * - HANDLER_CAN_GETANDGETNEXT * - HANDLER_CAN_SET * - HANDLER_CAN_GETBULK * * - HANDLER_CAN_RONLY (HANDLER_CAN_GETANDGETNEXT) * - HANDLER_CAN_RWRITE (HANDLER_CAN_GETANDGETNEXT | * HANDLER_CAN_SET) * - HANDLER_CAN_DEFAULT HANDLER_CAN_RONLY * * @return Returns a pointer to a netsnmp_handler_registration struct. * NULL is returned only when memory could not be allocated for the * netsnmp_handler_registration struct. * * * @see netsnmp_create_handler() * @see netsnmp_register_handler() */ netsnmp_handler_registration * netsnmp_handler_registration_create(const char *name, netsnmp_mib_handler *handler, const oid * reg_oid, size_t reg_oid_len, int modes) { netsnmp_handler_registration *the_reg; the_reg = SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration); if (!the_reg) return NULL; if (modes) the_reg->modes = modes; else the_reg->modes = HANDLER_CAN_DEFAULT; the_reg->handler = handler; the_reg->priority = DEFAULT_MIB_PRIORITY; if (name) the_reg->handlerName = strdup(name); the_reg->rootoid = snmp_duplicate_objid(reg_oid, reg_oid_len); the_reg->rootoid_len = reg_oid_len; return the_reg; } /** Creates a handler registration structure with a new MIB handler. * This function first @link netsnmp_create_handler() creates @endlink * a MIB handler, then @link netsnmp_handler_registration_create() * makes registation structure @endlink for it. * * @param name is the handler name for netsnmp_create_handler() * * @param handler_access_method is a function pointer used as the access * method for netsnmp_create_handler() * * @param reg_oid is the registration location oid. * * @param reg_oid_len is the length of reg_oid; can use the macro, * OID_LENGTH * * @param modes is used to configure read/write access, as in * netsnmp_handler_registration_create() * * @return Returns a pointer to a netsnmp_handler_registration struct. * If the structures creation failed, NULL is returned. * * @see netsnmp_create_handler() * @see netsnmp_handler_registration_create() */ netsnmp_handler_registration * netsnmp_create_handler_registration(const char *name, Netsnmp_Node_Handler * handler_access_method, const oid * reg_oid, size_t reg_oid_len, int modes) { netsnmp_handler_registration *rv = NULL; netsnmp_mib_handler *handler = netsnmp_create_handler(name, handler_access_method); if (handler) { rv = netsnmp_handler_registration_create( name, handler, reg_oid, reg_oid_len, modes); if (!rv) netsnmp_handler_free(handler); } return rv; } /** Registers a MIB handler inside the registration structure. * Checks given registation handler for sanity, then * @link netsnmp_register_mib() performs registration @endlink * in the MIB tree, as defined by the netsnmp_handler_registration * pointer. On success, SNMP_CALLBACK_APPLICATION is called. * The registration struct may be created by call of * netsnmp_create_handler_registration(). * * @param reginfo Pointer to a netsnmp_handler_registration struct. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. * * @see netsnmp_create_handler_registration() * @see netsnmp_register_mib() */ int netsnmp_register_handler(netsnmp_handler_registration *reginfo) { netsnmp_mib_handler *handler; int flags = 0; if (reginfo == NULL) { snmp_log(LOG_ERR, "netsnmp_register_handler() called illegally\n"); netsnmp_assert(reginfo != NULL); return SNMP_ERR_GENERR; } DEBUGIF("handler::register") { DEBUGMSGTL(("handler::register", "Registering %s (", reginfo->handlerName)); for (handler = reginfo->handler; handler; handler = handler->next) { DEBUGMSG(("handler::register", "::%s", handler->handler_name)); } DEBUGMSG(("handler::register", ") at ")); if (reginfo->rootoid && reginfo->range_subid) { DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid, reginfo->rootoid_len, reginfo->range_subid, reginfo->range_ubound)); } else if (reginfo->rootoid) { DEBUGMSGOID(("handler::register", reginfo->rootoid, reginfo->rootoid_len)); } else { DEBUGMSG(("handler::register", "[null]")); } DEBUGMSG(("handler::register", "\n")); } /* * don't let them register for absolutely nothing. Probably a mistake */ if (0 == reginfo->modes) { reginfo->modes = HANDLER_CAN_DEFAULT; snmp_log(LOG_WARNING, "no registration modes specified for %s. " "Defaulting to 0x%x\n", reginfo->handlerName, reginfo->modes); } /* * for handlers that can't GETBULK, force a conversion handler on them */ if (!(reginfo->modes & HANDLER_CAN_GETBULK)) { handler = netsnmp_get_bulk_to_next_handler(); if (!handler || (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) { snmp_log(LOG_WARNING, "could not inject bulk to next handler\n"); if (handler) netsnmp_handler_free(handler); /** should this be a critical error? */ netsnmp_handler_registration_free(reginfo); return SNMP_ERR_GENERR; } } for (handler = reginfo->handler; handler; handler = handler->next) { if (handler->flags & MIB_HANDLER_INSTANCE) flags = FULLY_QUALIFIED_INSTANCE; } return netsnmp_register_mib(reginfo->handlerName, NULL, 0, 0, reginfo->rootoid, reginfo->rootoid_len, reginfo->priority, reginfo->range_subid, reginfo->range_ubound, NULL, reginfo->contextName, reginfo->timeout, flags, reginfo, 1); } /** Unregisters a MIB handler described inside the registration structure. * Removes a registration, performed earlier by * netsnmp_register_handler(), from the MIB tree. * Uses unregister_mib_context() to do the task. * * @param reginfo Pointer to a netsnmp_handler_registration struct. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. * * @see netsnmp_register_handler() * @see unregister_mib_context() */ int netsnmp_unregister_handler(netsnmp_handler_registration *reginfo) { if (!reginfo) return SNMPERR_SUCCESS; return unregister_mib_context(reginfo->rootoid, reginfo->rootoid_len, reginfo->priority, reginfo->range_subid, reginfo->range_ubound, reginfo->contextName); } /** Registers a MIB handler inside the registration structure. * Checks given registation handler for sanity, then * @link netsnmp_register_mib() performs registration @endlink * in the MIB tree, as defined by the netsnmp_handler_registration * pointer. Never calls SNMP_CALLBACK_APPLICATION. * The registration struct may be created by call of * netsnmp_create_handler_registration(). * * @param reginfo Pointer to a netsnmp_handler_registration struct. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. * * @see netsnmp_create_handler_registration() * @see netsnmp_register_mib() */ int netsnmp_register_handler_nocallback(netsnmp_handler_registration *reginfo) { netsnmp_mib_handler *handler; if (reginfo == NULL) { snmp_log(LOG_ERR, "netsnmp_register_handler_nocallback() called illegally\n"); netsnmp_assert(reginfo != NULL); return SNMP_ERR_GENERR; } DEBUGIF("handler::register") { DEBUGMSGTL(("handler::register", "Registering (with no callback) ")); for (handler = reginfo->handler; handler; handler = handler->next) { DEBUGMSG(("handler::register", "::%s", handler->handler_name)); } DEBUGMSG(("handler::register", " at ")); if (reginfo->rootoid && reginfo->range_subid) { DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid, reginfo->rootoid_len, reginfo->range_subid, reginfo->range_ubound)); } else if (reginfo->rootoid) { DEBUGMSGOID(("handler::register", reginfo->rootoid, reginfo->rootoid_len)); } else { DEBUGMSG(("handler::register", "[null]")); } DEBUGMSG(("handler::register", "\n")); } /* * don't let them register for absolutely nothing. Probably a mistake */ if (0 == reginfo->modes) { reginfo->modes = HANDLER_CAN_DEFAULT; } return netsnmp_register_mib(reginfo->handler->handler_name, NULL, 0, 0, reginfo->rootoid, reginfo->rootoid_len, reginfo->priority, reginfo->range_subid, reginfo->range_ubound, NULL, reginfo->contextName, reginfo->timeout, 0, reginfo, 0); } /** Injects handler into the calling chain of handlers. * The given MIB handler is inserted after the handler named before_what. * If before_what is NULL, the handler is put at the top of the list, * and hence will be the handler to be called first. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. * * @see netsnmp_create_handler_registration() * @see netsnmp_inject_handler() */ int netsnmp_inject_handler_before(netsnmp_handler_registration *reginfo, netsnmp_mib_handler *handler, const char *before_what) { netsnmp_mib_handler *handler2 = handler; if (handler == NULL || reginfo == NULL) { snmp_log(LOG_ERR, "netsnmp_inject_handler() called illegally\n"); netsnmp_assert(reginfo != NULL); netsnmp_assert(handler != NULL); return SNMP_ERR_GENERR; } while (handler2->next) { handler2 = handler2->next; /* Find the end of a handler sub-chain */ } if (reginfo->handler == NULL) { DEBUGMSGTL(("handler:inject", "injecting %s\n", handler->handler_name)); } else { DEBUGMSGTL(("handler:inject", "injecting %s before %s\n", handler->handler_name, reginfo->handler->handler_name)); } if (before_what) { netsnmp_mib_handler *nexth, *prevh = NULL; if (reginfo->handler == NULL) { snmp_log(LOG_ERR, "no handler to inject before\n"); return SNMP_ERR_GENERR; } for(nexth = reginfo->handler; nexth; prevh = nexth, nexth = nexth->next) { if (strcmp(nexth->handler_name, before_what) == 0) break; } if (!nexth) { snmp_log(LOG_ERR, "Cannot inject '%s' before '%s': not found\n", handler->handler_name, before_what); snmp_log(LOG_ERR, "The handlers are:\n"); for (nexth = reginfo->handler; nexth; nexth = nexth->next) snmp_log(LOG_ERR, " %s\n", nexth->handler_name); return SNMP_ERR_GENERR; } if (prevh) { /* after prevh and before nexth */ prevh->next = handler; handler2->next = nexth; handler->prev = prevh; nexth->prev = handler2; return SNMPERR_SUCCESS; } /* else we're first, which is what we do next anyway so fall through */ } handler2->next = reginfo->handler; if (reginfo->handler) reginfo->handler->prev = handler2; reginfo->handler = handler; return SNMPERR_SUCCESS; } /** Injects handler into the calling chain of handlers. * The given MIB handler is put at the top of the list, * and hence will be the handler to be called first. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. * * @see netsnmp_create_handler_registration() * @see netsnmp_inject_handler_before() */ int netsnmp_inject_handler(netsnmp_handler_registration *reginfo, netsnmp_mib_handler *handler) { return netsnmp_inject_handler_before(reginfo, handler, NULL); } /** Calls a MIB handlers chain, starting with specific handler. * The given arguments and MIB handler are checked * for sanity, then the handlers are called, one by one, * until next handler is NULL. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. */ NETSNMP_INLINE int netsnmp_call_handler(netsnmp_mib_handler *next_handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { Netsnmp_Node_Handler *nh; int ret; if (next_handler == NULL || reginfo == NULL || reqinfo == NULL || requests == NULL) { snmp_log(LOG_ERR, "netsnmp_call_handler() called illegally\n"); netsnmp_assert(next_handler != NULL); netsnmp_assert(reqinfo != NULL); netsnmp_assert(reginfo != NULL); netsnmp_assert(requests != NULL); return SNMP_ERR_GENERR; } do { nh = next_handler->access_method; if (!nh) { if (next_handler->next) { snmp_log(LOG_ERR, "no access method specified in handler %s.", next_handler->handler_name); return SNMP_ERR_GENERR; } /* * The final handler registration in the chain may well not need * to include a handler routine, if the processing of this object * is handled completely by the agent toolkit helpers. */ return SNMP_ERR_NOERROR; } DEBUGMSGTL(("handler:calling", "calling handler %s for mode %s\n", next_handler->handler_name, se_find_label_in_slist("agent_mode", reqinfo->mode))); /* * XXX: define acceptable return statuses */ ret = (*nh) (next_handler, reginfo, reqinfo, requests); DEBUGMSGTL(("handler:returned", "handler %s returned %d\n", next_handler->handler_name, ret)); if (! (next_handler->flags & MIB_HANDLER_AUTO_NEXT)) break; /* * did handler signal that it didn't want auto next this time around? */ if(next_handler->flags & MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE) { next_handler->flags &= ~MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; break; } next_handler = next_handler->next; } while(next_handler); return ret; } /** @private * Calls all the MIB Handlers in registration struct for a given mode. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. */ int netsnmp_call_handlers(netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_request_info *request; int status; if (reginfo == NULL || reqinfo == NULL || requests == NULL) { snmp_log(LOG_ERR, "netsnmp_call_handlers() called illegally\n"); netsnmp_assert(reqinfo != NULL); netsnmp_assert(reginfo != NULL); netsnmp_assert(requests != NULL); return SNMP_ERR_GENERR; } if (reginfo->handler == NULL) { snmp_log(LOG_ERR, "no handler specified."); return SNMP_ERR_GENERR; } switch (reqinfo->mode) { case MODE_GETBULK: case MODE_GET: case MODE_GETNEXT: if (!(reginfo->modes & HANDLER_CAN_GETANDGETNEXT)) return SNMP_ERR_NOERROR; /* legal */ break; #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: case MODE_SET_RESERVE2: case MODE_SET_ACTION: case MODE_SET_COMMIT: case MODE_SET_FREE: case MODE_SET_UNDO: if (!(reginfo->modes & HANDLER_CAN_SET)) { for (; requests; requests = requests->next) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_NOTWRITABLE); } return SNMP_ERR_NOERROR; } break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ default: snmp_log(LOG_ERR, "unknown mode in netsnmp_call_handlers! bug!\n"); return SNMP_ERR_GENERR; } DEBUGMSGTL(("handler:calling", "main handler %s\n", reginfo->handler->handler_name)); for (request = requests ; request; request = request->next) { request->processed = 0; } status = netsnmp_call_handler(reginfo->handler, reginfo, reqinfo, requests); return status; } /** @private * Calls the next MIB handler in the chain, after the current one. * The given arguments and MIB handler are checked * for sanity, then the next handler is called. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. */ NETSNMP_INLINE int netsnmp_call_next_handler(netsnmp_mib_handler *current, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { if (current == NULL || reginfo == NULL || reqinfo == NULL || requests == NULL) { snmp_log(LOG_ERR, "netsnmp_call_next_handler() called illegally\n"); netsnmp_assert(current != NULL); netsnmp_assert(reginfo != NULL); netsnmp_assert(reqinfo != NULL); netsnmp_assert(requests != NULL); return SNMP_ERR_GENERR; } return netsnmp_call_handler(current->next, reginfo, reqinfo, requests); } /** @private * Calls the next MIB handler in the chain, after the current one. * The given arguments and MIB handler are not validated before * the call, only request is checked. * * @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code. */ netsnmp_feature_child_of(netsnmp_call_next_handler_one_request,netsnmp_unused) #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST NETSNMP_INLINE int netsnmp_call_next_handler_one_request(netsnmp_mib_handler *current, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_request_info *request; int ret; if (!requests) { snmp_log(LOG_ERR, "netsnmp_call_next_handler_ONE_REQUEST() called illegally\n"); netsnmp_assert(requests != NULL); return SNMP_ERR_GENERR; } request = requests->next; requests->next = NULL; ret = netsnmp_call_handler(current->next, reginfo, reqinfo, requests); requests->next = request; return ret; } #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST */ /** Deallocates resources associated with a given handler. * The handler is removed from chain and then freed. * After calling this function, the handler pointer is invalid * and should be set to NULL. * * @param handler is the MIB Handler to be freed */ void netsnmp_handler_free(netsnmp_mib_handler *handler) { if (handler != NULL) { if (handler->next != NULL) { /** make sure we aren't pointing to ourselves. */ netsnmp_assert(handler != handler->next); /* bugs caught: 1 */ netsnmp_handler_free(handler->next); handler->next = NULL; } if ((handler->myvoid != NULL) && (handler->data_free != NULL)) { handler->data_free(handler->myvoid); } SNMP_FREE(handler->handler_name); SNMP_FREE(handler); } } /** Duplicates a MIB handler and all subsequent handlers. * Creates a copy of all data in given handlers chain. * * @param handler is the MIB Handler to be duplicated * * @return Returns a pointer to the complete copy, * or NULL if any problem occured. * * @see _clone_handler() */ netsnmp_mib_handler * netsnmp_handler_dup(netsnmp_mib_handler *handler) { netsnmp_mib_handler *h = NULL; if (!handler) goto err; h = _clone_handler(handler); if (!h) goto err; /* * Providing a clone function without a free function is asking for * memory leaks, and providing a free function without clone function * is asking for memory corruption. Hence the log statement below. */ if (!!handler->data_clone != !!handler->data_free) snmp_log(LOG_ERR, "data_clone / data_free inconsistent (%s)\n", handler->handler_name); if (handler->myvoid && handler->data_clone) { h->myvoid = handler->data_clone(handler->myvoid); if (!h->myvoid) goto err; } else h->myvoid = handler->myvoid; h->data_clone = handler->data_clone; h->data_free = handler->data_free; if (handler->next != NULL) { h->next = netsnmp_handler_dup(handler->next); if (!h->next) goto err; h->next->prev = h; } h->prev = NULL; return h; err: netsnmp_handler_free(h); return NULL; } /** Free resources associated with a handler registration object. * The registration object and all MIB Handlers in the chain are * freed. When the function ends, given pointer is no longer valid * and should be set to NULL. * * @param reginfo is the handler registration object to be freed */ void netsnmp_handler_registration_free(netsnmp_handler_registration *reginfo) { if (reginfo != NULL) { netsnmp_handler_free(reginfo->handler); SNMP_FREE(reginfo->handlerName); SNMP_FREE(reginfo->contextName); SNMP_FREE(reginfo->rootoid); reginfo->rootoid_len = 0; SNMP_FREE(reginfo); } } /** Duplicates handler registration object and all subsequent handlers. * Creates a copy of the handler registration object and all its data. * * @param reginfo is the handler registration object to be duplicated * * @return Returns a pointer to the complete copy, * or NULL if any problem occured. * * @see netsnmp_handler_dup() */ netsnmp_handler_registration * netsnmp_handler_registration_dup(netsnmp_handler_registration *reginfo) { netsnmp_handler_registration *r = NULL; if (reginfo == NULL) { return NULL; } r = (netsnmp_handler_registration *) calloc(1, sizeof (netsnmp_handler_registration)); if (r != NULL) { r->modes = reginfo->modes; r->priority = reginfo->priority; r->range_subid = reginfo->range_subid; r->timeout = reginfo->timeout; r->range_ubound = reginfo->range_ubound; r->rootoid_len = reginfo->rootoid_len; if (reginfo->handlerName != NULL) { r->handlerName = strdup(reginfo->handlerName); if (r->handlerName == NULL) { netsnmp_handler_registration_free(r); return NULL; } } if (reginfo->contextName != NULL) { r->contextName = strdup(reginfo->contextName); if (r->contextName == NULL) { netsnmp_handler_registration_free(r); return NULL; } } if (reginfo->rootoid != NULL) { r->rootoid = snmp_duplicate_objid(reginfo->rootoid, reginfo->rootoid_len); if (r->rootoid == NULL) { netsnmp_handler_registration_free(r); return NULL; } } r->handler = netsnmp_handler_dup(reginfo->handler); if (r->handler == NULL) { netsnmp_handler_registration_free(r); return NULL; } return r; } return NULL; } /** Creates a cache of information which can be saved for future reference. * The cache is filled with pointers from parameters. Note that * the input structures are not duplicated, but put directly into * the new cache struct. * Use netsnmp_handler_check_cache() later to make sure it's still * valid before referencing it in the future. * * @see netsnmp_handler_check_cache() * @see netsnmp_free_delegated_cache() */ NETSNMP_INLINE netsnmp_delegated_cache * netsnmp_create_delegated_cache(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, void *localinfo) { netsnmp_delegated_cache *ret; ret = SNMP_MALLOC_TYPEDEF(netsnmp_delegated_cache); if (ret) { ret->transaction_id = reqinfo->asp->pdu->transid; ret->handler = handler; ret->reginfo = reginfo; ret->reqinfo = reqinfo; ret->requests = requests; ret->localinfo = localinfo; } return ret; } /** Check if a given delegated cache is still valid. * The cache is valid if it's a part of transaction * (ie, the agent still considers it to be an outstanding request). * * @param dcache is the delegated cache to be checked. * * @return Returns the cache pointer if it is still valid, NULL otherwise. * * @see netsnmp_create_delegated_cache() * @see netsnmp_free_delegated_cache() */ NETSNMP_INLINE netsnmp_delegated_cache * netsnmp_handler_check_cache(netsnmp_delegated_cache *dcache) { if (!dcache) return dcache; if (netsnmp_check_transaction_id(dcache->transaction_id) == SNMPERR_SUCCESS) return dcache; return NULL; } /** Free a cache once it's no longer used. * Deletes the data allocated by netsnmp_create_delegated_cache(). * Structures which were not allocated by netsnmp_create_delegated_cache() * are not freed (pointers are dropped). * * @param dcache is the delegated cache to be freed. * * @see netsnmp_create_delegated_cache() * @see netsnmp_handler_check_cache() */ NETSNMP_INLINE void netsnmp_free_delegated_cache(netsnmp_delegated_cache *dcache) { /* * right now, no extra data is there that needs to be freed */ if (dcache) SNMP_FREE(dcache); return; } #ifndef NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED /** Sets a list of requests as delegated or not delegated. * Sweeps through given chain of requests and sets 'delegated' * flag accordingly to the isdelegaded parameter. * * @param requests Request list. * @param isdelegated New value of the 'delegated' flag. */ void netsnmp_handler_mark_requests_as_delegated(netsnmp_request_info *requests, int isdelegated) { while (requests) { requests->delegated = isdelegated; requests = requests->next; } } #endif /* NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED */ /** Adds data from node list to the request information structure. * Data in the request can be later extracted and used by submodules. * * @param request Destination request information structure. * * @param node The data to be added to the linked list * request->parent_data. * * @see netsnmp_request_remove_list_data() * @see netsnmp_request_get_list_data() */ NETSNMP_INLINE void netsnmp_request_add_list_data(netsnmp_request_info *request, netsnmp_data_list *node) { if (request) { if (request->parent_data) netsnmp_add_list_data(&request->parent_data, node); else request->parent_data = node; } } /** Removes all data from a request. * * @param request the netsnmp request info structure * * @param name this is the name of the previously added data * * @return 0 on successful find-and-delete, 1 otherwise. * * @see netsnmp_request_add_list_data() * @see netsnmp_request_get_list_data() */ NETSNMP_INLINE int netsnmp_request_remove_list_data(netsnmp_request_info *request, const char *name) { if ((NULL == request) || (NULL ==request->parent_data)) return 1; return netsnmp_remove_list_node(&request->parent_data, name); } /** Extracts data from a request. * Retrieves data that was previously added to the request, * usually by a parent module. * * @param request Source request information structure. * * @param name Used to compare against the request->parent_data->name value; * if a match is found, request->parent_data->data is returned. * * @return Gives a void pointer(request->parent_data->data); NULL is * returned if source data is NULL or the object is not found. * * @see netsnmp_request_add_list_data() * @see netsnmp_request_remove_list_data() */ void * netsnmp_request_get_list_data(netsnmp_request_info *request, const char *name) { if (request) return netsnmp_get_list_data(request->parent_data, name); return NULL; } /** Free the extra data stored in a request. * Deletes the data in given request only. Other chain items * are unaffected. * * @param request Request information structure to be modified. * * @see netsnmp_request_add_list_data() * @see netsnmp_free_list_data() */ NETSNMP_INLINE void netsnmp_free_request_data_set(netsnmp_request_info *request) { if (request) netsnmp_free_list_data(request->parent_data); } /** Free the extra data stored in a bunch of requests. * Deletes all data in the chain inside request. * * @param request Request information structure to be modified. * * @see netsnmp_request_add_list_data() * @see netsnmp_free_request_data_set() */ NETSNMP_INLINE void netsnmp_free_request_data_sets(netsnmp_request_info *request) { if (request && request->parent_data) { netsnmp_free_all_list_data(request->parent_data); request->parent_data = NULL; } } /** Returns a MIB handler from a chain based on the name. * * @param reginfo Handler registration struct which contains the chain. * * @param name Target MIB Handler name string. The name is case * sensitive. * * @return The MIB Handler is returned, or NULL if not found. * * @see netsnmp_request_add_list_data() */ netsnmp_mib_handler * netsnmp_find_handler_by_name(netsnmp_handler_registration *reginfo, const char *name) { netsnmp_mib_handler *it; if (reginfo == NULL || name == NULL ) return NULL; for (it = reginfo->handler; it; it = it->next) { if (strcmp(it->handler_name, name) == 0) { return it; } } return NULL; } /** Returns a handler's void pointer from a chain based on the name. * * @warning The void pointer data may change as a handler evolves. * Handlers should really advertise some function for you * to use instead. * * @param reginfo Handler registration struct which contains the chain. * * @param name Target MIB Handler name string. The name is case * sensitive. * * @return The MIB Handler's void * pointer is returned, * or NULL if the handler is not found. * * @see netsnmp_find_handler_by_name() */ void * netsnmp_find_handler_data_by_name(netsnmp_handler_registration *reginfo, const char *name) { netsnmp_mib_handler *it = netsnmp_find_handler_by_name(reginfo, name); if (it) return it->myvoid; return NULL; } /** @private * Clones a MIB Handler with its basic properties. * Creates a copy of the given MIB Handler. Copies name, flags and * access methods only; not myvoid. * * @see netsnmp_handler_dup() */ static netsnmp_mib_handler * _clone_handler(netsnmp_mib_handler *it) { netsnmp_mib_handler *dup; if(NULL == it) return NULL; dup = netsnmp_create_handler(it->handler_name, it->access_method); if(NULL != dup) dup->flags = it->flags; return dup; } static netsnmp_data_list *handler_reg = NULL; void handler_free_callback(void *handler) { netsnmp_handler_free((netsnmp_mib_handler *)handler); } /** Registers a given handler by name, so that it can be found easily later. * Pointer to the handler is put into a list where it can be easily located * at any time. * * @param name Name string to be associated with the handler. * * @param handler Pointer the MIB Handler. * * @see netsnmp_clear_handler_list() */ void netsnmp_register_handler_by_name(const char *name, netsnmp_mib_handler *handler) { netsnmp_add_list_data(&handler_reg, netsnmp_create_data_list(name, (void *) handler, handler_free_callback)); DEBUGMSGTL(("handler_registry", "registering helper %s\n", name)); } /** Clears the entire MIB Handlers registration list. * MIB Handlers registration list is used to access any MIB Handler by * its name. The function frees the list memory and sets pointer to NULL. * Instead of calling this function directly, use shutdown_agent(). * * @see shutdown_agent() * @see netsnmp_register_handler_by_name() */ void netsnmp_clear_handler_list(void) { DEBUGMSGTL(("agent_handler", "netsnmp_clear_handler_list() called\n")); netsnmp_free_all_list_data(handler_reg); handler_reg = NULL; } /** @private * Injects a handler into a subtree, peers and children when a given * subtrees name matches a passed in name. */ void netsnmp_inject_handler_into_subtree(netsnmp_subtree *tp, const char *name, netsnmp_mib_handler *handler, const char *before_what) { netsnmp_subtree *tptr; netsnmp_mib_handler *mh; for (tptr = tp; tptr != NULL; tptr = tptr->next) { /* if (tptr->children) { netsnmp_inject_handler_into_subtree(tptr->children,name,handler); } */ if (strcmp(tptr->label_a, name) == 0) { DEBUGMSGTL(("injectHandler", "injecting handler %s into %s\n", handler->handler_name, tptr->label_a)); netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler), before_what); } else if (tptr->reginfo != NULL && tptr->reginfo->handlerName != NULL && strcmp(tptr->reginfo->handlerName, name) == 0) { DEBUGMSGTL(("injectHandler", "injecting handler into %s/%s\n", tptr->label_a, tptr->reginfo->handlerName)); netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler), before_what); } else if (tptr->reginfo != NULL) { for (mh = tptr->reginfo->handler; mh != NULL; mh = mh->next) { if (mh->handler_name && strcmp(mh->handler_name, name) == 0) { DEBUGMSGTL(("injectHandler", "injecting handler into %s\n", tptr->label_a)); netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler), before_what); break; } else { DEBUGMSGTL(("injectHandler", "not injecting handler into %s\n", mh->handler_name)); } } } } } static int doneit = 0; /** @private * Parses the "injectHandler" token line. */ void parse_injectHandler_conf(const char *token, char *cptr) { char handler_to_insert[256], reg_name[256]; subtree_context_cache *stc; netsnmp_mib_handler *handler; /* * XXXWWW: ensure instead that handler isn't inserted twice */ if (doneit) /* we only do this once without restart the agent */ return; cptr = copy_nword(cptr, handler_to_insert, sizeof(handler_to_insert)); handler = (netsnmp_mib_handler*)netsnmp_get_list_data(handler_reg, handler_to_insert); if (!handler) { netsnmp_config_error("no \"%s\" handler registered.", handler_to_insert); return; } if (!cptr) { config_perror("no INTONAME specified. Can't do insertion."); return; } cptr = copy_nword(cptr, reg_name, sizeof(reg_name)); for (stc = get_top_context_cache(); stc; stc = stc->next) { DEBUGMSGTL(("injectHandler", "Checking context tree %s (before=%s)\n", stc->context_name, (cptr)?cptr:"null")); netsnmp_inject_handler_into_subtree(stc->first_subtree, reg_name, handler, cptr); } } /** @private * Callback to ensure injectHandler parser doesn't do things twice. * @todo replace this with a method to check the handler chain instead. */ static int handler_mark_inject_handler_done(int majorID, int minorID, void *serverarg, void *clientarg) { doneit = 1; return 0; } /** @private * Registers the injectHandle parser token. * Used in init_agent_read_config(). * * @see init_agent_read_config() */ void netsnmp_init_handler_conf(void) { snmpd_register_config_handler("injectHandler", parse_injectHandler_conf, NULL, "injectHandler NAME INTONAME [BEFORE_OTHER_NAME]"); snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_READ_CONFIG, handler_mark_inject_handler_done, NULL); se_add_pair_to_slist("agent_mode", strdup("GET"), MODE_GET); se_add_pair_to_slist("agent_mode", strdup("GETNEXT"), MODE_GETNEXT); se_add_pair_to_slist("agent_mode", strdup("GETBULK"), MODE_GETBULK); #ifndef NETSNMP_NO_WRITE_SUPPORT se_add_pair_to_slist("agent_mode", strdup("SET_BEGIN"), MODE_SET_BEGIN); se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE1"), MODE_SET_RESERVE1); se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE2"), MODE_SET_RESERVE2); se_add_pair_to_slist("agent_mode", strdup("SET_ACTION"), MODE_SET_ACTION); se_add_pair_to_slist("agent_mode", strdup("SET_COMMIT"), MODE_SET_COMMIT); se_add_pair_to_slist("agent_mode", strdup("SET_FREE"), MODE_SET_FREE); se_add_pair_to_slist("agent_mode", strdup("SET_UNDO"), MODE_SET_UNDO); se_add_pair_to_slist("babystep_mode", strdup("pre-request"), MODE_BSTEP_PRE_REQUEST); se_add_pair_to_slist("babystep_mode", strdup("object_lookup"), MODE_BSTEP_OBJECT_LOOKUP); se_add_pair_to_slist("babystep_mode", strdup("check_value"), MODE_BSTEP_CHECK_VALUE); se_add_pair_to_slist("babystep_mode", strdup("row_create"), MODE_BSTEP_ROW_CREATE); se_add_pair_to_slist("babystep_mode", strdup("undo_setup"), MODE_BSTEP_UNDO_SETUP); se_add_pair_to_slist("babystep_mode", strdup("set_value"), MODE_BSTEP_SET_VALUE); se_add_pair_to_slist("babystep_mode", strdup("check_consistency"), MODE_BSTEP_CHECK_CONSISTENCY); se_add_pair_to_slist("babystep_mode", strdup("undo_set"), MODE_BSTEP_UNDO_SET); se_add_pair_to_slist("babystep_mode", strdup("commit"), MODE_BSTEP_COMMIT); se_add_pair_to_slist("babystep_mode", strdup("undo_commit"), MODE_BSTEP_UNDO_COMMIT); se_add_pair_to_slist("babystep_mode", strdup("irreversible_commit"), MODE_BSTEP_IRREVERSIBLE_COMMIT); se_add_pair_to_slist("babystep_mode", strdup("undo_cleanup"), MODE_BSTEP_UNDO_CLEANUP); se_add_pair_to_slist("babystep_mode", strdup("post_request"), MODE_BSTEP_POST_REQUEST); se_add_pair_to_slist("babystep_mode", strdup("original"), 0xffff); #endif /* NETSNMP_NO_WRITE_SUPPORT */ /* * xxx-rks: hmmm.. will this work for modes which are or'd together? * I'm betting not... */ se_add_pair_to_slist("handler_can_mode", strdup("GET/GETNEXT"), HANDLER_CAN_GETANDGETNEXT); se_add_pair_to_slist("handler_can_mode", strdup("SET"), HANDLER_CAN_SET); se_add_pair_to_slist("handler_can_mode", strdup("GETBULK"), HANDLER_CAN_GETBULK); se_add_pair_to_slist("handler_can_mode", strdup("BABY_STEP"), HANDLER_CAN_BABY_STEP); } /** @} */