1 /* Portions of this file are subject to the following copyright(s).  See
2  * the Net-SNMP's COPYING file for more details and other copyrights
3  * that may apply:
4  */
5 /*
6  * Portions of this file are copyrighted by:
7  * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
8  * Use is subject to license terms specified in the COPYING file
9  * distributed with the Net-SNMP package.
10  *
11  * Portions of this file are copyrighted by:
12  * Copyright (c) 2016 VMware, Inc. All rights reserved.
13  * Use is subject to license terms specified in the COPYING file
14  * distributed with the Net-SNMP package.
15  */
16 #include <net-snmp/net-snmp-config.h>
17 #include <net-snmp/net-snmp-features.h>
18 
19 #include <sys/types.h>
20 
21 #if HAVE_STRING_H
22 #include <string.h>
23 #endif
24 
25 #include <net-snmp/net-snmp-includes.h>
26 #include <net-snmp/agent/net-snmp-agent-includes.h>
27 
28 #include <net-snmp/agent/bulk_to_next.h>
29 
30 netsnmp_feature_child_of(agent_handler, libnetsnmpagent);
31 
32 netsnmp_feature_child_of(handler_mark_requests_as_delegated, agent_handler);
33 
34 static netsnmp_mib_handler *_clone_handler(netsnmp_mib_handler *it);
35 
36 /***********************************************************************/
37 /*
38  * New Handler based API
39  */
40 /***********************************************************************/
41 /** @defgroup handler Net-SNMP Agent handler and extensibility API
42  *  @ingroup agent
43  *
44  *  The basic theory goes something like this: In the past, with the
45  *  original mib module api (which derived from the original CMU SNMP
46  *  code) the underlying mib modules were passed very little
47  *  information (only the truly most basic information about a
48  *  request).  This worked well at the time but in todays world of
49  *  subagents, device instrumentation, low resource consumption, etc,
50  *  it just isn't flexible enough.  "handlers" are here to fix all that.
51  *
52  *  With the rewrite of the agent internals for the net-snmp 5.0
53  *  release, we introduce a modular calling scheme that allows agent
54  *  modules to be written in a very flexible manner, and more
55  *  importantly allows reuse of code in a decent way (and without the
56  *  memory and speed overheads of OO languages like C++).
57  *
58  *  Functionally, the notion of what a handler does is the same as the
59  *  older api: A handler is @link netsnmp_create_handler() created@endlink and
60  *  then @link netsnmp_register_handler() registered@endlink with the main
61  *  agent at a given OID in the OID tree and gets called any time a
62  *  request is made that it should respond to.  You probably should
63  *  use one of the convenience helpers instead of doing anything else
64  *  yourself though:
65  *
66  *  Most importantly, though, is that the handlers are built on the
67  *  notion of modularity and reuse.  Specifically, rather than do all
68  *  the really hard work (like parsing table indexes out of an
69  *  incoming oid request) in each module, the API is designed to make
70  *  it easy to write "helper" handlers that merely process some aspect
71  *  of the request before passing it along to the final handler that
72  *  returns the real answer.  Most people will want to make use of the
73  *  @link instance instance@endlink, @link table table@endlink, @link
74  *  table_iterator table_iterator@endlink, @link table_data
75  *  table_data@endlink, or @link table_dataset table_dataset@endlink
76  *  helpers to make their life easier.  These "helpers" interpert
77  *  important aspects of the request and pass them on to you.
78  *
79  *  For instance, the @link table table@endlink helper is designed to
80  *  hand you a list of extracted index values from an incoming
81  *  request.  THe @link table_iterator table_iterator@endlink helper
82  *  is built on top of the table helper, and is designed to help you
83  *  iterate through data stored elsewhere (like in a kernel) that is
84  *  not in OID lexographical order (ie, don't write your own index/oid
85  *  sorting routine, use this helper instead).  The beauty of the
86  *  @link table_iterator table_iterator helper@endlink, as well as the @link
87  *  instance instance@endlink helper is that they take care of the complex
88  *  GETNEXT processing entirely for you and hand you everything you
89  *  need to merely return the data as if it was a GET request.  Much
90  *  less code and hair pulling.  I've pulled all my hair out to help
91  *  you so that only one of us has to be bald.
92  *
93  * @{
94  */
95 
96 /** Creates a MIB handler structure.
97  *  The new structure is allocated and filled using the given name
98  *  and access method.
99  *  The returned handler should then be @link netsnmp_register_handler()
100  *  registered @endlink.
101  *
102  *  @param name is the handler name and is copied then assigned to
103  *              netsnmp_mib_handler->handler_name
104  *
105  *  @param handler_access_method is a function pointer used as the access
106  *	   method for this handler registration instance for whatever required
107  *         needs.
108  *
109  *  @return a pointer to a populated netsnmp_mib_handler struct to be
110  *          registered
111  *
112  *  @see netsnmp_create_handler_registration()
113  *  @see netsnmp_register_handler()
114  */
115 netsnmp_mib_handler *
netsnmp_create_handler(const char * name,Netsnmp_Node_Handler * handler_access_method)116 netsnmp_create_handler(const char *name,
117                        Netsnmp_Node_Handler * handler_access_method)
118 {
119     netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler);
120     if (ret) {
121         ret->access_method = handler_access_method;
122         if (NULL != name) {
123             ret->handler_name = strdup(name);
124             if (NULL == ret->handler_name)
125                 SNMP_FREE(ret);
126         }
127     }
128     return ret;
129 }
130 
131 /** Creates a MIB handler structure.
132  *  The new structure is allocated and filled using the given name,
133  *  access function, registration location OID and list of modes that
134  *  the handler supports. If modes == 0, then modes will automatically
135  *  be set to the default value of only HANDLER_CAN_DEFAULT, which is
136  *  by default read-only GET and GETNEXT requests. A hander which supports
137  *  sets but not row creation should set us a mode of HANDLER_CAN_SET_ONLY.
138  *  @note This ends up calling netsnmp_create_handler(name, handler_access_method)
139  *  @param name is the handler name and is copied then assigned to
140  *              netsnmp_handler_registration->handlerName.
141  *
142  *  @param handler is a function pointer used as the access
143  *	method for this handler registration instance for whatever required
144  *	needs.
145  *
146  *  @param reg_oid is the registration location oid.
147  *
148  *  @param reg_oid_len is the length of reg_oid; can use the macro,
149  *         OID_LENGTH
150  *
151  *  @param modes is used to configure read/write access.  If modes == 0,
152  *	then modes will automatically be set to the default
153  *	value of only HANDLER_CAN_DEFAULT, which is by default read-only GET
154  *	and GETNEXT requests.  The other two mode options are read only,
155  *	HANDLER_CAN_RONLY, and read/write, HANDLER_CAN_RWRITE.
156  *
157  *		- HANDLER_CAN_GETANDGETNEXT
158  *		- HANDLER_CAN_SET
159  *		- HANDLER_CAN_GETBULK
160  *
161  *		- HANDLER_CAN_RONLY   (HANDLER_CAN_GETANDGETNEXT)
162  *		- HANDLER_CAN_RWRITE  (HANDLER_CAN_GETANDGETNEXT |
163  *			HANDLER_CAN_SET)
164  *		- HANDLER_CAN_DEFAULT HANDLER_CAN_RONLY
165  *
166  *  @return Returns a pointer to a netsnmp_handler_registration struct.
167  *          NULL is returned only when memory could not be allocated for the
168  *          netsnmp_handler_registration struct.
169  *
170  *
171  *  @see netsnmp_create_handler()
172  *  @see netsnmp_register_handler()
173  */
174 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)175 netsnmp_handler_registration_create(const char *name,
176                                     netsnmp_mib_handler *handler,
177                                     const oid * reg_oid, size_t reg_oid_len,
178                                     int modes)
179 {
180     netsnmp_handler_registration *the_reg;
181     the_reg = SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration);
182     if (!the_reg)
183         return NULL;
184 
185     if (modes)
186         the_reg->modes = modes;
187     else
188         the_reg->modes = HANDLER_CAN_DEFAULT;
189 
190     the_reg->handler = handler;
191     the_reg->priority = DEFAULT_MIB_PRIORITY;
192     if (name)
193         the_reg->handlerName = strdup(name);
194     the_reg->rootoid = snmp_duplicate_objid(reg_oid, reg_oid_len);
195     the_reg->rootoid_len = reg_oid_len;
196     return the_reg;
197 }
198 
199 /** Creates a handler registration structure with a new MIB handler.
200  *  This function first @link netsnmp_create_handler() creates @endlink
201  *  a MIB handler, then @link netsnmp_handler_registration_create()
202  *  makes registation structure @endlink for it.
203  *
204  *  @param name is the handler name for netsnmp_create_handler()
205  *
206  *  @param handler_access_method is a function pointer used as the access
207  *     method for netsnmp_create_handler()
208  *
209  *  @param reg_oid is the registration location oid.
210  *
211  *  @param reg_oid_len is the length of reg_oid; can use the macro,
212  *         OID_LENGTH
213  *
214  *  @param modes is used to configure read/write access, as in
215  *         netsnmp_handler_registration_create()
216  *
217  *  @return Returns a pointer to a netsnmp_handler_registration struct.
218  *          If the structures creation failed, NULL is returned.
219  *
220  *  @see netsnmp_create_handler()
221  *  @see netsnmp_handler_registration_create()
222  */
223 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)224 netsnmp_create_handler_registration(const char *name,
225                                     Netsnmp_Node_Handler *
226                                     handler_access_method, const oid * reg_oid,
227                                     size_t reg_oid_len, int modes)
228 {
229     netsnmp_handler_registration *rv = NULL;
230     netsnmp_mib_handler *handler =
231         netsnmp_create_handler(name, handler_access_method);
232     if (handler) {
233         rv = netsnmp_handler_registration_create(
234             name, handler, reg_oid, reg_oid_len, modes);
235         if (!rv)
236             netsnmp_handler_free(handler);
237     }
238     return rv;
239 }
240 
241 /** Registers a MIB handler inside the registration structure.
242  *  Checks given registation handler for sanity, then
243  *  @link netsnmp_register_mib() performs registration @endlink
244  *  in the MIB tree, as defined by the netsnmp_handler_registration
245  *  pointer. On success, SNMP_CALLBACK_APPLICATION is called.
246  *  The registration struct may be created by call of
247  *  netsnmp_create_handler_registration().
248  *
249  *  @param reginfo Pointer to a netsnmp_handler_registration struct.
250  *
251  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
252  *
253  *  @see netsnmp_create_handler_registration()
254  *  @see netsnmp_register_mib()
255  */
256 int
netsnmp_register_handler(netsnmp_handler_registration * reginfo)257 netsnmp_register_handler(netsnmp_handler_registration *reginfo)
258 {
259     netsnmp_mib_handler *handler;
260     int flags = 0;
261 
262     if (reginfo == NULL) {
263         snmp_log(LOG_ERR, "netsnmp_register_handler() called illegally\n");
264         netsnmp_assert(reginfo != NULL);
265         return SNMP_ERR_GENERR;
266     }
267 
268     DEBUGIF("handler::register") {
269         DEBUGMSGTL(("handler::register", "Registering %s (", reginfo->handlerName));
270         for (handler = reginfo->handler; handler; handler = handler->next) {
271             DEBUGMSG(("handler::register", "::%s", handler->handler_name));
272         }
273 
274         DEBUGMSG(("handler::register", ") at "));
275         if (reginfo->rootoid && reginfo->range_subid) {
276             DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid,
277                               reginfo->rootoid_len, reginfo->range_subid,
278                               reginfo->range_ubound));
279         } else if (reginfo->rootoid) {
280             DEBUGMSGOID(("handler::register", reginfo->rootoid,
281                          reginfo->rootoid_len));
282         } else {
283             DEBUGMSG(("handler::register", "[null]"));
284         }
285         DEBUGMSG(("handler::register", "\n"));
286     }
287 
288     /*
289      * don't let them register for absolutely nothing.  Probably a mistake
290      */
291     if (0 == reginfo->modes) {
292         reginfo->modes = HANDLER_CAN_DEFAULT;
293         snmp_log(LOG_WARNING, "no registration modes specified for %s. "
294                  "Defaulting to 0x%x\n", reginfo->handlerName, reginfo->modes);
295     }
296 
297     /*
298      * for handlers that can't GETBULK, force a conversion handler on them
299      */
300     if (!(reginfo->modes & HANDLER_CAN_GETBULK)) {
301         handler = netsnmp_get_bulk_to_next_handler();
302         if (!handler ||
303             (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
304             snmp_log(LOG_WARNING, "could not inject bulk to next handler\n");
305             if (handler)
306                 netsnmp_handler_free(handler);
307             /** should this be a critical error? */
308             netsnmp_handler_registration_free(reginfo);
309             return SNMP_ERR_GENERR;
310         }
311     }
312 
313     for (handler = reginfo->handler; handler; handler = handler->next) {
314         if (handler->flags & MIB_HANDLER_INSTANCE)
315             flags = FULLY_QUALIFIED_INSTANCE;
316     }
317 
318     return netsnmp_register_mib(reginfo->handlerName,
319                                 NULL, 0, 0,
320                                 reginfo->rootoid, reginfo->rootoid_len,
321                                 reginfo->priority,
322                                 reginfo->range_subid,
323                                 reginfo->range_ubound, NULL,
324                                 reginfo->contextName, reginfo->timeout, flags,
325                                 reginfo, 1);
326 }
327 
328 /** Unregisters a MIB handler described inside the registration structure.
329  *  Removes a registration, performed earlier by
330  *  netsnmp_register_handler(), from the MIB tree.
331  *  Uses unregister_mib_context() to do the task.
332  *
333  *  @param reginfo Pointer to a netsnmp_handler_registration struct.
334  *
335  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
336  *
337  *  @see netsnmp_register_handler()
338  *  @see unregister_mib_context()
339  */
340 int
netsnmp_unregister_handler(netsnmp_handler_registration * reginfo)341 netsnmp_unregister_handler(netsnmp_handler_registration *reginfo)
342 {
343     if (!reginfo)
344         return SNMPERR_SUCCESS;
345     return unregister_mib_context(reginfo->rootoid, reginfo->rootoid_len,
346                                   reginfo->priority,
347                                   reginfo->range_subid, reginfo->range_ubound,
348                                   reginfo->contextName);
349 }
350 
351 /** Registers a MIB handler inside the registration structure.
352  *  Checks given registation handler for sanity, then
353  *  @link netsnmp_register_mib() performs registration @endlink
354  *  in the MIB tree, as defined by the netsnmp_handler_registration
355  *  pointer. Never calls SNMP_CALLBACK_APPLICATION.
356  *  The registration struct may be created by call of
357  *  netsnmp_create_handler_registration().
358  *
359  *  @param reginfo Pointer to a netsnmp_handler_registration struct.
360  *
361  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
362  *
363  *  @see netsnmp_create_handler_registration()
364  *  @see netsnmp_register_mib()
365  */
366 int
netsnmp_register_handler_nocallback(netsnmp_handler_registration * reginfo)367 netsnmp_register_handler_nocallback(netsnmp_handler_registration *reginfo)
368 {
369     netsnmp_mib_handler *handler;
370     if (reginfo == NULL) {
371         snmp_log(LOG_ERR, "netsnmp_register_handler_nocallback() called illegally\n");
372         netsnmp_assert(reginfo != NULL);
373         return SNMP_ERR_GENERR;
374     }
375     DEBUGIF("handler::register") {
376         DEBUGMSGTL(("handler::register",
377                     "Registering (with no callback) "));
378         for (handler = reginfo->handler; handler; handler = handler->next) {
379             DEBUGMSG(("handler::register", "::%s", handler->handler_name));
380         }
381 
382         DEBUGMSG(("handler::register", " at "));
383         if (reginfo->rootoid && reginfo->range_subid) {
384             DEBUGMSGOIDRANGE(("handler::register", reginfo->rootoid,
385                               reginfo->rootoid_len, reginfo->range_subid,
386                               reginfo->range_ubound));
387         } else if (reginfo->rootoid) {
388             DEBUGMSGOID(("handler::register", reginfo->rootoid,
389                          reginfo->rootoid_len));
390         } else {
391             DEBUGMSG(("handler::register", "[null]"));
392         }
393         DEBUGMSG(("handler::register", "\n"));
394     }
395 
396     /*
397      * don't let them register for absolutely nothing.  Probably a mistake
398      */
399     if (0 == reginfo->modes) {
400         reginfo->modes = HANDLER_CAN_DEFAULT;
401     }
402 
403     return netsnmp_register_mib(reginfo->handler->handler_name,
404                                 NULL, 0, 0,
405                                 reginfo->rootoid, reginfo->rootoid_len,
406                                 reginfo->priority,
407                                 reginfo->range_subid,
408                                 reginfo->range_ubound, NULL,
409                                 reginfo->contextName, reginfo->timeout, 0,
410                                 reginfo, 0);
411 }
412 
413 /** Injects handler into the calling chain of handlers.
414  *  The given MIB handler is inserted after the handler named before_what.
415  *  If before_what is NULL, the handler is put at the top of the list,
416  *  and hence will be the handler to be called first.
417  *
418  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
419  *
420  *  @see netsnmp_create_handler_registration()
421  *  @see netsnmp_inject_handler()
422  */
423 int
netsnmp_inject_handler_before(netsnmp_handler_registration * reginfo,netsnmp_mib_handler * handler,const char * before_what)424 netsnmp_inject_handler_before(netsnmp_handler_registration *reginfo,
425                               netsnmp_mib_handler *handler,
426                               const char *before_what)
427 {
428     netsnmp_mib_handler *handler2 = handler;
429 
430     if (handler == NULL || reginfo == NULL) {
431         snmp_log(LOG_ERR, "netsnmp_inject_handler() called illegally\n");
432         netsnmp_assert(reginfo != NULL);
433         netsnmp_assert(handler != NULL);
434         return SNMP_ERR_GENERR;
435     }
436     while (handler2->next) {
437         handler2 = handler2->next;  /* Find the end of a handler sub-chain */
438     }
439     if (reginfo->handler == NULL) {
440         DEBUGMSGTL(("handler:inject", "injecting %s\n", handler->handler_name));
441     }
442     else {
443         DEBUGMSGTL(("handler:inject", "injecting %s before %s\n",
444                     handler->handler_name, reginfo->handler->handler_name));
445     }
446     if (before_what) {
447         netsnmp_mib_handler *nexth, *prevh = NULL;
448         if (reginfo->handler == NULL) {
449             snmp_log(LOG_ERR, "no handler to inject before\n");
450             return SNMP_ERR_GENERR;
451         }
452         for(nexth = reginfo->handler; nexth;
453             prevh = nexth, nexth = nexth->next) {
454             if (strcmp(nexth->handler_name, before_what) == 0)
455                 break;
456         }
457         if (!nexth) {
458 	    snmp_log(LOG_ERR, "Cannot inject '%s' before '%s': not found\n", handler->handler_name, before_what);
459 	    snmp_log(LOG_ERR, "The handlers are:\n");
460 	    for (nexth = reginfo->handler; nexth; nexth = nexth->next)
461 		snmp_log(LOG_ERR, "  %s\n", nexth->handler_name);
462             return SNMP_ERR_GENERR;
463 	}
464         if (prevh) {
465             /* after prevh and before nexth */
466             prevh->next = handler;
467             handler2->next = nexth;
468             handler->prev = prevh;
469             nexth->prev = handler2;
470             return SNMPERR_SUCCESS;
471         }
472         /* else we're first, which is what we do next anyway so fall through */
473     }
474     handler2->next = reginfo->handler;
475     if (reginfo->handler)
476         reginfo->handler->prev = handler2;
477     reginfo->handler = handler;
478     return SNMPERR_SUCCESS;
479 }
480 
481 /** Injects handler into the calling chain of handlers.
482  *  The given MIB handler is put at the top of the list,
483  *  and hence will be the handler to be called first.
484  *
485  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
486  *
487  *  @see netsnmp_create_handler_registration()
488  *  @see netsnmp_inject_handler_before()
489  */
490 int
netsnmp_inject_handler(netsnmp_handler_registration * reginfo,netsnmp_mib_handler * handler)491 netsnmp_inject_handler(netsnmp_handler_registration *reginfo,
492                        netsnmp_mib_handler *handler)
493 {
494     return netsnmp_inject_handler_before(reginfo, handler, NULL);
495 }
496 
497 /** Calls a MIB handlers chain, starting with specific handler.
498  *  The given arguments and MIB handler are checked
499  *  for sanity, then the handlers are called, one by one,
500  *  until next handler is NULL.
501  *
502  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
503  */
504 NETSNMP_INLINE int
netsnmp_call_handler(netsnmp_mib_handler * next_handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)505 netsnmp_call_handler(netsnmp_mib_handler *next_handler,
506                      netsnmp_handler_registration *reginfo,
507                      netsnmp_agent_request_info *reqinfo,
508                      netsnmp_request_info *requests)
509 {
510     Netsnmp_Node_Handler *nh;
511     int             ret;
512 
513     if (next_handler == NULL || reginfo == NULL || reqinfo == NULL ||
514         requests == NULL) {
515         snmp_log(LOG_ERR, "netsnmp_call_handler() called illegally\n");
516         netsnmp_assert(next_handler != NULL);
517         netsnmp_assert(reqinfo != NULL);
518         netsnmp_assert(reginfo != NULL);
519         netsnmp_assert(requests != NULL);
520         return SNMP_ERR_GENERR;
521     }
522 
523     do {
524         nh = next_handler->access_method;
525         if (!nh) {
526             if (next_handler->next) {
527                 snmp_log(LOG_ERR, "no access method specified in handler %s.",
528                          next_handler->handler_name);
529                 return SNMP_ERR_GENERR;
530             }
531             /*
532              * The final handler registration in the chain may well not need
533              * to include a handler routine, if the processing of this object
534              * is handled completely by the agent toolkit helpers.
535              */
536             return SNMP_ERR_NOERROR;
537         }
538 
539         DEBUGMSGTL(("handler:calling", "calling handler %s for mode %s\n",
540                     next_handler->handler_name,
541                     se_find_label_in_slist("agent_mode", reqinfo->mode)));
542 
543         /*
544          * XXX: define acceptable return statuses
545          */
546         ret = (*nh) (next_handler, reginfo, reqinfo, requests);
547 
548         DEBUGMSGTL(("handler:returned", "handler %s returned %d\n",
549                     next_handler->handler_name, ret));
550 
551         if (! (next_handler->flags & MIB_HANDLER_AUTO_NEXT))
552             break;
553 
554         /*
555          * did handler signal that it didn't want auto next this time around?
556          */
557         if(next_handler->flags & MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE) {
558             next_handler->flags &= ~MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
559             break;
560         }
561 
562         next_handler = next_handler->next;
563 
564     } while(next_handler);
565 
566     return ret;
567 }
568 
569 /** @private
570  *  Calls all the MIB Handlers in registration struct for a given mode.
571  *
572  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
573  */
574 int
netsnmp_call_handlers(netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)575 netsnmp_call_handlers(netsnmp_handler_registration *reginfo,
576                       netsnmp_agent_request_info *reqinfo,
577                       netsnmp_request_info *requests)
578 {
579     netsnmp_request_info *request;
580     int             status;
581 
582     if (reginfo == NULL || reqinfo == NULL || requests == NULL) {
583         snmp_log(LOG_ERR, "netsnmp_call_handlers() called illegally\n");
584         netsnmp_assert(reqinfo != NULL);
585         netsnmp_assert(reginfo != NULL);
586         netsnmp_assert(requests != NULL);
587         return SNMP_ERR_GENERR;
588     }
589 
590     if (reginfo->handler == NULL) {
591         snmp_log(LOG_ERR, "no handler specified.");
592         return SNMP_ERR_GENERR;
593     }
594 
595     switch (reqinfo->mode) {
596     case MODE_GETBULK:
597     case MODE_GET:
598     case MODE_GETNEXT:
599         if (!(reginfo->modes & HANDLER_CAN_GETANDGETNEXT))
600             return SNMP_ERR_NOERROR;    /* legal */
601         break;
602 
603 #ifndef NETSNMP_NO_WRITE_SUPPORT
604     case MODE_SET_RESERVE1:
605     case MODE_SET_RESERVE2:
606     case MODE_SET_ACTION:
607     case MODE_SET_COMMIT:
608     case MODE_SET_FREE:
609     case MODE_SET_UNDO:
610         if (!(reginfo->modes & HANDLER_CAN_SET)) {
611             for (; requests; requests = requests->next) {
612                 netsnmp_set_request_error(reqinfo, requests,
613                                           SNMP_ERR_NOTWRITABLE);
614             }
615             return SNMP_ERR_NOERROR;
616         }
617         break;
618 #endif /* NETSNMP_NO_WRITE_SUPPORT */
619 
620     default:
621         snmp_log(LOG_ERR, "unknown mode in netsnmp_call_handlers! bug!\n");
622         return SNMP_ERR_GENERR;
623     }
624     DEBUGMSGTL(("handler:calling", "main handler %s\n",
625                 reginfo->handler->handler_name));
626 
627     for (request = requests ; request; request = request->next) {
628         request->processed = 0;
629     }
630 
631     status = netsnmp_call_handler(reginfo->handler, reginfo, reqinfo, requests);
632 
633     return status;
634 }
635 
636 /** @private
637  *  Calls the next MIB handler in the chain, after the current one.
638  *  The given arguments and MIB handler are checked
639  *  for sanity, then the next handler is called.
640  *
641  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
642  */
643 NETSNMP_INLINE int
netsnmp_call_next_handler(netsnmp_mib_handler * current,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)644 netsnmp_call_next_handler(netsnmp_mib_handler *current,
645                           netsnmp_handler_registration *reginfo,
646                           netsnmp_agent_request_info *reqinfo,
647                           netsnmp_request_info *requests)
648 {
649 
650     if (current == NULL || reginfo == NULL || reqinfo == NULL ||
651         requests == NULL) {
652         snmp_log(LOG_ERR, "netsnmp_call_next_handler() called illegally\n");
653         netsnmp_assert(current != NULL);
654         netsnmp_assert(reginfo != NULL);
655         netsnmp_assert(reqinfo != NULL);
656         netsnmp_assert(requests != NULL);
657         return SNMP_ERR_GENERR;
658     }
659 
660     return netsnmp_call_handler(current->next, reginfo, reqinfo, requests);
661 }
662 
663 /** @private
664  *  Calls the next MIB handler in the chain, after the current one.
665  *  The given arguments and MIB handler are not validated before
666  *  the call, only request is checked.
667  *
668  *  @return Returns SNMPERR_SUCCESS or SNMP_ERR_* error code.
669  */
670 netsnmp_feature_child_of(netsnmp_call_next_handler_one_request,netsnmp_unused);
671 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST
672 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)673 netsnmp_call_next_handler_one_request(netsnmp_mib_handler *current,
674                                       netsnmp_handler_registration *reginfo,
675                                       netsnmp_agent_request_info *reqinfo,
676                                       netsnmp_request_info *requests)
677 {
678     netsnmp_request_info *request;
679     int ret;
680 
681     if (!requests) {
682         snmp_log(LOG_ERR, "netsnmp_call_next_handler_ONE_REQUEST() called illegally\n");
683         netsnmp_assert(requests != NULL);
684         return SNMP_ERR_GENERR;
685     }
686 
687     request = requests->next;
688     requests->next = NULL;
689     ret = netsnmp_call_handler(current->next, reginfo, reqinfo, requests);
690     requests->next = request;
691     return ret;
692 }
693 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CALL_NEXT_HANDLER_ONE_REQUEST */
694 
695 /** Deallocates resources associated with a given handler.
696  *  The handler is removed from chain and then freed.
697  *  After calling this function, the handler pointer is invalid
698  *  and should be set to NULL.
699  *
700  *  @param handler is the MIB Handler to be freed
701  */
702 void
netsnmp_handler_free(netsnmp_mib_handler * handler)703 netsnmp_handler_free(netsnmp_mib_handler *handler)
704 {
705     if (handler != NULL) {
706         if (handler->next != NULL) {
707             /** make sure we aren't pointing to ourselves.  */
708             netsnmp_assert(handler != handler->next); /* bugs caught: 1 */
709             netsnmp_handler_free(handler->next);
710             handler->next = NULL;
711         }
712         if ((handler->myvoid != NULL) && (handler->data_free != NULL))
713         {
714             handler->data_free(handler->myvoid);
715         }
716         SNMP_FREE(handler->handler_name);
717         SNMP_FREE(handler);
718     }
719 }
720 
721 /** Duplicates a MIB handler and all subsequent handlers.
722  *  Creates a copy of all data in given handlers chain.
723  *
724  *  @param handler is the MIB Handler to be duplicated
725  *
726  *  @return Returns a pointer to the complete copy,
727  *         or NULL if any problem occured.
728  *
729  * @see _clone_handler()
730  */
731 netsnmp_mib_handler *
netsnmp_handler_dup(netsnmp_mib_handler * handler)732 netsnmp_handler_dup(netsnmp_mib_handler *handler)
733 {
734     netsnmp_mib_handler *h = NULL;
735 
736     if (!handler)
737         goto err;
738 
739     h = _clone_handler(handler);
740     if (!h)
741         goto err;
742 
743     /*
744      * Providing a clone function without a free function is asking for
745      * memory leaks, and providing a free function without clone function
746      * is asking for memory corruption. Hence the log statement below.
747      */
748     if (!!handler->data_clone != !!handler->data_free)
749         snmp_log(LOG_ERR, "data_clone / data_free inconsistent (%s)\n",
750                  handler->handler_name);
751     if (handler->myvoid && handler->data_clone) {
752         h->myvoid = handler->data_clone(handler->myvoid);
753         if (!h->myvoid)
754             goto err;
755     } else
756         h->myvoid = handler->myvoid;
757     h->data_clone = handler->data_clone;
758     h->data_free = handler->data_free;
759 
760     if (handler->next != NULL) {
761         h->next = netsnmp_handler_dup(handler->next);
762         if (!h->next)
763             goto err;
764         h->next->prev = h;
765     }
766     h->prev = NULL;
767     return h;
768 
769 err:
770     netsnmp_handler_free(h);
771     return NULL;
772 }
773 
774 /** Free resources associated with a handler registration object.
775  *  The registration object and all MIB Handlers in the chain are
776  *  freed. When the function ends, given pointer is no longer valid
777  *  and should be set to NULL.
778  *
779  *  @param reginfo is the handler registration object to be freed
780  */
781 void
netsnmp_handler_registration_free(netsnmp_handler_registration * reginfo)782 netsnmp_handler_registration_free(netsnmp_handler_registration *reginfo)
783 {
784     if (reginfo != NULL) {
785         netsnmp_handler_free(reginfo->handler);
786         SNMP_FREE(reginfo->handlerName);
787         SNMP_FREE(reginfo->contextName);
788         SNMP_FREE(reginfo->rootoid);
789         reginfo->rootoid_len = 0;
790         SNMP_FREE(reginfo);
791     }
792 }
793 
794 /** Duplicates handler registration object and all subsequent handlers.
795  *  Creates a copy of the handler registration object and all its data.
796  *
797  *  @param reginfo is the handler registration object to be duplicated
798  *
799  *  @return Returns a pointer to the complete copy,
800  *         or NULL if any problem occured.
801  *
802  * @see netsnmp_handler_dup()
803  */
804 netsnmp_handler_registration *
netsnmp_handler_registration_dup(netsnmp_handler_registration * reginfo)805 netsnmp_handler_registration_dup(netsnmp_handler_registration *reginfo)
806 {
807     netsnmp_handler_registration *r = NULL;
808 
809     if (reginfo == NULL)
810         return NULL;
811 
812     r = calloc(1, sizeof(netsnmp_handler_registration));
813     if (!r)
814         return r;
815     r->modes = reginfo->modes;
816     r->priority = reginfo->priority;
817     r->range_subid = reginfo->range_subid;
818     r->timeout = reginfo->timeout;
819     r->range_ubound = reginfo->range_ubound;
820     r->rootoid_len = reginfo->rootoid_len;
821 
822     if (reginfo->handlerName != NULL) {
823         r->handlerName = strdup(reginfo->handlerName);
824         if (r->handlerName == NULL)
825             goto err;
826     }
827 
828     if (reginfo->contextName != NULL) {
829         r->contextName = strdup(reginfo->contextName);
830         if (r->contextName == NULL)
831             goto err;
832     }
833 
834     if (reginfo->rootoid != NULL) {
835         /*
836          * + 1 to make the following code safe:
837          * reginfo->rootoid[reginfo->rootoid_len++] = 0;
838          * See also netsnmp_scalar_helper_handler().
839          */
840         r->rootoid = malloc((reginfo->rootoid_len + 1) * sizeof(oid));
841         if (r->rootoid == NULL)
842             goto err;
843         memcpy(r->rootoid, reginfo->rootoid,
844                reginfo->rootoid_len * sizeof(oid));
845     }
846 
847     r->handler = netsnmp_handler_dup(reginfo->handler);
848     if (r->handler == NULL)
849         goto err;
850     return r;
851 
852 err:
853     netsnmp_handler_registration_free(r);
854     return NULL;
855 }
856 
857 /** Creates a cache of information which can be saved for future reference.
858  *  The cache is filled with pointers from parameters. Note that
859  *  the input structures are not duplicated, but put directly into
860  *  the new cache struct.
861  *  Use netsnmp_handler_check_cache() later to make sure it's still
862  *  valid before referencing it in the future.
863  *
864  * @see netsnmp_handler_check_cache()
865  * @see netsnmp_free_delegated_cache()
866  */
867 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)868 netsnmp_create_delegated_cache(netsnmp_mib_handler *handler,
869                                netsnmp_handler_registration *reginfo,
870                                netsnmp_agent_request_info *reqinfo,
871                                netsnmp_request_info *requests,
872                                void *localinfo)
873 {
874     netsnmp_delegated_cache *ret;
875 
876     ret = SNMP_MALLOC_TYPEDEF(netsnmp_delegated_cache);
877     if (ret) {
878         ret->transaction_id = reqinfo->asp->pdu->transid;
879         ret->handler = handler;
880         ret->reginfo = reginfo;
881         ret->reqinfo = reqinfo;
882         ret->requests = requests;
883         ret->localinfo = localinfo;
884     }
885     return ret;
886 }
887 
888 /** Check if a given delegated cache is still valid.
889  *  The cache is valid if it's a part of transaction
890  *  (ie, the agent still considers it to be an outstanding request).
891  *
892  *  @param dcache is the delegated cache to be checked.
893  *
894  *  @return Returns the cache pointer if it is still valid, NULL otherwise.
895  *
896  * @see netsnmp_create_delegated_cache()
897  * @see netsnmp_free_delegated_cache()
898  */
899 NETSNMP_INLINE netsnmp_delegated_cache *
netsnmp_handler_check_cache(netsnmp_delegated_cache * dcache)900 netsnmp_handler_check_cache(netsnmp_delegated_cache *dcache)
901 {
902     if (!dcache)
903         return dcache;
904 
905     if (netsnmp_check_transaction_id(dcache->transaction_id) ==
906         SNMPERR_SUCCESS)
907         return dcache;
908 
909     return NULL;
910 }
911 
912 /** Free a cache once it's no longer used.
913  *  Deletes the data allocated by netsnmp_create_delegated_cache().
914  *  Structures which were not allocated by netsnmp_create_delegated_cache()
915  *  are not freed (pointers are dropped).
916  *
917  *  @param dcache is the delegated cache to be freed.
918  *
919  * @see netsnmp_create_delegated_cache()
920  * @see netsnmp_handler_check_cache()
921  */
922 NETSNMP_INLINE void
netsnmp_free_delegated_cache(netsnmp_delegated_cache * dcache)923 netsnmp_free_delegated_cache(netsnmp_delegated_cache *dcache)
924 {
925     /*
926      * right now, no extra data is there that needs to be freed
927      */
928     if (dcache)
929         SNMP_FREE(dcache);
930 
931     return;
932 }
933 
934 
935 #ifndef NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED
936 /** Sets a list of requests as delegated or not delegated.
937  *  Sweeps through given chain of requests and sets 'delegated'
938  *  flag accordingly to the isdelegaded parameter.
939  *
940  *  @param requests Request list.
941  *  @param isdelegated New value of the 'delegated' flag.
942  */
943 void
netsnmp_handler_mark_requests_as_delegated(netsnmp_request_info * requests,int isdelegated)944 netsnmp_handler_mark_requests_as_delegated(netsnmp_request_info *requests,
945                                            int isdelegated)
946 {
947     while (requests) {
948         requests->delegated = isdelegated;
949         requests = requests->next;
950     }
951 }
952 #endif /* NETSNMP_FEATURE_REMOVE_HANDLER_MARK_REQUESTS_AS_DELEGATED */
953 
954 /** Adds data from node list to the request information structure.
955  *  Data in the request can be later extracted and used by submodules.
956  *
957  * @param request Destination request information structure.
958  *
959  * @param node The data to be added to the linked list
960  *             request->parent_data.
961  *
962  * @see netsnmp_request_remove_list_data()
963  * @see netsnmp_request_get_list_data()
964  */
965 NETSNMP_INLINE void
netsnmp_request_add_list_data(netsnmp_request_info * request,netsnmp_data_list * node)966 netsnmp_request_add_list_data(netsnmp_request_info *request,
967                               netsnmp_data_list *node)
968 {
969     if (request) {
970         if (request->parent_data)
971             netsnmp_add_list_data(&request->parent_data, node);
972         else
973             request->parent_data = node;
974     }
975 }
976 
977 /** Removes all data from a request.
978  *
979  * @param request the netsnmp request info structure
980  *
981  * @param name this is the name of the previously added data
982  *
983  * @return 0 on successful find-and-delete, 1 otherwise.
984  *
985  * @see netsnmp_request_add_list_data()
986  * @see netsnmp_request_get_list_data()
987  */
988 NETSNMP_INLINE int
netsnmp_request_remove_list_data(netsnmp_request_info * request,const char * name)989 netsnmp_request_remove_list_data(netsnmp_request_info *request,
990                                  const char *name)
991 {
992     if ((NULL == request) || (NULL ==request->parent_data))
993         return 1;
994 
995     return netsnmp_remove_list_node(&request->parent_data, name);
996 }
997 
998 /** Extracts data from a request.
999  *  Retrieves data that was previously added to the request,
1000  *  usually by a parent module.
1001  *
1002  * @param request Source request information structure.
1003  *
1004  * @param name Used to compare against the request->parent_data->name value;
1005  *             if a match is found, request->parent_data->data is returned.
1006  *
1007  * @return Gives a void pointer(request->parent_data->data); NULL is
1008  *         returned if source data is NULL or the object is not found.
1009  *
1010  * @see netsnmp_request_add_list_data()
1011  * @see netsnmp_request_remove_list_data()
1012  */
1013 void    *
netsnmp_request_get_list_data(netsnmp_request_info * request,const char * name)1014 netsnmp_request_get_list_data(netsnmp_request_info *request,
1015                               const char *name)
1016 {
1017     if (request)
1018         return netsnmp_get_list_data(request->parent_data, name);
1019     return NULL;
1020 }
1021 
1022 /** Free the extra data stored in a request.
1023  *  Deletes the data in given request only. Other chain items
1024  *  are unaffected.
1025  *
1026  * @param request Request information structure to be modified.
1027  *
1028  * @see netsnmp_request_add_list_data()
1029  * @see netsnmp_free_list_data()
1030  */
1031 NETSNMP_INLINE void
netsnmp_free_request_data_set(netsnmp_request_info * request)1032 netsnmp_free_request_data_set(netsnmp_request_info *request)
1033 {
1034     if (request)
1035         netsnmp_free_list_data(request->parent_data);
1036 }
1037 
1038 /** Free the extra data stored in a bunch of requests.
1039  *  Deletes all data in the chain inside request.
1040  *
1041  * @param request Request information structure to be modified.
1042  *
1043  * @see netsnmp_request_add_list_data()
1044  * @see netsnmp_free_request_data_set()
1045  */
1046 NETSNMP_INLINE void
netsnmp_free_request_data_sets(netsnmp_request_info * request)1047 netsnmp_free_request_data_sets(netsnmp_request_info *request)
1048 {
1049     if (request && request->parent_data) {
1050         netsnmp_free_all_list_data(request->parent_data);
1051         request->parent_data = NULL;
1052     }
1053 }
1054 
1055 /** Returns a MIB handler from a chain based on the name.
1056  *
1057  * @param reginfo Handler registration struct which contains the chain.
1058  *
1059  * @param name Target MIB Handler name string. The name is case
1060  *        sensitive.
1061  *
1062  * @return The MIB Handler is returned, or NULL if not found.
1063  *
1064  * @see netsnmp_request_add_list_data()
1065  */
1066 netsnmp_mib_handler *
netsnmp_find_handler_by_name(netsnmp_handler_registration * reginfo,const char * name)1067 netsnmp_find_handler_by_name(netsnmp_handler_registration *reginfo,
1068                              const char *name)
1069 {
1070     netsnmp_mib_handler *it;
1071     if (reginfo == NULL || name == NULL )
1072         return NULL;
1073     for (it = reginfo->handler; it; it = it->next) {
1074         if (strcmp(it->handler_name, name) == 0) {
1075             return it;
1076         }
1077     }
1078     return NULL;
1079 }
1080 
1081 /** Returns a handler's void pointer from a chain based on the name.
1082  *
1083  * @warning The void pointer data may change as a handler evolves.
1084  *        Handlers should really advertise some function for you
1085  *        to use instead.
1086  *
1087  * @param reginfo Handler registration struct which contains the chain.
1088  *
1089  * @param name Target MIB Handler name string. The name is case
1090  *        sensitive.
1091  *
1092  * @return The MIB Handler's void * pointer is returned,
1093  *        or NULL if the handler is not found.
1094  *
1095  * @see netsnmp_find_handler_by_name()
1096  */
1097 void           *
netsnmp_find_handler_data_by_name(netsnmp_handler_registration * reginfo,const char * name)1098 netsnmp_find_handler_data_by_name(netsnmp_handler_registration *reginfo,
1099                                   const char *name)
1100 {
1101     netsnmp_mib_handler *it = netsnmp_find_handler_by_name(reginfo, name);
1102     if (it)
1103         return it->myvoid;
1104     return NULL;
1105 }
1106 
1107 /** @private
1108  *  Clones a MIB Handler with its basic properties.
1109  *  Creates a copy of the given MIB Handler. Copies name, flags and
1110  *  access methods only; not myvoid.
1111  *
1112  *  @see netsnmp_handler_dup()
1113  */
1114 static netsnmp_mib_handler *
_clone_handler(netsnmp_mib_handler * it)1115 _clone_handler(netsnmp_mib_handler *it)
1116 {
1117     netsnmp_mib_handler *dup;
1118 
1119     if(NULL == it)
1120         return NULL;
1121 
1122     dup = netsnmp_create_handler(it->handler_name, it->access_method);
1123     if(NULL != dup)
1124         dup->flags = it->flags;
1125 
1126     return dup;
1127 }
1128 
1129 static netsnmp_data_list *handler_reg = NULL;
1130 
1131 void
handler_free_callback(void * handler)1132 handler_free_callback(void *handler)
1133 {
1134     netsnmp_handler_free((netsnmp_mib_handler *)handler);
1135 }
1136 
1137 /** Registers a given handler by name, so that it can be found easily later.
1138  *  Pointer to the handler is put into a list where it can be easily located
1139  *  at any time.
1140  *
1141  * @param name Name string to be associated with the handler.
1142  *
1143  * @param handler Pointer the MIB Handler.
1144  *
1145  * @see netsnmp_clear_handler_list()
1146  */
1147 void
netsnmp_register_handler_by_name(const char * name,netsnmp_mib_handler * handler)1148 netsnmp_register_handler_by_name(const char *name,
1149                                  netsnmp_mib_handler *handler)
1150 {
1151     netsnmp_add_list_data(&handler_reg,
1152                           netsnmp_create_data_list(name, (void *) handler,
1153                                                    handler_free_callback));
1154     DEBUGMSGTL(("handler_registry", "registering helper %s\n", name));
1155 }
1156 
1157 /** Clears the entire MIB Handlers registration list.
1158  *  MIB Handlers registration list is used to access any MIB Handler by
1159  *  its name. The function frees the list memory and sets pointer to NULL.
1160  *  Instead of calling this function directly, use shutdown_agent().
1161  *
1162  *  @see shutdown_agent()
1163  *  @see netsnmp_register_handler_by_name()
1164  */
1165 void
netsnmp_clear_handler_list(void)1166 netsnmp_clear_handler_list(void)
1167 {
1168     DEBUGMSGTL(("agent_handler", "netsnmp_clear_handler_list() called\n"));
1169     netsnmp_free_all_list_data(handler_reg);
1170     handler_reg = NULL;
1171 }
1172 
1173 /** @private
1174  *  Injects a handler into a subtree, peers and children when a given
1175  *  subtrees name matches a passed in name.
1176  */
1177 void
netsnmp_inject_handler_into_subtree(netsnmp_subtree * tp,const char * name,netsnmp_mib_handler * handler,const char * before_what)1178 netsnmp_inject_handler_into_subtree(netsnmp_subtree *tp, const char *name,
1179                                     netsnmp_mib_handler *handler,
1180                                     const char *before_what)
1181 {
1182     netsnmp_subtree *tptr;
1183     netsnmp_mib_handler *mh;
1184 
1185     for (tptr = tp; tptr != NULL; tptr = tptr->next) {
1186         /*  if (tptr->children) {
1187               netsnmp_inject_handler_into_subtree(tptr->children,name,handler);
1188 	    }   */
1189         if (strcmp(tptr->label_a, name) == 0) {
1190             DEBUGMSGTL(("injectHandler", "injecting handler %s into %s\n",
1191                         handler->handler_name, tptr->label_a));
1192             netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler),
1193                                           before_what);
1194         } else if (tptr->reginfo != NULL &&
1195 		   tptr->reginfo->handlerName != NULL &&
1196                    strcmp(tptr->reginfo->handlerName, name) == 0) {
1197             DEBUGMSGTL(("injectHandler", "injecting handler into %s/%s\n",
1198                         tptr->label_a, tptr->reginfo->handlerName));
1199             netsnmp_inject_handler_before(tptr->reginfo, _clone_handler(handler),
1200                                           before_what);
1201         } else if (tptr->reginfo != NULL) {
1202             for (mh = tptr->reginfo->handler; mh != NULL; mh = mh->next) {
1203                 if (mh->handler_name && strcmp(mh->handler_name, name) == 0) {
1204                     DEBUGMSGTL(("injectHandler", "injecting handler into %s\n",
1205                                 tptr->label_a));
1206                     netsnmp_inject_handler_before(tptr->reginfo,
1207                                                   _clone_handler(handler),
1208                                                   before_what);
1209                     break;
1210                 } else {
1211                     DEBUGMSGTL(("injectHandler",
1212                                 "not injecting handler into %s\n",
1213                                 mh->handler_name));
1214                 }
1215             }
1216         }
1217     }
1218 }
1219 
1220 static int      doneit = 0;
1221 /** @private
1222  *  Parses the "injectHandler" token line.
1223  */
1224 void
parse_injectHandler_conf(const char * token,char * cptr)1225 parse_injectHandler_conf(const char *token, char *cptr)
1226 {
1227     char            handler_to_insert[256], reg_name[256];
1228     subtree_context_cache *stc;
1229     netsnmp_mib_handler *handler;
1230 
1231     /*
1232      * XXXWWW: ensure instead that handler isn't inserted twice
1233      */
1234     if (doneit)                 /* we only do this once without restart the agent */
1235         return;
1236 
1237     cptr = copy_nword(cptr, handler_to_insert, sizeof(handler_to_insert));
1238     handler = (netsnmp_mib_handler*)netsnmp_get_list_data(handler_reg, handler_to_insert);
1239     if (!handler) {
1240 	netsnmp_config_error("no \"%s\" handler registered.",
1241 			     handler_to_insert);
1242         return;
1243     }
1244 
1245     if (!cptr) {
1246         config_perror("no INTONAME specified.  Can't do insertion.");
1247         return;
1248     }
1249     cptr = copy_nword(cptr, reg_name, sizeof(reg_name));
1250 
1251     for (stc = get_top_context_cache(); stc; stc = stc->next) {
1252         DEBUGMSGTL(("injectHandler", "Checking context tree %s (before=%s)\n",
1253                     stc->context_name, (cptr)?cptr:"null"));
1254         netsnmp_inject_handler_into_subtree(stc->first_subtree, reg_name,
1255                                             handler, cptr);
1256     }
1257 }
1258 
1259 /** @private
1260  *  Callback to ensure injectHandler parser doesn't do things twice.
1261  *  @todo replace this with a method to check the handler chain instead.
1262  */
1263 static int
handler_mark_inject_handler_done(int majorID,int minorID,void * serverarg,void * clientarg)1264 handler_mark_inject_handler_done(int majorID, int minorID,
1265                     void *serverarg, void *clientarg)
1266 {
1267     doneit = 1;
1268     return 0;
1269 }
1270 
1271 /** @private
1272  *  Registers the injectHandle parser token.
1273  *  Used in init_agent_read_config().
1274  *
1275  *  @see init_agent_read_config()
1276  */
1277 void
netsnmp_init_handler_conf(void)1278 netsnmp_init_handler_conf(void)
1279 {
1280     snmpd_register_config_handler("injectHandler",
1281                                   parse_injectHandler_conf,
1282                                   NULL, "injectHandler NAME INTONAME [BEFORE_OTHER_NAME]");
1283     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
1284                            SNMP_CALLBACK_POST_READ_CONFIG,
1285                            handler_mark_inject_handler_done, NULL);
1286 
1287     se_add_pair_to_slist("agent_mode", strdup("GET"), MODE_GET);
1288     se_add_pair_to_slist("agent_mode", strdup("GETNEXT"), MODE_GETNEXT);
1289     se_add_pair_to_slist("agent_mode", strdup("GETBULK"), MODE_GETBULK);
1290 #ifndef NETSNMP_NO_WRITE_SUPPORT
1291     se_add_pair_to_slist("agent_mode", strdup("SET_BEGIN"),
1292                          MODE_SET_BEGIN);
1293     se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE1"),
1294                          MODE_SET_RESERVE1);
1295     se_add_pair_to_slist("agent_mode", strdup("SET_RESERVE2"),
1296                          MODE_SET_RESERVE2);
1297     se_add_pair_to_slist("agent_mode", strdup("SET_ACTION"),
1298                          MODE_SET_ACTION);
1299     se_add_pair_to_slist("agent_mode", strdup("SET_COMMIT"),
1300                          MODE_SET_COMMIT);
1301     se_add_pair_to_slist("agent_mode", strdup("SET_FREE"), MODE_SET_FREE);
1302     se_add_pair_to_slist("agent_mode", strdup("SET_UNDO"), MODE_SET_UNDO);
1303 
1304     se_add_pair_to_slist("babystep_mode", strdup("pre-request"),
1305                          MODE_BSTEP_PRE_REQUEST);
1306     se_add_pair_to_slist("babystep_mode", strdup("object_lookup"),
1307                          MODE_BSTEP_OBJECT_LOOKUP);
1308     se_add_pair_to_slist("babystep_mode", strdup("check_value"),
1309                          MODE_BSTEP_CHECK_VALUE);
1310     se_add_pair_to_slist("babystep_mode", strdup("row_create"),
1311                          MODE_BSTEP_ROW_CREATE);
1312     se_add_pair_to_slist("babystep_mode", strdup("undo_setup"),
1313                          MODE_BSTEP_UNDO_SETUP);
1314     se_add_pair_to_slist("babystep_mode", strdup("set_value"),
1315                          MODE_BSTEP_SET_VALUE);
1316     se_add_pair_to_slist("babystep_mode", strdup("check_consistency"),
1317                          MODE_BSTEP_CHECK_CONSISTENCY);
1318     se_add_pair_to_slist("babystep_mode", strdup("undo_set"),
1319                          MODE_BSTEP_UNDO_SET);
1320     se_add_pair_to_slist("babystep_mode", strdup("commit"),
1321                          MODE_BSTEP_COMMIT);
1322     se_add_pair_to_slist("babystep_mode", strdup("undo_commit"),
1323                          MODE_BSTEP_UNDO_COMMIT);
1324     se_add_pair_to_slist("babystep_mode", strdup("irreversible_commit"),
1325                          MODE_BSTEP_IRREVERSIBLE_COMMIT);
1326     se_add_pair_to_slist("babystep_mode", strdup("undo_cleanup"),
1327                          MODE_BSTEP_UNDO_CLEANUP);
1328     se_add_pair_to_slist("babystep_mode", strdup("post_request"),
1329                          MODE_BSTEP_POST_REQUEST);
1330     se_add_pair_to_slist("babystep_mode", strdup("original"), 0xffff);
1331 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1332 
1333     /*
1334      * xxx-rks: hmmm.. will this work for modes which are or'd together?
1335      *          I'm betting not...
1336      */
1337     se_add_pair_to_slist("handler_can_mode", strdup("GET/GETNEXT"),
1338                          HANDLER_CAN_GETANDGETNEXT);
1339     se_add_pair_to_slist("handler_can_mode", strdup("SET"),
1340                          HANDLER_CAN_SET);
1341     se_add_pair_to_slist("handler_can_mode", strdup("GETBULK"),
1342                          HANDLER_CAN_GETBULK);
1343     se_add_pair_to_slist("handler_can_mode", strdup("BABY_STEP"),
1344                          HANDLER_CAN_BABY_STEP);
1345 }
1346 
1347 /** @} */
1348