and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / snmp_agent.c
1 /*
2  * snmp_agent.c
3  *
4  * Simple Network Management Protocol (RFC 1067).
5  */
6 /***********************************************************
7         Copyright 1988, 1989 by Carnegie Mellon University
8
9                       All Rights Reserved
10
11 Permission to use, copy, modify, and distribute this software and its 
12 documentation for any purpose and without fee is hereby granted, 
13 provided that the above copyright notice appear in all copies and that
14 both that copyright notice and this permission notice appear in 
15 supporting documentation, and that the name of CMU not be
16 used in advertising or publicity pertaining to distribution of the
17 software without specific, written prior permission.  
18
19 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
20 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
21 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
22 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
23 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25 SOFTWARE.
26 ******************************************************************/
27
28 #include <net-snmp/net-snmp-config.h>
29
30 #include <sys/types.h>
31 #ifdef HAVE_LIMITS_H
32 #include <limits.h>
33 #endif
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #if HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #if HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #if TIME_WITH_SYS_TIME
44 # ifdef WIN32
45 #  include <sys/timeb.h>
46 # else
47 #  include <sys/time.h>
48 # endif
49 # include <time.h>
50 #else
51 # if HAVE_SYS_TIME_H
52 #  include <sys/time.h>
53 # else
54 #  include <time.h>
55 # endif
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #if HAVE_NETINET_IN_H
61 #include <netinet/in.h>
62 #endif
63 #include <errno.h>
64 #if HAVE_WINSOCK_H
65 #include <winsock.h>
66 #endif
67
68 #if HAVE_DMALLOC_H
69 #include <dmalloc.h>
70 #endif
71
72 #define SNMP_NEED_REQUEST_LIST
73 #include <net-snmp/net-snmp-includes.h>
74 #include <net-snmp/agent/net-snmp-agent-includes.h>
75 #include <net-snmp/library/snmp_assert.h>
76
77 #ifdef USE_LIBWRAP
78 #include <tcpd.h>
79 #include <syslog.h>
80 int             allow_severity = LOG_INFO;
81 int             deny_severity = LOG_WARNING;
82 #endif
83
84 #include "snmpd.h"
85 #include "mibgroup/struct.h"
86 #include "mibgroup/util_funcs.h"
87 #include <net-snmp/agent/mib_module_config.h>
88
89 #ifdef USING_AGENTX_PROTOCOL_MODULE
90 #include "agentx/protocol.h"
91 #endif
92
93 #ifdef USING_AGENTX_MASTER_MODULE
94 #include "agentx/master.h"
95 #endif
96
97 #define SNMP_ADDRCACHE_SIZE 10
98
99 struct addrCache {
100     char           *addr;
101     enum { SNMP_ADDRCACHE_UNUSED = 0,
102         SNMP_ADDRCACHE_USED = 1,
103         SNMP_ADDRCACHE_OLD = 2
104     } status;
105 };
106
107 static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
108 int             lastAddrAge = 0;
109 int             log_addresses = 0;
110
111
112
113 typedef struct _agent_nsap {
114     int             handle;
115     netsnmp_transport *t;
116     void           *s;          /*  Opaque internal session pointer.  */
117     struct _agent_nsap *next;
118 } agent_nsap;
119
120 static agent_nsap *agent_nsap_list = NULL;
121 static netsnmp_agent_session *agent_session_list = NULL;
122 static netsnmp_agent_session *netsnmp_processing_set = NULL;
123 netsnmp_agent_session *agent_delegated_list = NULL;
124 netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
125
126
127 int             netsnmp_agent_check_packet(netsnmp_session *,
128                                            struct netsnmp_transport_s *,
129                                            void *, int);
130 int             netsnmp_agent_check_parse(netsnmp_session *, netsnmp_pdu *,
131                                           int);
132 void            delete_subnetsnmp_tree_cache(netsnmp_agent_session *asp);
133 int             handle_pdu(netsnmp_agent_session *asp);
134 int             netsnmp_handle_request(netsnmp_agent_session *asp,
135                                        int status);
136 int             netsnmp_wrap_up_request(netsnmp_agent_session *asp,
137                                         int status);
138 int             check_delayed_request(netsnmp_agent_session *asp);
139 int             handle_getnext_loop(netsnmp_agent_session *asp);
140 int             handle_set_loop(netsnmp_agent_session *asp);
141
142 int             netsnmp_check_queued_chain_for(netsnmp_agent_session *asp);
143 int             netsnmp_add_queued(netsnmp_agent_session *asp);
144 int             netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
145
146
147 static int      current_globalid = 0;
148
149 int
150 netsnmp_allocate_globalcacheid(void)
151 {
152     return ++current_globalid;
153 }
154
155 int
156 netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
157 {
158     while (cache_store != NULL) {
159         if (cache_store->globalid == globalid)
160             return cache_store->cacheid;
161         cache_store = cache_store->next;
162     }
163     return -1;
164 }
165
166 netsnmp_cachemap *
167 netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
168                                 int globalid, int localid)
169 {
170     netsnmp_cachemap *tmpp;
171
172     tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
173     if (*cache_store) {
174         tmpp->next = *cache_store;
175         *cache_store = tmpp;
176     } else {
177         *cache_store = tmpp;
178     }
179
180     tmpp->globalid = globalid;
181     tmpp->cacheid = localid;
182     return tmpp;
183 }
184
185 void
186 netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
187 {
188     netsnmp_cachemap *tmpp;
189     while (cache_store) {
190         tmpp = cache_store;
191         cache_store = cache_store->next;
192         free(tmpp);
193     }
194 }
195
196
197 typedef struct agent_set_cache_s {
198     /*
199      * match on these 2 
200      */
201     int             transID;
202     netsnmp_session *sess;
203
204     /*
205      * store this info 
206      */
207     netsnmp_tree_cache *treecache;
208     int             treecache_len;
209     int             treecache_num;
210
211     int             vbcount;
212     netsnmp_request_info *requests;
213     netsnmp_data_list *agent_data;
214
215     /*
216      * list 
217      */
218     struct agent_set_cache_s *next;
219 } agent_set_cache;
220
221 static agent_set_cache *Sets = NULL;
222
223 agent_set_cache *
224 save_set_cache(netsnmp_agent_session *asp)
225 {
226     agent_set_cache *ptr;
227
228     if (!asp->reqinfo || !asp->pdu)
229         return NULL;
230
231     ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
232     if (ptr == NULL)
233         return NULL;
234
235     /*
236      * Save the important information 
237      */
238     ptr->transID = asp->pdu->transid;
239     ptr->sess = asp->session;
240     ptr->treecache = asp->treecache;
241     ptr->treecache_len = asp->treecache_len;
242     ptr->treecache_num = asp->treecache_num;
243     ptr->agent_data = asp->reqinfo->agent_data;
244     ptr->requests = asp->requests;
245     ptr->vbcount = asp->vbcount;
246
247     /*
248      * make the agent forget about what we've saved 
249      */
250     asp->treecache = NULL;
251     asp->reqinfo->agent_data = NULL;
252     asp->pdu->variables = NULL;
253     asp->requests = NULL;
254
255     ptr->next = Sets;
256     Sets = ptr;
257
258     return ptr;
259 }
260
261 int
262 get_set_cache(netsnmp_agent_session *asp)
263 {
264     agent_set_cache *ptr, *prev = NULL;
265
266     for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
267         if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
268             if (prev)
269                 prev->next = ptr->next;
270             else
271                 Sets = ptr->next;
272
273             /*
274              * found it.  Get the needed data 
275              */
276             asp->treecache = ptr->treecache;
277             asp->treecache_len = ptr->treecache_len;
278             asp->treecache_num = ptr->treecache_num;
279             asp->requests = ptr->requests;
280             asp->vbcount = ptr->vbcount;
281             if (!asp->reqinfo) {
282                 asp->reqinfo =
283                     SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
284                 if (asp->reqinfo) {
285                     asp->reqinfo->asp = asp;
286                     asp->reqinfo->agent_data = ptr->agent_data;
287                 }
288             }
289             free(ptr);
290             return SNMP_ERR_NOERROR;
291         }
292         prev = ptr;
293     }
294     return SNMP_ERR_GENERR;
295 }
296
297 int
298 getNextSessID()
299 {
300     static int      SessionID = 0;
301
302     return ++SessionID;
303 }
304
305 int
306 agent_check_and_process(int block)
307 {
308     int             numfds;
309     fd_set          fdset;
310     struct timeval  timeout = { LONG_MAX, 0 }, *tvp = &timeout;
311     int             count;
312     int             fakeblock = 0;
313
314     numfds = 0;
315     FD_ZERO(&fdset);
316     snmp_select_info(&numfds, &fdset, tvp, &fakeblock);
317     if (block != 0 && fakeblock != 0) {
318         /*
319          * There are no alarms registered, and the caller asked for blocking, so
320          * let select() block forever.  
321          */
322
323         tvp = NULL;
324     } else if (block != 0 && fakeblock == 0) {
325         /*
326          * The caller asked for blocking, but there is an alarm due sooner than
327          * LONG_MAX seconds from now, so use the modified timeout returned by
328          * snmp_select_info as the timeout for select().  
329          */
330
331     } else if (block == 0) {
332         /*
333          * The caller does not want us to block at all.  
334          */
335
336         tvp->tv_sec = 0;
337         tvp->tv_usec = 0;
338     }
339
340     count = select(numfds, &fdset, 0, 0, tvp);
341
342     if (count > 0) {
343         /*
344          * packets found, process them 
345          */
346         snmp_read(&fdset);
347     } else
348         switch (count) {
349         case 0:
350             snmp_timeout();
351             break;
352         case -1:
353             if (errno != EINTR) {
354                 snmp_log_perror("select");
355             }
356             return -1;
357         default:
358             snmp_log(LOG_ERR, "select returned %d\n", count);
359             return -1;
360         }                       /* endif -- count>0 */
361
362     /*
363      * Run requested alarms.  
364      */
365     run_alarms();
366
367     return count;
368 }
369
370
371
372 /*
373  * Set up the address cache.  
374  */
375 void
376 netsnmp_addrcache_initialise(void)
377 {
378     int             i = 0;
379
380     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
381         addrCache[i].addr = NULL;
382         addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
383     }
384 }
385
386
387
388 /*
389  * Age the entries in the address cache.  
390  */
391
392 void
393 netsnmp_addrcache_age(void)
394 {
395     int             i = 0;
396
397     lastAddrAge = 0;
398     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
399         if (addrCache[i].status == SNMP_ADDRCACHE_OLD) {
400             addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
401             if (addrCache[i].addr != NULL) {
402                 free(addrCache[i].addr);
403                 addrCache[i].addr = NULL;
404             }
405         }
406         if (addrCache[i].status == SNMP_ADDRCACHE_USED) {
407             addrCache[i].status = SNMP_ADDRCACHE_OLD;
408         }
409     }
410 }
411
412 /*******************************************************************-o-******
413  * netsnmp_agent_check_packet
414  *
415  * Parameters:
416  *      session, transport, transport_data, transport_data_length
417  *      
418  * Returns:
419  *      1       On success.
420  *      0       On error.
421  *
422  * Handler for all incoming messages (a.k.a. packets) for the agent.  If using
423  * the libwrap utility, log the connection and deny/allow the access. Print
424  * output when appropriate, and increment the incoming counter.
425  *
426  */
427
428 int
429 netsnmp_agent_check_packet(netsnmp_session * session,
430                            netsnmp_transport *transport,
431                            void *transport_data, int transport_data_length)
432 {
433     char           *addr_string = NULL;
434     int             i = 0;
435
436     /*
437      * Log the message and/or dump the message.
438      * Optionally cache the network address of the sender.
439      */
440
441     if (transport != NULL && transport->f_fmtaddr != NULL) {
442         /*
443          * Okay I do know how to format this address for logging.  
444          */
445         addr_string = transport->f_fmtaddr(transport, transport_data,
446                                            transport_data_length);
447         /*
448          * Don't forget to free() it.  
449          */
450     }
451 #ifdef  USE_LIBWRAP
452     if (addr_string != NULL) {
453         if (hosts_ctl("snmpd", STRING_UNKNOWN, addr_string, STRING_UNKNOWN)) {
454             snmp_log(allow_severity, "Connection from %s\n", addr_string);
455         } else {
456             snmp_log(deny_severity, "Connection from %s REFUSED\n",
457                      addr_string);
458             free(addr_string);
459             return 0;
460         }
461     } else {
462         if (hosts_ctl("snmp", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
463             snmp_log(allow_severity, "Connection from <UNKNOWN>\n");
464             addr_string = strdup("<UNKNOWN>");
465         } else {
466             snmp_log(deny_severity, "Connection from <UNKNOWN> REFUSED\n");
467             return 0;
468         }
469     }
470 #endif                          /*USE_LIBWRAP */
471
472     snmp_increment_statistic(STAT_SNMPINPKTS);
473
474     if (log_addresses || netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
475                                                 NETSNMP_DS_AGENT_VERBOSE)) {
476         for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
477             if ((addrCache[i].status != SNMP_ADDRCACHE_UNUSED) &&
478                 (strcmp(addrCache[i].addr, addr_string) == 0)) {
479                 break;
480             }
481         }
482
483         if (i >= SNMP_ADDRCACHE_SIZE ||
484             netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
485                                    NETSNMP_DS_AGENT_VERBOSE)) {
486             /*
487              * Address wasn't in the cache, so log the packet...  
488              */
489             snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n",
490                      addr_string);
491             /*
492              * ...and try to cache the address.  
493              */
494             for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
495                 if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) {
496                     if (addrCache[i].addr != NULL) {
497                         free(addrCache[i].addr);
498                     }
499                     addrCache[i].addr = addr_string;
500                     addrCache[i].status = SNMP_ADDRCACHE_USED;
501                     addr_string = NULL; /* Don't free this 'temporary' string
502                                          * since it's now part of the cache */
503                     break;
504                 }
505             }
506             if (i >= SNMP_ADDRCACHE_SIZE) {
507                 /*
508                  * We didn't find a free slot to cache the address.  Perhaps
509                  * we should be using an LRU replacement policy here or
510                  * something.  Oh well.
511                  */
512                 DEBUGMSGTL(("netsnmp_agent_check_packet",
513                             "cache overrun"));
514             }
515         } else {
516             addrCache[i].status = SNMP_ADDRCACHE_USED;
517         }
518     }
519
520     if (addr_string != NULL) {
521         free(addr_string);
522         addr_string = NULL;
523     }
524     return 1;
525 }
526
527
528 int
529 netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
530                           int result)
531 {
532     if (result == 0) {
533         if (snmp_get_do_logging() &&
534             netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
535                                    NETSNMP_DS_AGENT_VERBOSE)) {
536             netsnmp_variable_list *var_ptr;
537
538             switch (pdu->command) {
539             case SNMP_MSG_GET:
540                 snmp_log(LOG_DEBUG, "  GET message\n");
541                 break;
542             case SNMP_MSG_GETNEXT:
543                 snmp_log(LOG_DEBUG, "  GETNEXT message\n");
544                 break;
545             case SNMP_MSG_RESPONSE:
546                 snmp_log(LOG_DEBUG, "  RESPONSE message\n");
547                 break;
548             case SNMP_MSG_SET:
549                 snmp_log(LOG_DEBUG, "  SET message\n");
550                 break;
551             case SNMP_MSG_TRAP:
552                 snmp_log(LOG_DEBUG, "  TRAP message\n");
553                 break;
554             case SNMP_MSG_GETBULK:
555                 snmp_log(LOG_DEBUG, "  GETBULK message, non-rep=%d, max_rep=%d\n",
556                          pdu->errstat, pdu->errindex);
557                 break;
558             case SNMP_MSG_INFORM:
559                 snmp_log(LOG_DEBUG, "  INFORM message\n");
560                 break;
561             case SNMP_MSG_TRAP2:
562                 snmp_log(LOG_DEBUG, "  TRAP2 message\n");
563                 break;
564             case SNMP_MSG_REPORT:
565                 snmp_log(LOG_DEBUG, "  REPORT message\n");
566                 break;
567
568             case SNMP_MSG_INTERNAL_SET_RESERVE1:
569                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE1 message\n");
570                 break;
571
572             case SNMP_MSG_INTERNAL_SET_RESERVE2:
573                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE2 message\n");
574                 break;
575
576             case SNMP_MSG_INTERNAL_SET_ACTION:
577                 snmp_log(LOG_DEBUG, "  INTERNAL ACTION message\n");
578                 break;
579
580             case SNMP_MSG_INTERNAL_SET_COMMIT:
581                 snmp_log(LOG_DEBUG, "  INTERNAL COMMIT message\n");
582                 break;
583
584             case SNMP_MSG_INTERNAL_SET_FREE:
585                 snmp_log(LOG_DEBUG, "  INTERNAL FREE message\n");
586                 break;
587
588             case SNMP_MSG_INTERNAL_SET_UNDO:
589                 snmp_log(LOG_DEBUG, "  INTERNAL UNDO message\n");
590                 break;
591
592             default:
593                 snmp_log(LOG_DEBUG, "  UNKNOWN message, type=%02X\n",
594                          pdu->command);
595                 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
596                 return 0;
597             }
598
599             for (var_ptr = pdu->variables; var_ptr != NULL;
600                  var_ptr = var_ptr->next_variable) {
601                 size_t          c_oidlen = 256, c_outlen = 0;
602                 u_char         *c_oid = (u_char *) malloc(c_oidlen);
603
604                 if (c_oid) {
605                     if (!sprint_realloc_objid
606                         (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
607                          var_ptr->name_length)) {
608                         snmp_log(LOG_DEBUG, "    -- %s [TRUNCATED]\n",
609                                  c_oid);
610                     } else {
611                         snmp_log(LOG_DEBUG, "    -- %s\n", c_oid);
612                     }
613                     free(c_oid);
614                 }
615             }
616         }
617         return 1;
618     }
619     return 0;                   /* XXX: does it matter what the return value
620                                  * is?  Yes: if we return 0, then the PDU is
621                                  * dumped.  */
622 }
623
624
625 /*
626  * Global access to the primary session structure for this agent.
627  * for Index Allocation use initially. 
628  */
629
630 /*
631  * I don't understand what this is for at the moment.  AFAICS as long as it
632  * gets set and points at a session, that's fine.  ???  
633  */
634
635 netsnmp_session *main_session = NULL;
636
637
638
639 /*
640  * Set up an agent session on the given transport.  Return a handle
641  * which may later be used to de-register this transport.  A return
642  * value of -1 indicates an error.  
643  */
644
645 int
646 netsnmp_register_agent_nsap(netsnmp_transport *t)
647 {
648     netsnmp_session *s, *sp = NULL;
649     agent_nsap     *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
650     int             handle = 0;
651     void           *isp = NULL;
652
653     if (t == NULL) {
654         return -1;
655     }
656
657     DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
658
659     n = (agent_nsap *) malloc(sizeof(agent_nsap));
660     if (n == NULL) {
661         return -1;
662     }
663     s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
664     if (s == NULL) {
665         free(n);
666         return -1;
667     }
668     memset(s, 0, sizeof(netsnmp_session));
669     snmp_sess_init(s);
670
671     /*
672      * Set up the session appropriately for an agent.  
673      */
674
675     s->version = SNMP_DEFAULT_VERSION;
676     s->callback = handle_snmp_packet;
677     s->authenticator = NULL;
678     s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 
679                                   NETSNMP_DS_AGENT_FLAGS);
680     s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
681
682     sp = snmp_add(s, t, netsnmp_agent_check_packet,
683                   netsnmp_agent_check_parse);
684     if (sp == NULL) {
685         free(s);
686         free(n);
687         return -1;
688     }
689
690     isp = snmp_sess_pointer(sp);
691     if (isp == NULL) {          /*  over-cautious  */
692         free(s);
693         free(n);
694         return -1;
695     }
696
697     n->s = isp;
698     n->t = t;
699
700     if (main_session == NULL) {
701         main_session = snmp_sess_session(isp);
702     }
703
704     for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
705          a = a->next) {
706         handle = a->handle;
707         prevNext = &(a->next);
708     }
709
710     if (handle < INT_MAX) {
711         n->handle = handle + 1;
712         n->next = a;
713         *prevNext = n;
714         free(s);
715         return n->handle;
716     } else {
717         free(s);
718         free(n);
719         return -1;
720     }
721 }
722
723 void
724 netsnmp_deregister_agent_nsap(int handle)
725 {
726     agent_nsap     *a = NULL, **prevNext = &agent_nsap_list;
727     int             main_session_deregistered = 0;
728
729     DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
730
731     for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
732         prevNext = &(a->next);
733     }
734
735     if (a != NULL && a->handle == handle) {
736         *prevNext = a->next;
737         if (main_session == snmp_sess_session(a->s)) {
738             main_session_deregistered = 1;
739         }
740         snmp_close(snmp_sess_session(a->s));
741         /*
742          * The above free()s the transport and session pointers.  
743          */
744         free(a);
745     }
746
747     /*
748      * If we've deregistered the session that main_session used to point to,
749      * then make it point to another one, or in the last resort, make it equal
750      * to NULL.  Basically this shouldn't ever happen in normal operation
751      * because main_session starts off pointing at the first session added by
752      * init_master_agent(), which then discards the handle.  
753      */
754
755     if (main_session_deregistered) {
756         if (agent_nsap_list != NULL) {
757             DEBUGMSGTL(("snmp_agent",
758                         "WARNING: main_session ptr changed from %p to %p\n",
759                         main_session, snmp_sess_session(agent_nsap_list->s)));
760             main_session = snmp_sess_session(agent_nsap_list->s);
761         } else {
762             DEBUGMSGTL(("snmp_agent",
763                         "WARNING: main_session ptr changed from %p to NULL\n",
764                         main_session));
765             main_session = NULL;
766         }
767     }
768 }
769
770
771
772 /*
773  * 
774  * This function has been modified to use the experimental
775  * netsnmp_register_agent_nsap interface.  The major responsibility of this
776  * function now is to interpret a string specified to the agent (via -p on the
777  * command line, or from a configuration file) as a list of agent NSAPs on
778  * which to listen for SNMP packets.  Typically, when you add a new transport
779  * domain "foo", you add code here such that if the "foo" code is compiled
780  * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
781  * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
782  * transport descriptor.  netsnmp_register_agent_nsap is then called with that
783  * transport descriptor and sets up a listening agent session on it.
784  * 
785  * Everything then works much as normal: the agent runs in an infinite loop
786  * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
787  * is readable on any of the given transports.  This routine then traverses
788  * the library 'Sessions' list to identify the relevant session and eventually
789  * invokes '_sess_read'.  This then processes the incoming packet, calling the
790  * pre_parse, parse, post_parse and callback routines in turn.
791  * 
792  * JBPN 20001117
793  */
794
795 int
796 init_master_agent(void)
797 {
798     netsnmp_transport *transport;
799     char           *cptr;
800     char            buf[SPRINT_MAX_LEN];
801
802     /* default to turning off lookup caching */
803     netsnmp_set_lookup_cache_size(0);
804
805     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
806                                NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
807         DEBUGMSGTL(("snmp_agent",
808                     "init_master_agent; not master agent\n"));
809         return 0;               /*  No error if ! MASTER_AGENT  */
810     }
811 #ifdef USING_AGENTX_MASTER_MODULE
812     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
813                                NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
814         real_init_master();
815 #endif
816
817     /*
818      * Have specific agent ports been specified?  
819      */
820     cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
821                                  NETSNMP_DS_AGENT_PORTS);
822
823     if (cptr) {
824         snprintf(buf, sizeof(buf), "%s", cptr);
825         buf[ sizeof(buf)-1 ] = 0;
826     } else {
827         /*
828          * No, so just specify the default port.  
829          */
830         if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_FLAGS) & SNMP_FLAGS_STREAM_SOCKET) {
831             sprintf(buf, "tcp:%d", SNMP_PORT);
832         } else {
833             sprintf(buf, "udp:%d", SNMP_PORT);
834         }
835     }
836
837     DEBUGMSGTL(("snmp_agent", "final port spec: %s\n", buf));
838     cptr = strtok(buf, ",");
839     while (cptr) {
840         /*
841          * Specification format: 
842          * 
843          * NONE:                      (a pseudo-transport)
844          * UDP:[address:]port        (also default if no transport is specified)
845          * TCP:[address:]port         (if supported)
846          * Unix:pathname              (if supported)
847          * AAL5PVC:itf.vpi.vci        (if supported)
848          * IPX:[network]:node[/port] (if supported)
849          * 
850          */
851
852         DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
853                     cptr));
854
855         if (!cptr || !(*cptr)) {
856             snmp_log(LOG_ERR, "improper port specification\n");
857             return 1;
858         }
859
860         if (strncasecmp(cptr, "none", 4) == 0) {
861             DEBUGMSGTL(("snmp_agent",
862                         "init_master_agent; pseudo-transport \"none\" requested\n"));
863             return 0;
864         }
865         transport = netsnmp_tdomain_transport(cptr, 1, "udp");
866
867         if (transport == NULL) {
868             snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n",
869                      cptr);
870             return 1;
871         }
872
873         if (netsnmp_register_agent_nsap(transport) == 0) {
874             snmp_log(LOG_ERR,
875                      "Error registering specified transport \"%s\" as an agent NSAP\n",
876                      cptr);
877             return 1;
878         } else {
879             DEBUGMSGTL(("snmp_agent",
880                         "init_master_agent; \"%s\" registered as an agent NSAP\n",
881                         cptr));
882         }
883
884         /*
885          * Next transport please...  
886          */
887         cptr = strtok(NULL, ",");
888     }
889
890     return 0;
891 }
892
893
894
895 netsnmp_agent_session *
896 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
897 {
898     netsnmp_agent_session *asp = (netsnmp_agent_session *)
899         calloc(1, sizeof(netsnmp_agent_session));
900
901     if (asp == NULL) {
902         return NULL;
903     }
904
905     DEBUGMSGTL(("snmp_agent","agent_sesion %08p created\n", asp));
906     asp->session = session;
907     asp->pdu = snmp_clone_pdu(pdu);
908     asp->orig_pdu = snmp_clone_pdu(pdu);
909     asp->rw = READ;
910     asp->exact = TRUE;
911     asp->next = NULL;
912     asp->mode = RESERVE1;
913     asp->status = SNMP_ERR_NOERROR;
914     asp->index = 0;
915     asp->oldmode = 0;
916     asp->treecache_num = -1;
917     asp->treecache_len = 0;
918
919     return asp;
920 }
921
922 void
923 free_agent_snmp_session(netsnmp_agent_session *asp)
924 {
925     if (!asp)
926         return;
927
928     DEBUGMSGTL(("snmp_agent","agent_sesion %08p released\n", asp));
929
930     netsnmp_remove_from_delegated(asp);
931     
932     if (asp->orig_pdu)
933         snmp_free_pdu(asp->orig_pdu);
934     if (asp->pdu)
935         snmp_free_pdu(asp->pdu);
936     if (asp->reqinfo)
937         netsnmp_free_agent_request_info(asp->reqinfo);
938     if (asp->treecache) {
939         free(asp->treecache);
940     }
941     if (asp->bulkcache) {
942         free(asp->bulkcache);
943     }
944     if (asp->requests) {
945         int             i;
946         for (i = 0; i < asp->vbcount; i++) {
947             netsnmp_free_request_data_sets(&asp->requests[i]);
948         }
949     }
950     if (asp->requests) {
951         free(asp->requests);
952     }
953     if (asp->cache_store) {
954         netsnmp_free_cachemap(asp->cache_store);
955         asp->cache_store = NULL;
956     }
957     free(asp);
958 }
959
960 int
961 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
962 {
963     int             i;
964     netsnmp_request_info *request;
965
966     if (NULL == asp->treecache)
967         return 0;
968     
969     for (i = 0; i <= asp->treecache_num; i++) {
970         for (request = asp->treecache[i].requests_begin; request;
971              request = request->next) {
972             if (request->delegated)
973                 return 1;
974         }
975     }
976     return 0;
977 }
978
979 int
980 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
981 {
982     netsnmp_agent_session *asptmp;
983     for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
984         if (asptmp == asp)
985             return 1;
986     }
987     return 0;
988 }
989
990 int
991 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
992 {
993     if (netsnmp_check_for_delegated(asp)) {
994         if (!netsnmp_check_delegated_chain_for(asp)) {
995             /*
996              * add to delegated request chain 
997              */
998             asp->next = agent_delegated_list;
999             agent_delegated_list = asp;
1000             DEBUGMSGTL(("snmp_agent", "delegate session == %08p\n", asp));
1001         }
1002         return 1;
1003     }
1004     return 0;
1005 }
1006
1007 int
1008 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
1009 {
1010     netsnmp_agent_session *curr, *prev = NULL;
1011     
1012     for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
1013         /*
1014          * is this us?
1015          */
1016         if (curr != asp)
1017             continue;
1018         
1019         /*
1020          * remove from queue 
1021          */
1022         if (prev != NULL)
1023             prev->next = asp->next;
1024         else
1025             agent_delegated_list = asp->next;
1026
1027         DEBUGMSGTL(("snmp_agent", "remove delegated session == %08p\n", asp));
1028
1029         return 1;
1030     }
1031
1032     return 0;
1033 }
1034
1035 /*
1036  * netsnmp_remove_delegated_requests_for_session
1037  *
1038  * called when a session is being closed. Check all delegated requests to
1039  * see if the are waiting on this session, and if set, set the status for
1040  * that request to GENERR.
1041  */
1042 int
1043 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
1044 {
1045     netsnmp_agent_session *asp;
1046     int count = 0;
1047     
1048     for (asp = agent_delegated_list; asp; asp = asp->next) {
1049         /*
1050          * check each request
1051          */
1052         netsnmp_request_info *request;
1053         for(request = asp->requests; request; request = request->next) {
1054             /*
1055              * check session
1056              */
1057             netsnmp_assert(NULL!=request->subtree);
1058             if(request->subtree->session != sess)
1059                 continue;
1060
1061             /*
1062              * matched! mark request as done
1063              */
1064             netsnmp_set_mode_request_error(MODE_SET_BEGIN, request,
1065                                            SNMP_ERR_GENERR);
1066             ++count;
1067         }
1068     }
1069
1070     /*
1071      * if we found any, that request may be finished now
1072      */
1073     if(count) {
1074         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
1075                     "%08p\n", count, sess));
1076         netsnmp_check_outstanding_agent_requests();
1077     }
1078     
1079     return count;
1080 }
1081
1082 int
1083 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
1084 {
1085     netsnmp_agent_session *asptmp;
1086     for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
1087         if (asptmp == asp)
1088             return 1;
1089     }
1090     return 0;
1091 }
1092
1093 int
1094 netsnmp_add_queued(netsnmp_agent_session *asp)
1095 {
1096     netsnmp_agent_session *asp_tmp;
1097
1098     /*
1099      * first item?
1100      */
1101     if (NULL == netsnmp_agent_queued_list) {
1102         netsnmp_agent_queued_list = asp;
1103         return 1;
1104     }
1105
1106
1107     /*
1108      * add to end of queued request chain 
1109      */
1110     asp_tmp = netsnmp_agent_queued_list;
1111     for (; asp_tmp; asp_tmp = asp_tmp->next) {
1112         /*
1113          * already in queue?
1114          */
1115         if (asp_tmp == asp)
1116             break;
1117
1118         /*
1119          * end of queue?
1120          */
1121         if (NULL == asp_tmp->next)
1122             asp_tmp->next = asp;
1123     }
1124     return 1;
1125 }
1126
1127
1128 int
1129 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
1130 {
1131     netsnmp_variable_list *var_ptr;
1132     int             i, n = 0, r = 0;
1133
1134     /*
1135      * if this request was a set, clear the global now that we are
1136      * done.
1137      */
1138     if (asp == netsnmp_processing_set) {
1139         DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %08p\n",
1140                     asp));
1141         netsnmp_processing_set = NULL;
1142     }
1143
1144     /*
1145      * some stuff needs to be saved in special subagent cases 
1146      */
1147     if (asp->pdu) {
1148
1149         switch (asp->pdu->command) {
1150             case SNMP_MSG_INTERNAL_SET_BEGIN:
1151             case SNMP_MSG_INTERNAL_SET_RESERVE1:
1152             case SNMP_MSG_INTERNAL_SET_RESERVE2:
1153             case SNMP_MSG_INTERNAL_SET_ACTION:
1154                 save_set_cache(asp);
1155                 break;
1156         }
1157
1158         /*
1159          * if this is a GETBULK response we need to rearrange the varbinds 
1160          */
1161         if (asp->pdu->command == SNMP_MSG_GETBULK) {
1162             int             repeats = asp->pdu->errindex;
1163             int             j;
1164             
1165             if (asp->pdu->errstat < asp->vbcount) {
1166                 n = asp->pdu->errstat;
1167             } else {
1168                 n = asp->vbcount;
1169             }
1170             if ((r = asp->vbcount - n) < 0) {
1171                 r = 0;
1172             }
1173             
1174             for (i = 0; i < r - 1; i++) {
1175                 for (j = 0; j < repeats; j++) {
1176                     asp->bulkcache[i * repeats + j]->next_variable =
1177                         asp->bulkcache[(i + 1) * repeats + j];
1178                 }
1179             }
1180             if (r > 0) {
1181                 for (j = 0; j < repeats - 1; j++) {
1182                     asp->bulkcache[(r - 1) * repeats + j]->next_variable =
1183                         asp->bulkcache[j + 1];
1184                 }
1185             }
1186         }
1187
1188         /*
1189          * May need to "dumb down" a SET error status for a
1190          * v1 query.  See RFC2576 - section 4.3
1191          */
1192         if ((asp->pdu->command == SNMP_MSG_SET) &&
1193             (asp->pdu->version == SNMP_VERSION_1)) {
1194             switch (status) {
1195                 case SNMP_ERR_WRONGVALUE:
1196                 case SNMP_ERR_WRONGENCODING:
1197                 case SNMP_ERR_WRONGTYPE:
1198                 case SNMP_ERR_WRONGLENGTH:
1199                 case SNMP_ERR_INCONSISTENTVALUE:
1200                     status = SNMP_ERR_BADVALUE;
1201                     asp->status = SNMP_ERR_BADVALUE;
1202                     break;
1203                 case SNMP_ERR_NOACCESS:
1204                 case SNMP_ERR_NOTWRITABLE:
1205                 case SNMP_ERR_NOCREATION:
1206                 case SNMP_ERR_INCONSISTENTNAME:
1207                 case SNMP_ERR_AUTHORIZATIONERROR:
1208                     status = SNMP_ERR_NOSUCHNAME;
1209                     asp->status = SNMP_ERR_NOSUCHNAME;
1210                     break;
1211                 case SNMP_ERR_RESOURCEUNAVAILABLE:
1212                 case SNMP_ERR_COMMITFAILED:
1213                 case SNMP_ERR_UNDOFAILED:
1214                     status = SNMP_ERR_GENERR;
1215                     asp->status = SNMP_ERR_GENERR;
1216                     break;
1217             }
1218         }
1219         /*
1220          * Similarly we may need to "dumb down" v2 exception
1221          *  types to throw an error for a v1 query.
1222          *  See RFC2576 - section 4.1.2.3
1223          */
1224         if ((asp->pdu->command != SNMP_MSG_SET) &&
1225             (asp->pdu->version == SNMP_VERSION_1)) {
1226             for (var_ptr = asp->pdu->variables, i = 1;
1227                  var_ptr != NULL; var_ptr = var_ptr->next_variable, i++) {
1228                 switch (var_ptr->type) {
1229                     case SNMP_NOSUCHOBJECT:
1230                     case SNMP_NOSUCHINSTANCE:
1231                     case SNMP_ENDOFMIBVIEW:
1232                     case ASN_COUNTER64:
1233                         status = SNMP_ERR_NOSUCHNAME;
1234                         asp->status = SNMP_ERR_NOSUCHNAME;
1235                         asp->index = i;
1236                         break;
1237                 }
1238             }
1239         }
1240     } /** if asp->pdu */
1241
1242     /*
1243      * Update the snmp error-count statistics
1244      *   XXX - should we include the V2 errors in this or not?
1245      */
1246 #define INCLUDE_V2ERRORS_IN_V1STATS
1247
1248     switch (status) {
1249 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1250     case SNMP_ERR_WRONGVALUE:
1251     case SNMP_ERR_WRONGENCODING:
1252     case SNMP_ERR_WRONGTYPE:
1253     case SNMP_ERR_WRONGLENGTH:
1254     case SNMP_ERR_INCONSISTENTVALUE:
1255 #endif
1256     case SNMP_ERR_BADVALUE:
1257         snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
1258         break;
1259 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1260     case SNMP_ERR_NOACCESS:
1261     case SNMP_ERR_NOTWRITABLE:
1262     case SNMP_ERR_NOCREATION:
1263     case SNMP_ERR_INCONSISTENTNAME:
1264     case SNMP_ERR_AUTHORIZATIONERROR:
1265 #endif
1266     case SNMP_ERR_NOSUCHNAME:
1267         snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
1268         break;
1269 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1270     case SNMP_ERR_RESOURCEUNAVAILABLE:
1271     case SNMP_ERR_COMMITFAILED:
1272     case SNMP_ERR_UNDOFAILED:
1273 #endif
1274     case SNMP_ERR_GENERR:
1275         snmp_increment_statistic(STAT_SNMPOUTGENERRS);
1276         break;
1277
1278     case SNMP_ERR_TOOBIG:
1279         snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
1280         break;
1281     }
1282
1283     if ((status == SNMP_ERR_NOERROR) && (asp->pdu)) {
1284         snmp_increment_statistic_by((asp->pdu->command == SNMP_MSG_SET ?
1285                                      STAT_SNMPINTOTALSETVARS :
1286                                      STAT_SNMPINTOTALREQVARS),
1287                                     count_varbinds(asp->pdu->variables));
1288     } else {
1289         /*
1290          * Use a copy of the original request
1291          *   to report failures.
1292          */
1293         snmp_free_pdu(asp->pdu);
1294         asp->pdu = asp->orig_pdu;
1295         asp->orig_pdu = NULL;
1296     }
1297     if (asp->pdu) {
1298         asp->pdu->command = SNMP_MSG_RESPONSE;
1299         asp->pdu->errstat = asp->status;
1300         asp->pdu->errindex = asp->index;
1301         if (!snmp_send(asp->session, asp->pdu)) {
1302             snmp_free_pdu(asp->pdu);
1303             asp->pdu = NULL;
1304         }
1305         snmp_increment_statistic(STAT_SNMPOUTPKTS);
1306         snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
1307         asp->pdu = NULL;
1308         netsnmp_remove_and_free_agent_snmp_session(asp);
1309     }
1310     return 1;
1311 }
1312
1313 void
1314 dump_sess_list(void)
1315 {
1316     netsnmp_agent_session *a;
1317
1318     DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
1319     for (a = agent_session_list; a != NULL; a = a->next) {
1320         DEBUGMSG(("snmp_agent", "%08p[session %08p] -> ", a, a->session));
1321     }
1322     DEBUGMSG(("snmp_agent", "[NIL]\n"));
1323 }
1324
1325 void
1326 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
1327 {
1328     netsnmp_agent_session *a, **prevNext = &agent_session_list;
1329
1330     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", asp));
1331
1332     for (a = agent_session_list; a != NULL; a = *prevNext) {
1333         if (a == asp) {
1334             *prevNext = a->next;
1335             a->next = NULL;
1336             free_agent_snmp_session(a);
1337             asp = NULL;
1338             break;
1339         } else {
1340             prevNext = &(a->next);
1341         }
1342     }
1343
1344     if (a == NULL && asp != NULL) {
1345         /*
1346          * We coulnd't find it on the list, so free it anyway.  
1347          */
1348         free_agent_snmp_session(asp);
1349     }
1350 }
1351
1352 void
1353 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
1354                                            void (*free_request)
1355                                            (netsnmp_request_list *))
1356 {
1357     netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
1358
1359     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", sess));
1360
1361     for (a = agent_session_list; a != NULL; a = next) {
1362         if (a->session == sess) {
1363             *prevNext = a->next;
1364             next = a->next;
1365             free_agent_snmp_session(a);
1366         } else {
1367             prevNext = &(a->next);
1368             next = a->next;
1369         }
1370     }
1371 }
1372
1373 /** handles an incoming SNMP packet into the agent */
1374 int
1375 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
1376                    netsnmp_pdu *pdu, void *magic)
1377 {
1378     netsnmp_agent_session *asp;
1379     int             status, access_ret, rc;
1380
1381     /*
1382      * We only support receiving here.  
1383      */
1384     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
1385         return 1;
1386     }
1387
1388     /*
1389      * RESPONSE messages won't get this far, but TRAP-like messages
1390      * might.  
1391      */
1392     if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
1393         pdu->command == SNMP_MSG_TRAP2) {
1394         DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
1395                     pdu->command));
1396         pdu->command = SNMP_MSG_TRAP2;
1397         snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
1398         return 1;
1399     }
1400
1401     if (magic == NULL) {
1402         asp = init_agent_snmp_session(session, pdu);
1403         status = SNMP_ERR_NOERROR;
1404     } else {
1405         asp = (netsnmp_agent_session *) magic;
1406         status = asp->status;
1407     }
1408
1409     if ((access_ret = check_access(pdu)) != 0) {
1410         if (access_ret == VACM_NOSUCHCONTEXT) {
1411             /*
1412              * rfc2573 section 3.2, step 5 says that we increment the
1413              * counter but don't return a response of any kind 
1414              */
1415
1416             /*
1417              * we currently don't support unavailable contexts, as
1418              * there is no reason to that I currently know of 
1419              */
1420             snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
1421
1422             /*
1423              * drop the request 
1424              */
1425             netsnmp_remove_and_free_agent_snmp_session(asp);
1426             return 0;
1427         } else {
1428             /*
1429              * access control setup is incorrect 
1430              */
1431             send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
1432             if (asp->pdu->version != SNMP_VERSION_1
1433                 && asp->pdu->version != SNMP_VERSION_2c) {
1434                 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
1435                 asp->pdu->command = SNMP_MSG_RESPONSE;
1436                 snmp_increment_statistic(STAT_SNMPOUTPKTS);
1437                 if (!snmp_send(asp->session, asp->pdu))
1438                     snmp_free_pdu(asp->pdu);
1439                 asp->pdu = NULL;
1440                 netsnmp_remove_and_free_agent_snmp_session(asp);
1441                 return 1;
1442             } else {
1443                 /*
1444                  * drop the request 
1445                  */
1446                 netsnmp_remove_and_free_agent_snmp_session(asp);
1447                 return 0;
1448             }
1449         }
1450     }
1451
1452     rc = netsnmp_handle_request(asp, status);
1453
1454     /*
1455      * done 
1456      */
1457     DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %08p\n",
1458                 asp));
1459     return rc;
1460 }
1461
1462 netsnmp_request_info *
1463 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
1464                              netsnmp_variable_list * varbind_ptr,
1465                              netsnmp_subtree *tp)
1466 {
1467     netsnmp_request_info *request = NULL;
1468     int             cacheid;
1469
1470     DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
1471     DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
1472                  varbind_ptr->name_length));
1473     DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
1474
1475     if (tp &&
1476         (asp->pdu->command == SNMP_MSG_GETNEXT ||
1477          asp->pdu->command == SNMP_MSG_GETBULK)) {
1478         int result;
1479         int prefix_len;
1480
1481         prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1482                                              tp->start_len,
1483                                              tp->end_a, tp->end_len);
1484         result =
1485             netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
1486
1487         while (result == VACM_NOTINVIEW) {
1488             /* the entire subtree is not in view. Skip it. */
1489             /** @todo make this be more intelligent about ranges.
1490                 Right now we merely take the highest level
1491                 commonality of a registration range and use that.
1492                 At times we might be able to be smarter about
1493                 checking the range itself as opposed to the node
1494                 above where the range exists, but I doubt this will
1495                 come up all that frequently. */
1496             tp = tp->next;
1497             if (tp) {
1498                 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1499                                                      tp->start_len,
1500                                                      tp->end_a,
1501                                                      tp->end_len);
1502                 result =
1503                     netsnmp_acm_check_subtree(asp->pdu,
1504                                               tp->start_a, prefix_len);
1505             }
1506         }
1507     }
1508     if (tp == NULL) {
1509         /*
1510          * no appropriate registration found 
1511          */
1512         /*
1513          * make up the response ourselves 
1514          */
1515         switch (asp->pdu->command) {
1516         case SNMP_MSG_GETNEXT:
1517         case SNMP_MSG_GETBULK:
1518             varbind_ptr->type = SNMP_ENDOFMIBVIEW;
1519             break;
1520
1521         case SNMP_MSG_SET:
1522             varbind_ptr->type = SNMP_NOSUCHOBJECT;
1523             break;
1524
1525         case SNMP_MSG_GET:
1526             varbind_ptr->type = SNMP_NOSUCHOBJECT;
1527             break;
1528
1529         default:
1530             return NULL;        /* shouldn't get here */
1531         }
1532     } else {
1533         DEBUGMSGTL(("snmp_agent", "tp->start "));
1534         DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
1535         DEBUGMSG(("snmp_agent", ", tp->end "));
1536         DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
1537         DEBUGMSG(("snmp_agent", ", \n"));
1538
1539         /*
1540          * malloc the request structure 
1541          */
1542         request = &(asp->requests[vbcount - 1]);
1543         request->index = vbcount;
1544         request->delegated = 0;
1545         request->processed = 0;
1546         request->status = 0;
1547         request->subtree = tp;
1548         if (request->parent_data) {
1549             netsnmp_free_request_data_sets(request);
1550         }
1551
1552         /*
1553          * for non-SET modes, set the type to NULL 
1554          */
1555         if (!MODE_IS_SET(asp->pdu->command)) {
1556             if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
1557                 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
1558                             request->index));
1559                 request->inclusive = 1;
1560             }
1561             varbind_ptr->type = ASN_NULL;
1562         }
1563
1564         /*
1565          * place them in a cache 
1566          */
1567         if (tp->global_cacheid) {
1568             /*
1569              * we need to merge all marked subtrees together 
1570              */
1571             if (asp->cache_store && -1 !=
1572                 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
1573                                                     tp->global_cacheid))) {
1574             } else {
1575                 cacheid = ++(asp->treecache_num);
1576                 netsnmp_get_or_add_local_cachid(&asp->cache_store,
1577                                                 tp->global_cacheid,
1578                                                 cacheid);
1579                 goto mallocslot;        /* XXX: ick */
1580             }
1581         } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
1582                    asp->treecache[tp->cacheid].subtree == tp) {
1583             /*
1584              * we have already added a request to this tree
1585              * pointer before 
1586              */
1587             cacheid = tp->cacheid;
1588         } else {
1589             cacheid = ++(asp->treecache_num);
1590           mallocslot:
1591             /*
1592              * new slot needed 
1593              */
1594             if (asp->treecache_num >= asp->treecache_len) {
1595                 /*
1596                  * exapand cache array 
1597                  */
1598                 /*
1599                  * WWW: non-linear expansion needed (with cap) 
1600                  */
1601 #define CACHE_GROW_SIZE 16
1602                 asp->treecache_len =
1603                     (asp->treecache_len + CACHE_GROW_SIZE);
1604                 asp->treecache =
1605                     realloc(asp->treecache,
1606                             sizeof(netsnmp_tree_cache) *
1607                             asp->treecache_len);
1608                 if (asp->treecache == NULL)
1609                     return NULL;
1610                 memset(&(asp->treecache[cacheid]), 0x00,
1611                        sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
1612             }
1613             asp->treecache[cacheid].subtree = tp;
1614             asp->treecache[cacheid].requests_begin = request;
1615             tp->cacheid = cacheid;
1616         }
1617
1618         /*
1619          * if this is a search type, get the ending range oid as well 
1620          */
1621         if (asp->pdu->command == SNMP_MSG_GETNEXT ||
1622             asp->pdu->command == SNMP_MSG_GETBULK) {
1623             request->range_end = tp->end_a;
1624             request->range_end_len = tp->end_len;
1625         } else {
1626             request->range_end = NULL;
1627             request->range_end_len = 0;
1628         }
1629
1630         /*
1631          * link into chain 
1632          */
1633         if (asp->treecache[cacheid].requests_end)
1634             asp->treecache[cacheid].requests_end->next = request;
1635         request->next = NULL;
1636         request->prev = asp->treecache[cacheid].requests_end;
1637         asp->treecache[cacheid].requests_end = request;
1638
1639         /*
1640          * add the given request to the list of requests they need
1641          * to handle results for 
1642          */
1643         request->requestvb = varbind_ptr;
1644     }
1645     return request;
1646 }
1647
1648 /*
1649  * check the ACM(s) for the results on each of the varbinds.
1650  * If ACM disallows it, replace the value with type
1651  * 
1652  * Returns number of varbinds with ACM errors
1653  */
1654 int
1655 check_acm(netsnmp_agent_session *asp, u_char type)
1656 {
1657     int             view;
1658     int             i;
1659     netsnmp_request_info *request;
1660     int             ret = 0;
1661     netsnmp_variable_list *vb;
1662
1663     for (i = 0; i <= asp->treecache_num; i++) {
1664         for (request = asp->treecache[i].requests_begin;
1665              request; request = request->next) {
1666             /*
1667              * for each request, run it through in_a_view() 
1668              */
1669             vb = request->requestvb;
1670             if (vb->type == ASN_NULL)   /* not yet processed */
1671                 continue;
1672             view =
1673                 in_a_view(vb->name, &vb->name_length, asp->pdu, vb->type);
1674
1675             /*
1676              * if a ACM error occurs, mark it as type passed in 
1677              */
1678             if (view != VACM_SUCCESS) {
1679                 ret++;
1680                 snmp_set_var_typed_value(vb, type, NULL, 0);
1681             }
1682         }
1683     }
1684     return ret;
1685 }
1686
1687
1688 int
1689 netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
1690 {
1691     netsnmp_subtree *tp;
1692     netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
1693     int             view;
1694     int             vbcount = 0;
1695     int             bulkcount = 0, bulkrep = 0;
1696     int             i = 0, n = 0, r = 0;
1697     netsnmp_request_info *request;
1698
1699     if (asp->treecache == NULL && asp->treecache_len == 0) {
1700         asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
1701         asp->treecache =
1702             calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
1703         if (asp->treecache == NULL)
1704             return SNMP_ERR_GENERR;
1705     }
1706     asp->treecache_num = -1;
1707
1708     if (asp->pdu->command == SNMP_MSG_GETBULK) {
1709         /*
1710          * getbulk prep 
1711          */
1712         int             count = count_varbinds(asp->pdu->variables);
1713
1714         if (asp->pdu->errstat < 0) {
1715             asp->pdu->errstat = 0;
1716         }
1717         if (asp->pdu->errindex < 0) {
1718             asp->pdu->errindex = 0;
1719         }
1720
1721         if (asp->pdu->errstat < count) {
1722             n = asp->pdu->errstat;
1723         } else {
1724             n = count;
1725         }
1726         if ((r = count - n) < 0) {
1727             r = 0;
1728             asp->bulkcache = NULL;
1729         } else {
1730             asp->bulkcache =
1731                 (netsnmp_variable_list **) malloc(asp->pdu->errindex * r *
1732                                                   sizeof(struct
1733                                                          varbind_list *));
1734         }
1735         DEBUGMSGTL(("snmp_agent", "GETBULK N = %d, M = %d, R = %d\n",
1736                     n, asp->pdu->errindex, r));
1737     }
1738
1739     /*
1740      * collect varbinds into their registered trees 
1741      */
1742     prevNext = &(asp->pdu->variables);
1743     for (varbind_ptr = asp->pdu->variables; varbind_ptr;
1744          varbind_ptr = vbsave) {
1745
1746         /*
1747          * getbulk mess with this pointer, so save it 
1748          */
1749         vbsave = varbind_ptr->next_variable;
1750
1751         if (asp->pdu->command == SNMP_MSG_GETBULK) {
1752             if (n > 0) {
1753                 n--;
1754             } else {
1755                 /*
1756                  * repeate request varbinds on GETBULK.  These will
1757                  * have to be properly rearranged later though as
1758                  * responses are supposed to actually be interlaced
1759                  * with each other.  This is done with the asp->bulkcache. 
1760                  */
1761                 bulkrep = asp->pdu->errindex - 1;
1762                 if (asp->pdu->errindex > 0) {
1763                     vbptr = varbind_ptr;
1764                     asp->bulkcache[bulkcount++] = vbptr;
1765
1766                     for (i = 1; i < asp->pdu->errindex; i++) {
1767                         vbptr->next_variable =
1768                             SNMP_MALLOC_STRUCT(variable_list);
1769                         /*
1770                          * don't clone the oid as it's got to be
1771                          * overwwritten anyway 
1772                          */
1773                         if (!vbptr->next_variable) {
1774                             /*
1775                              * XXXWWW: ack!!! 
1776                              */
1777                         } else {
1778                             vbptr = vbptr->next_variable;
1779                             vbptr->name_length = 0;
1780                             vbptr->type = ASN_NULL;
1781                             asp->bulkcache[bulkcount++] = vbptr;
1782                         }
1783                     }
1784                     vbptr->next_variable = vbsave;
1785                 } else {
1786                     /*
1787                      * 0 repeats requested for this varbind, so take it off
1788                      * the list.  
1789                      */
1790                     vbptr = varbind_ptr;
1791                     *prevNext = vbptr->next_variable;
1792                     vbptr->next_variable = NULL;
1793                     snmp_free_varbind(vbptr);
1794                     asp->vbcount--;
1795                     continue;
1796                 }
1797             }
1798         }
1799
1800         /*
1801          * count the varbinds 
1802          */
1803         ++vbcount;
1804
1805         /*
1806          * find the owning tree 
1807          */
1808         tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
1809                                   NULL, asp->pdu->contextName);
1810
1811         /*
1812          * check access control 
1813          */
1814         switch (asp->pdu->command) {
1815         case SNMP_MSG_GET:
1816             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1817                              asp->pdu, varbind_ptr->type);
1818             if (view != VACM_SUCCESS)
1819                 snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
1820                                          NULL, 0);
1821             break;
1822
1823         case SNMP_MSG_SET:
1824             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1825                              asp->pdu, varbind_ptr->type);
1826             if (view != VACM_SUCCESS)
1827                 return SNMP_ERR_NOTWRITABLE;
1828             break;
1829
1830         case SNMP_MSG_GETNEXT:
1831         case SNMP_MSG_GETBULK:
1832         default:
1833             view = VACM_SUCCESS;
1834             /*
1835              * XXXWWW: check VACM here to see if "tp" is even worthwhile 
1836              */
1837         }
1838         if (view == VACM_SUCCESS) {
1839             request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
1840                                                    tp);
1841             if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
1842                 request->repeat = bulkrep;
1843             }
1844             if (!request)
1845                 return SNMP_ERR_GENERR;
1846         }
1847
1848         prevNext = &(varbind_ptr->next_variable);
1849     }
1850
1851     return SNMPERR_SUCCESS;
1852 }
1853
1854 /*
1855  * this function is only applicable in getnext like contexts 
1856  */
1857 int
1858 netsnmp_reassign_requests(netsnmp_agent_session *asp)
1859 {
1860     /*
1861      * assume all the requests have been filled or rejected by the
1862      * subtrees, so reassign the rejected ones to the next subtree in
1863      * the chain 
1864      */
1865
1866     int             i;
1867
1868     /*
1869      * get old info 
1870      */
1871     netsnmp_tree_cache *old_treecache = asp->treecache;
1872
1873     /*
1874      * malloc new space 
1875      */
1876     asp->treecache =
1877         (netsnmp_tree_cache *) calloc(asp->treecache_len,
1878                                       sizeof(netsnmp_tree_cache));
1879     asp->treecache_num = -1;
1880     if (asp->cache_store) {
1881         netsnmp_free_cachemap(asp->cache_store);
1882         asp->cache_store = NULL;
1883     }
1884
1885     for (i = 0; i < asp->vbcount; i++) {
1886         if (asp->requests[i].requestvb->type == ASN_NULL) {
1887             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1888                                               asp->requests[i].requestvb,
1889                                               asp->requests[i].subtree->next)) {
1890                 if (old_treecache != NULL) {
1891                     free(old_treecache);
1892                 }
1893                 return SNMP_ERR_GENERR;
1894             }
1895         } else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
1896             /*
1897              * re-add the same subtree 
1898              */
1899             asp->requests[i].requestvb->type = ASN_NULL;
1900             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1901                                               asp->requests[i].requestvb,
1902                                               asp->requests[i].subtree)) {
1903                 if (old_treecache != NULL) {
1904                     free(old_treecache);
1905                 }
1906                 return SNMP_ERR_GENERR;
1907             }
1908         }
1909     }
1910
1911     if (old_treecache != NULL) {
1912         free(old_treecache);
1913     }
1914     return SNMP_ERR_NOERROR;
1915 }
1916
1917 void
1918 netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
1919 {
1920     while (reqlist) {
1921         netsnmp_free_request_data_sets(reqlist);
1922         reqlist = reqlist->next;
1923     }
1924 }
1925
1926 void
1927 netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
1928 {
1929     while (asp->treecache_num >= 0) {
1930         /*
1931          * don't delete subtrees 
1932          */
1933         netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
1934                                      requests_begin);
1935         asp->treecache_num--;
1936     }
1937 }
1938
1939 int
1940 netsnmp_check_requests_status(netsnmp_agent_session *asp,
1941                               netsnmp_request_info *requests,
1942                               int look_for_specific)
1943 {
1944     /*
1945      * find any errors marked in the requests 
1946      */
1947     while (requests) {
1948         if (requests->status != SNMP_ERR_NOERROR &&
1949             (!look_for_specific || requests->status == look_for_specific)
1950             && (look_for_specific || asp->index == 0
1951                 || requests->index < asp->index)) {
1952             asp->index = requests->index;
1953             asp->status = requests->status;
1954         }
1955         requests = requests->next;
1956     }
1957     return asp->status;
1958 }
1959
1960 int
1961 netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
1962                                   int look_for_specific)
1963 {
1964     int             i;
1965     for (i = 0; i <= asp->treecache_num; i++) {
1966         netsnmp_check_requests_status(asp,
1967                                       asp->treecache[i].requests_begin,
1968                                       look_for_specific);
1969     }
1970     return asp->status;
1971 }
1972
1973 int
1974 handle_var_requests(netsnmp_agent_session *asp)
1975 {
1976     int             i, retstatus = SNMP_ERR_NOERROR,
1977         status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
1978     netsnmp_handler_registration *reginfo;
1979
1980     /*
1981      * create the netsnmp_agent_request_info data 
1982      */
1983     if (!asp->reqinfo) {
1984         asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
1985         if (!asp->reqinfo)
1986             return SNMP_ERR_GENERR;
1987     }
1988
1989     asp->reqinfo->asp = asp;
1990     asp->reqinfo->mode = asp->mode;
1991
1992     /*
1993      * now, have the subtrees in the cache go search for their results 
1994      */
1995     for (i = 0; i <= asp->treecache_num; i++) {
1996         reginfo = asp->treecache[i].subtree->reginfo;
1997         status = netsnmp_call_handlers(reginfo, asp->reqinfo,
1998                                        asp->treecache[i].requests_begin);
1999
2000         /*
2001          * find any errors marked in the requests.  For later parts of
2002          * SET processing, only check for new errors specific to that
2003          * set processing directive (which must superceed the previous
2004          * errors).
2005          */
2006         switch (asp->mode) {
2007         case MODE_SET_COMMIT:
2008             retstatus = netsnmp_check_requests_status(asp,
2009                                                       asp->treecache[i].
2010                                                       requests_begin,
2011                                                       SNMP_ERR_COMMITFAILED);
2012             break;
2013
2014         case MODE_SET_UNDO:
2015             retstatus = netsnmp_check_requests_status(asp,
2016                                                       asp->treecache[i].
2017                                                       requests_begin,
2018                                                       SNMP_ERR_UNDOFAILED);
2019             break;
2020
2021         default:
2022             retstatus = netsnmp_check_requests_status(asp,
2023                                                       asp->treecache[i].
2024                                                       requests_begin, 0);
2025             break;
2026         }
2027
2028         /*
2029          * always take lowest varbind if possible 
2030          */
2031         if (retstatus != SNMP_ERR_NOERROR) {
2032             status = retstatus;
2033         }
2034
2035         /*
2036          * other things we know less about (no index) 
2037          */
2038         /*
2039          * WWW: drop support for this? 
2040          */
2041         if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
2042             /*
2043              * we can't break here, since some processing needs to be
2044              * done for all requests anyway (IE, SET handling for UNDO
2045              * needs to be called regardless of previous status
2046              * results.
2047              * WWW:  This should be predictable though and
2048              * breaking should be possible in some cases (eg GET,
2049              * GETNEXT, ...) 
2050              */
2051             final_status = status;
2052         }
2053     }
2054
2055     return final_status;
2056 }
2057
2058 /*
2059  * loop through our sessions known delegated sessions and check to see
2060  * if they've completed yet. If there are no more delegated sessions,
2061  * check for and process any queued requests
2062  */
2063 void
2064 netsnmp_check_outstanding_agent_requests(void)
2065 {
2066     netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
2067
2068     /*
2069      * deal with delegated requests
2070      */
2071     for (asp = agent_delegated_list; asp; prev_asp = asp, asp = next_asp) {
2072         next_asp = asp->next;   /* save in case we clean up asp */
2073         if (!netsnmp_check_for_delegated(asp)) {
2074
2075             /*
2076              * we're done with this one, remove from queue 
2077              */
2078             if (prev_asp != NULL)
2079                 prev_asp->next = asp->next;
2080             else
2081                 agent_delegated_list = asp->next;
2082
2083             /*
2084              * check request status
2085              */
2086             netsnmp_check_all_requests_status(asp, 0);
2087             
2088             /*
2089              * continue processing or finish up 
2090              */
2091             check_delayed_request(asp);
2092         }
2093     }
2094
2095     /*
2096      * if we are processing a set and there are more delegated
2097      * requests, keep waiting before getting to queued requests.
2098      */
2099     if (netsnmp_processing_set && (NULL != agent_delegated_list))
2100         return;
2101
2102     while (netsnmp_agent_queued_list) {
2103
2104         /*
2105          * if we are processing a set, the first item better be
2106          * the set being (or waiting to be) processed.
2107          */
2108         netsnmp_assert((!netsnmp_processing_set) ||
2109                        (netsnmp_processing_set == netsnmp_agent_queued_list));
2110
2111         /*
2112          * if the top request is a set, don't pop it
2113          * off if there are delegated requests
2114          */
2115         if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
2116             (agent_delegated_list)) {
2117
2118             netsnmp_assert(netsnmp_processing_set == NULL);
2119
2120             netsnmp_processing_set = netsnmp_agent_queued_list;
2121             DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
2122                         "delegated requests finish, asp = %08p\n", asp));
2123             break;
2124         }
2125
2126         /*
2127          * pop the first request and process it
2128          */
2129         asp = netsnmp_agent_queued_list;
2130         netsnmp_agent_queued_list = asp->next;
2131         DEBUGMSGTL(("snmp_agent",
2132                     "processing queued request, asp = %08p\n", asp));
2133
2134         netsnmp_handle_request(asp, asp->status);
2135
2136         /*
2137          * if we hit a set, stop
2138          */
2139         if (NULL != netsnmp_processing_set)
2140             break;
2141     }
2142 }
2143
2144 /** Decide if the requested transaction_id is still being processed
2145    within the agent.  This is used to validate whether a delayed cache
2146    (containing possibly freed pointers) is still usable.
2147
2148    returns SNMPERR_SUCCESS if it's still valid, or SNMPERR_GENERR if not. */
2149 int
2150 netsnmp_check_transaction_id(int transaction_id)
2151 {
2152     netsnmp_agent_session *asp, *prev_asp = NULL;
2153
2154     for (asp = agent_delegated_list; asp; prev_asp = asp, asp = asp->next) {
2155         if (asp->pdu->transid == transaction_id)
2156             return SNMPERR_SUCCESS;
2157     }
2158     return SNMPERR_GENERR;
2159 }
2160
2161
2162 /*
2163  * check_delayed_request(asp)
2164  *
2165  * Called to rexamine a set of requests and continue processing them
2166  * once all the previous (delayed) requests have been handled one way
2167  * or another.
2168  */
2169
2170 int
2171 check_delayed_request(netsnmp_agent_session *asp)
2172 {
2173     int             status = SNMP_ERR_NOERROR;
2174
2175     DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %08p\n",
2176                 asp));
2177
2178     switch (asp->mode) {
2179     case SNMP_MSG_GETBULK:
2180     case SNMP_MSG_GETNEXT:
2181         netsnmp_check_all_requests_status(asp, 0);
2182         handle_getnext_loop(asp);
2183         if (netsnmp_check_for_delegated(asp) &&
2184             netsnmp_check_transaction_id(asp->pdu->transid) !=
2185             SNMPERR_SUCCESS) {
2186             /*
2187              * add to delegated request chain 
2188              */
2189             if (!netsnmp_check_delegated_chain_for(asp)) {
2190                 asp->next = agent_delegated_list;
2191                 agent_delegated_list = asp;
2192             }
2193         }
2194         break;
2195
2196     case MODE_SET_COMMIT:
2197         netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
2198         goto settop;
2199
2200     case MODE_SET_UNDO:
2201         netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
2202         goto settop;
2203
2204     case MODE_SET_BEGIN:
2205     case MODE_SET_RESERVE1:
2206     case MODE_SET_RESERVE2:
2207     case MODE_SET_ACTION:
2208     case MODE_SET_FREE:
2209       settop:
2210         handle_set_loop(asp);
2211         if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2212
2213             if (netsnmp_check_for_delegated_and_add(asp)) {
2214                 /*
2215                  * add to delegated request chain 
2216                  */
2217                 if (!asp->status)
2218                     asp->status = status;
2219             }
2220
2221             return SNMP_ERR_NOERROR;
2222         }
2223         break;
2224
2225     default:
2226         break;
2227     }
2228
2229     /*
2230      * if we don't have anything outstanding (delegated), wrap up 
2231      */
2232     if (!netsnmp_check_for_delegated(asp))
2233         return netsnmp_wrap_up_request(asp, status);
2234
2235     return 1;
2236 }
2237
2238 /** returns 1 if there are valid GETNEXT requests left.  Returns 0 if not. */
2239 int
2240 check_getnext_results(netsnmp_agent_session *asp)
2241 {
2242     /*
2243      * get old info 
2244      */
2245     netsnmp_tree_cache *old_treecache = asp->treecache;
2246     int             old_treecache_num = asp->treecache_num;
2247     int             count = 0;
2248     int             i, special = 0;
2249     netsnmp_request_info *request;
2250
2251     if (asp->mode == SNMP_MSG_GET) {
2252         /*
2253          * Special case for doing INCLUSIVE getNext operations in
2254          * AgentX subagents.  
2255          */
2256         DEBUGMSGTL(("snmp_agent",
2257                     "asp->mode == SNMP_MSG_GET in ch_getnext\n"));
2258         asp->mode = asp->oldmode;
2259         special = 1;
2260     }
2261
2262     for (i = 0; i <= old_treecache_num; i++) {
2263         for (request = old_treecache[i].requests_begin; request;
2264              request = request->next) {
2265
2266             /*
2267              * If we have just done the special case AgentX GET, then any
2268              * requests which were not INCLUSIVE will now have a wrong
2269              * response, so junk them and retry from the same place (except
2270              * that this time the handler will be called in "inexact"
2271              * mode).  
2272              */
2273
2274             if (special) {
2275                 if (!request->inclusive) {
2276                     DEBUGMSGTL(("snmp_agent",
2277                                 "request %d wasn't inclusive\n",
2278                                 request->index));
2279                     snmp_set_var_typed_value(request->requestvb,
2280                                              ASN_PRIV_RETRY, NULL, 0);
2281                 } else if (request->requestvb->type == ASN_NULL ||
2282                            request->requestvb->type == SNMP_NOSUCHINSTANCE ||
2283                            request->requestvb->type == SNMP_NOSUCHOBJECT) {
2284                     /*
2285                      * it was inclusive, but no results.  Still retry this
2286                      * search. 
2287                      */
2288                     snmp_set_var_typed_value(request->requestvb,
2289                                              ASN_PRIV_RETRY, NULL, 0);
2290                 }
2291             }
2292
2293             /*
2294              * out of range? 
2295              */
2296             if (snmp_oid_compare(request->requestvb->name,
2297                                  request->requestvb->name_length,
2298                                  request->range_end,
2299                                  request->range_end_len) >= 0) {
2300                 /*
2301                  * ack, it's beyond the accepted end of range. 
2302                  */
2303                 /*
2304                  * fix it by setting the oid to the end of range oid instead 
2305                  */
2306                 DEBUGMSGTL(("check_getnext_results",
2307                             "request response %d out of range\n",
2308                             request->index));
2309                 request->inclusive = 1;
2310                 /*
2311                  * XXX: should set this to the original OID? 
2312                  */
2313                 snmp_set_var_objid(request->requestvb,
2314                                    request->range_end,
2315                                    request->range_end_len);
2316                 snmp_set_var_typed_value(request->requestvb, ASN_NULL,
2317                                          NULL, 0);
2318             }
2319
2320             /*
2321              * mark any existent requests with illegal results as NULL 
2322              */
2323             if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
2324                 /*
2325                  * illegal response from a subagent.  Change it back to NULL 
2326                  */
2327                 request->requestvb->type = ASN_NULL;
2328                 request->inclusive = 1;
2329             }
2330
2331             if (request->requestvb->type == ASN_NULL ||
2332                 request->requestvb->type == ASN_PRIV_RETRY ||
2333                 (asp->reqinfo->mode == MODE_GETBULK
2334                  && request->repeat > 0))
2335                 count++;
2336         }
2337     }
2338     return count;
2339 }
2340
2341 /** repeatedly calls getnext handlers looking for an answer till all
2342    requests are satisified.  It's expected that one pass has been made
2343    before entering this function */
2344 int
2345 handle_getnext_loop(netsnmp_agent_session *asp)
2346 {
2347     int             status;
2348     netsnmp_variable_list *var_ptr;
2349
2350     /*
2351      * loop 
2352      */
2353     while (1) {
2354
2355         /*
2356          * bail for now if anything is delegated. 
2357          */
2358         if (netsnmp_check_for_delegated(asp)) {
2359             return SNMP_ERR_NOERROR;
2360         }
2361
2362         /*
2363          * check vacm against results 
2364          */
2365         check_acm(asp, ASN_PRIV_RETRY);
2366
2367         /*
2368          * need to keep going we're not done yet. 
2369          */
2370         if (!check_getnext_results(asp))
2371             /*
2372              * nothing left, quit now 
2373              */
2374             break;
2375
2376         /*
2377          * never had a request (empty pdu), quit now 
2378          */
2379         /*
2380          * XXXWWW: huh?  this would be too late, no?  shouldn't we
2381          * catch this earlier? 
2382          */
2383         /*
2384          * if (count == 0)
2385          * break; 
2386          */
2387
2388         DEBUGIF("results") {
2389             DEBUGMSGTL(("results",
2390                         "getnext results, before next pass:\n"));
2391             for (var_ptr = asp->pdu->variables; var_ptr;
2392                  var_ptr = var_ptr->next_variable) {
2393                 DEBUGMSGTL(("results", "\t"));
2394                 DEBUGMSGVAR(("results", var_ptr));
2395                 DEBUGMSG(("results", "\n"));
2396             }
2397         }
2398
2399         netsnmp_reassign_requests(asp);
2400         status = handle_var_requests(asp);
2401         if (status != SNMP_ERR_NOERROR) {
2402             return status;      /* should never really happen */
2403         }
2404     }
2405     return SNMP_ERR_NOERROR;
2406 }
2407
2408 int
2409 handle_set(netsnmp_agent_session *asp)
2410 {
2411     int             status;
2412     /*
2413      * SETS require 3-4 passes through the var_op_list.
2414      * The first two
2415      * passes verify that all types, lengths, and values are valid
2416      * and may reserve resources and the third does the set and a
2417      * fourth executes any actions.  Then the identical GET RESPONSE
2418      * packet is returned.
2419      * If either of the first two passes returns an error, another
2420      * pass is made so that any reserved resources can be freed.
2421      * If the third pass returns an error, another pass is
2422      * made so that
2423      * any changes can be reversed.
2424      * If the fourth pass (or any of the error handling passes)
2425      * return an error, we'd rather not know about it!
2426      */
2427     if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
2428         switch (asp->mode) {
2429         case MODE_SET_BEGIN:
2430             snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
2431             asp->rw = WRITE;    /* WWW: still needed? */
2432             asp->mode = MODE_SET_RESERVE1;
2433             asp->status = SNMP_ERR_NOERROR;
2434             break;
2435
2436         case MODE_SET_RESERVE1:
2437
2438             if (asp->status != SNMP_ERR_NOERROR)
2439                 asp->mode = MODE_SET_FREE;
2440             else
2441                 asp->mode = MODE_SET_RESERVE2;
2442             break;
2443
2444         case MODE_SET_RESERVE2:
2445             if (asp->status != SNMP_ERR_NOERROR)
2446                 asp->mode = MODE_SET_FREE;
2447             else
2448                 asp->mode = MODE_SET_ACTION;
2449             break;
2450
2451         case MODE_SET_ACTION:
2452             if (asp->status != SNMP_ERR_NOERROR)
2453                 asp->mode = MODE_SET_UNDO;
2454             else
2455                 asp->mode = MODE_SET_COMMIT;
2456             break;
2457
2458         case MODE_SET_COMMIT:
2459             if (asp->status != SNMP_ERR_NOERROR) {
2460                 asp->mode = FINISHED_FAILURE;
2461             } else {
2462                 asp->mode = FINISHED_SUCCESS;
2463             }
2464             break;
2465
2466         case MODE_SET_UNDO:
2467             asp->mode = FINISHED_FAILURE;
2468             break;
2469
2470         case MODE_SET_FREE:
2471             asp->mode = FINISHED_FAILURE;
2472             break;
2473         }
2474     }
2475
2476     if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2477         DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
2478                     se_find_label_in_slist("agent_mode", asp->mode)));
2479         status = handle_var_requests(asp);
2480         DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
2481                     asp->mode, status));
2482         if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
2483             status == SNMP_ERR_COMMITFAILED || 
2484             status == SNMP_ERR_UNDOFAILED) {
2485             asp->status = status;
2486         }
2487     }
2488     return asp->status;
2489 }
2490
2491 int
2492 handle_set_loop(netsnmp_agent_session *asp)
2493 {
2494     while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
2495         handle_set(asp);
2496         if (netsnmp_check_for_delegated(asp)) {
2497             return SNMP_ERR_NOERROR;
2498         }
2499         if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
2500             return asp->status;
2501         }
2502     }
2503     return asp->status;
2504 }
2505
2506 int
2507 netsnmp_handle_request(netsnmp_agent_session *asp, int status)
2508 {
2509     /*
2510      * if this isn't a delegated request trying to finish,
2511      * processing of a set request should not start until all
2512      * delegated requests have completed, and no other new requests
2513      * should be processed until the set request completes.
2514      */
2515     if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
2516         (asp != netsnmp_processing_set)) {
2517         /*
2518          * if we are processing a set and this is not a delegated
2519          * request, queue the request
2520          */
2521         if (netsnmp_processing_set) {
2522             netsnmp_add_queued(asp);
2523             DEBUGMSGTL(("snmp_agent",
2524                         "request queued while processing set, "
2525                         "asp = %08p\n", asp));
2526             return 1;
2527         }
2528
2529         /*
2530          * check for set request
2531          */
2532         if (asp->pdu->command == SNMP_MSG_SET) {
2533             netsnmp_processing_set = asp;
2534
2535             /*
2536              * if there are delegated requests, we must wait for them
2537              * to finishd.
2538              */
2539             if (agent_delegated_list) {
2540                 DEBUGMSGTL(("snmp_agent", "SET request queued while "
2541                             "delegated requests finish, asp = %08p\n",
2542                             asp));
2543                 netsnmp_add_queued(asp);
2544                 return 1;
2545             }
2546         }
2547     }
2548
2549     /*
2550      * process the request 
2551      */
2552     status = handle_pdu(asp);
2553
2554     /*
2555      * print the results in appropriate debugging mode 
2556      */
2557     DEBUGIF("results") {
2558         netsnmp_variable_list *var_ptr;
2559         DEBUGMSGTL(("results", "request results (status = %d):\n",
2560                     status));
2561         for (var_ptr = asp->pdu->variables; var_ptr;
2562              var_ptr = var_ptr->next_variable) {
2563             DEBUGMSGTL(("results", "\t"));
2564             DEBUGMSGVAR(("results", var_ptr));
2565             DEBUGMSG(("results", "\n"));
2566         }
2567     }
2568
2569     /*
2570      * check for uncompleted requests 
2571      */
2572     if (netsnmp_check_for_delegated_and_add(asp)) {
2573         /*
2574          * add to delegated request chain 
2575          */
2576         asp->status = status;
2577     } else {
2578         /*
2579          * if we don't have anything outstanding (delegated), wrap up
2580          */
2581         return netsnmp_wrap_up_request(asp, status);
2582     }
2583
2584     return 1;
2585 }
2586
2587 int
2588 handle_pdu(netsnmp_agent_session *asp)
2589 {
2590     int             status, inclusives = 0;
2591     netsnmp_variable_list *v = NULL;
2592
2593     /*
2594      * for illegal requests, mark all nodes as ASN_NULL 
2595      */
2596     switch (asp->pdu->command) {
2597
2598     case SNMP_MSG_INTERNAL_SET_RESERVE2:
2599     case SNMP_MSG_INTERNAL_SET_ACTION:
2600     case SNMP_MSG_INTERNAL_SET_COMMIT:
2601     case SNMP_MSG_INTERNAL_SET_FREE:
2602     case SNMP_MSG_INTERNAL_SET_UNDO:
2603         status = get_set_cache(asp);
2604         if (status != SNMP_ERR_NOERROR)
2605             return status;
2606         break;
2607
2608     case SNMP_MSG_GET:
2609     case SNMP_MSG_GETNEXT:
2610     case SNMP_MSG_GETBULK:
2611         for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
2612             if (v->type == ASN_PRIV_INCL_RANGE) {
2613                 /*
2614                  * Leave the type for now (it gets set to
2615                  * ASN_NULL in netsnmp_add_varbind_to_cache,
2616                  * called by create_subnetsnmp_tree_cache below).
2617                  * If we set it to ASN_NULL now, we wouldn't be
2618                  * able to distinguish INCLUSIVE search
2619                  * ranges.  
2620                  */
2621                 inclusives++;
2622             } else {
2623                 snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
2624             }
2625         }
2626         /*
2627          * fall through 
2628          */
2629
2630     case SNMP_MSG_INTERNAL_SET_BEGIN:
2631     case SNMP_MSG_INTERNAL_SET_RESERVE1:
2632     default:
2633         asp->vbcount = count_varbinds(asp->pdu->variables);
2634         if (asp->vbcount) /* efence doesn't like 0 size allocs */
2635             asp->requests = (netsnmp_request_info *)
2636                 calloc(asp->vbcount, sizeof(netsnmp_request_info));
2637         /*
2638          * collect varbinds 
2639          */
2640         status = netsnmp_create_subtree_cache(asp);
2641         if (status != SNMP_ERR_NOERROR)
2642             return status;
2643     }
2644
2645     asp->mode = asp->pdu->command;
2646     switch (asp->mode) {
2647     case SNMP_MSG_GET:
2648         /*
2649          * increment the message type counter 
2650          */
2651         snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
2652
2653         /*
2654          * check vacm ahead of time 
2655          */
2656         check_acm(asp, SNMP_NOSUCHOBJECT);
2657
2658         /*
2659          * get the results 
2660          */
2661         status = handle_var_requests(asp);
2662
2663         /*
2664          * Deal with unhandled results -> noSuchInstance (rather
2665          * than noSuchObject -- in that case, the type will
2666          * already have been set to noSuchObject when we realised
2667          * we couldn't find an appropriate tree).  
2668          */
2669         if (status == SNMP_ERR_NOERROR)
2670             snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
2671                                    SNMP_NOSUCHINSTANCE);
2672         break;
2673
2674     case SNMP_MSG_GETNEXT:
2675         snmp_increment_statistic(STAT_SNMPINGETNEXTS);
2676         /*
2677          * fall through 
2678          */
2679
2680     case SNMP_MSG_GETBULK:     /* note: there is no getbulk stat */
2681         /*
2682          * loop through our mib tree till we find an
2683          * appropriate response to return to the caller. 
2684          */
2685
2686         if (inclusives) {
2687             /*
2688              * This is a special case for AgentX INCLUSIVE getNext
2689              * requests where a result lexi-equal to the request is okay
2690              * but if such a result does not exist, we still want the
2691              * lexi-next one.  So basically we do a GET first, and if any
2692              * of the INCLUSIVE requests are satisfied, we use that
2693              * value.  Then, unsatisfied INCLUSIVE requests, and
2694              * non-INCLUSIVE requests get done as normal.  
2695              */
2696
2697             DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
2698             asp->oldmode = asp->mode;
2699             asp->mode = SNMP_MSG_GET;
2700         }
2701
2702         /*
2703          * first pass 
2704          */
2705         status = handle_var_requests(asp);
2706         if (status != SNMP_ERR_NOERROR) {
2707             if (!inclusives)
2708                 return status;  /* should never really happen */
2709             else
2710                 asp->status = SNMP_ERR_NOERROR;
2711         }
2712
2713         /*
2714          * loop through our mib tree till we find an
2715          * appropriate response to return to the caller. 
2716          */
2717
2718         status = handle_getnext_loop(asp);
2719         break;
2720
2721     case SNMP_MSG_SET:
2722         /*
2723          * check access permissions first 
2724          */
2725         if (check_acm(asp, SNMP_NOSUCHOBJECT))
2726             return SNMP_ERR_NOTWRITABLE;
2727
2728         asp->mode = MODE_SET_BEGIN;
2729         status = handle_set_loop(asp);
2730
2731         break;
2732
2733     case SNMP_MSG_INTERNAL_SET_BEGIN:
2734     case SNMP_MSG_INTERNAL_SET_RESERVE1:
2735     case SNMP_MSG_INTERNAL_SET_RESERVE2:
2736     case SNMP_MSG_INTERNAL_SET_ACTION:
2737     case SNMP_MSG_INTERNAL_SET_COMMIT:
2738     case SNMP_MSG_INTERNAL_SET_FREE:
2739     case SNMP_MSG_INTERNAL_SET_UNDO:
2740         asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
2741         status = handle_set_loop(asp);
2742         /*
2743          * asp related cache is saved in cleanup 
2744          */
2745         break;
2746
2747     case SNMP_MSG_RESPONSE:
2748         snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
2749         return SNMP_ERR_NOERROR;
2750
2751     case SNMP_MSG_TRAP:
2752     case SNMP_MSG_TRAP2:
2753         snmp_increment_statistic(STAT_SNMPINTRAPS);
2754         return SNMP_ERR_NOERROR;
2755
2756     default:
2757         /*
2758          * WWW: are reports counted somewhere ? 
2759          */
2760         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
2761         return SNMPERR_GENERR;  /* shouldn't get here */
2762         /*
2763          * WWW 
2764          */
2765     }
2766     return status;
2767 }
2768
2769 int
2770 netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
2771                           netsnmp_request_info *request, int error_value)
2772 {
2773     if (!request || !reqinfo)
2774         return error_value;
2775
2776     return netsnmp_set_mode_request_error(reqinfo->mode, request,
2777                                           error_value);
2778 }
2779
2780 int
2781 netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
2782                                int error_value)
2783 {
2784     if (!request)
2785         return error_value;
2786
2787     request->processed = 1;
2788     request->delegated = REQUEST_IS_NOT_DELEGATED;
2789
2790     switch (error_value) {
2791     case SNMP_NOSUCHOBJECT:
2792     case SNMP_NOSUCHINSTANCE:
2793     case SNMP_ENDOFMIBVIEW:
2794         /*
2795          * these are exceptions that should be put in the varbind
2796          * in the case of a GET but should be translated for a SET
2797          * into a real error status code and put in the request 
2798          */
2799         switch (mode) {
2800         case MODE_GET:
2801             request->requestvb->type = error_value;
2802             return error_value;
2803
2804         case MODE_GETNEXT:
2805         case MODE_GETBULK:
2806             /*
2807              * ignore these.  They're illegal to set by the
2808              * client APIs for these modes 
2809              */
2810             snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
2811                      error_value, mode);
2812             return error_value;
2813
2814         default:
2815             request->status = SNMP_ERR_NOSUCHNAME;      /* WWW: correct? */
2816             return error_value;
2817         }
2818         break;                  /* never get here */
2819
2820     default:
2821         if (request->status < 0) {
2822             /*
2823              * illegal local error code.  translate to generr 
2824              */
2825             /*
2826              * WWW: full translation map? 
2827              */
2828             snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
2829                      error_value, SNMP_ERR_GENERR);
2830             request->status = SNMP_ERR_GENERR;
2831         } else {
2832             /*
2833              * WWW: translations and mode checking? 
2834              */
2835             request->status = error_value;
2836         }
2837         return error_value;
2838     }
2839     return error_value;
2840 }
2841
2842 int
2843 netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
2844                                netsnmp_request_info *requests,
2845                                int error_value)
2846 {
2847     while (requests) {
2848         netsnmp_set_request_error(reqinfo, requests, error_value);
2849         requests = requests->next;
2850     }
2851     return error_value;
2852 }
2853
2854 extern struct timeval starttime;
2855
2856                 /*
2857                  * Return the value of 'sysUpTime' at the given marker 
2858                  */
2859 u_long
2860 netsnmp_marker_uptime(marker_t pm)
2861 {
2862     u_long          res;
2863     marker_t        start = (marker_t) & starttime;
2864
2865     res = uatime_hdiff(start, pm);
2866     return res;                 /* atime_diff works in msec, not csec */
2867 }
2868
2869                         /*
2870                          * struct timeval equivalents of these 
2871                          */
2872 u_long
2873 netsnmp_timeval_uptime(struct timeval * tv)
2874 {
2875     return netsnmp_marker_uptime((marker_t) tv);
2876 }
2877
2878                 /*
2879                  * Return the current value of 'sysUpTime' 
2880                  */
2881 u_long
2882 netsnmp_get_agent_uptime(void)
2883 {
2884     struct timeval  now;
2885     gettimeofday(&now, NULL);
2886
2887     return netsnmp_timeval_uptime(&now);
2888 }
2889
2890
2891
2892 NETSNMP_INLINE void
2893 netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
2894                             netsnmp_data_list *node)
2895 {
2896     if (ari) {
2897         if (ari->agent_data) {
2898             netsnmp_add_list_data(&ari->agent_data, node);
2899         } else {
2900             ari->agent_data = node;
2901         }
2902     }
2903 }
2904
2905 NETSNMP_INLINE void    *
2906 netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
2907                             const char *name)
2908 {
2909     if (ari) {
2910         return netsnmp_get_list_data(ari->agent_data, name);
2911     }
2912     return NULL;
2913 }
2914
2915 NETSNMP_INLINE void
2916 netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
2917 {
2918     if (ari) {
2919         netsnmp_free_list_data(ari->agent_data);
2920     }
2921 }
2922
2923 NETSNMP_INLINE void
2924 netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
2925 {
2926     if (ari) {
2927         netsnmp_free_all_list_data(ari->agent_data);
2928     }
2929 }
2930
2931 NETSNMP_INLINE void
2932 netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
2933 {
2934     if (ari) {
2935         if (ari->agent_data) {
2936             netsnmp_free_all_list_data(ari->agent_data);
2937         }
2938         free(ari);
2939     }
2940 }