Revert "Revert "and added files""
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / 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 #if BRCM_SNMP_SUPPORT
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 #endif /* BRCM_SNMP_SUPPORT */
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 #ifdef BRCM_SNMP_SUPPORT
683     sp = snmp_add(s, t, netsnmp_agent_check_packet,
684                   netsnmp_agent_check_parse);
685 #else
686     sp = snmp_add(s, t, NULL,NULL);
687 #endif
688     if (sp == NULL) {
689         free(s);
690         free(n);
691         return -1;
692     }
693
694     isp = snmp_sess_pointer(sp);
695     if (isp == NULL) {          /*  over-cautious  */
696         free(s);
697         free(n);
698         return -1;
699     }
700
701     n->s = isp;
702     n->t = t;
703
704     if (main_session == NULL) {
705         main_session = snmp_sess_session(isp);
706     }
707
708     for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
709          a = a->next) {
710         handle = a->handle;
711         prevNext = &(a->next);
712     }
713
714     if (handle < INT_MAX) {
715         n->handle = handle + 1;
716         n->next = a;
717         *prevNext = n;
718         free(s);
719         return n->handle;
720     } else {
721         free(s);
722         free(n);
723         return -1;
724     }
725 }
726
727 void
728 netsnmp_deregister_agent_nsap(int handle)
729 {
730     agent_nsap     *a = NULL, **prevNext = &agent_nsap_list;
731     int             main_session_deregistered = 0;
732
733     DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
734
735     for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
736         prevNext = &(a->next);
737     }
738
739     if (a != NULL && a->handle == handle) {
740         *prevNext = a->next;
741         if (main_session == snmp_sess_session(a->s)) {
742             main_session_deregistered = 1;
743         }
744         snmp_close(snmp_sess_session(a->s));
745         /*
746          * The above free()s the transport and session pointers.  
747          */
748         free(a);
749     }
750
751     /*
752      * If we've deregistered the session that main_session used to point to,
753      * then make it point to another one, or in the last resort, make it equal
754      * to NULL.  Basically this shouldn't ever happen in normal operation
755      * because main_session starts off pointing at the first session added by
756      * init_master_agent(), which then discards the handle.  
757      */
758
759     if (main_session_deregistered) {
760         if (agent_nsap_list != NULL) {
761             DEBUGMSGTL(("snmp_agent",
762                         "WARNING: main_session ptr changed from %p to %p\n",
763                         main_session, snmp_sess_session(agent_nsap_list->s)));
764             main_session = snmp_sess_session(agent_nsap_list->s);
765         } else {
766             DEBUGMSGTL(("snmp_agent",
767                         "WARNING: main_session ptr changed from %p to NULL\n",
768                         main_session));
769             main_session = NULL;
770         }
771     }
772 }
773
774
775
776 /*
777  * 
778  * This function has been modified to use the experimental
779  * netsnmp_register_agent_nsap interface.  The major responsibility of this
780  * function now is to interpret a string specified to the agent (via -p on the
781  * command line, or from a configuration file) as a list of agent NSAPs on
782  * which to listen for SNMP packets.  Typically, when you add a new transport
783  * domain "foo", you add code here such that if the "foo" code is compiled
784  * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
785  * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
786  * transport descriptor.  netsnmp_register_agent_nsap is then called with that
787  * transport descriptor and sets up a listening agent session on it.
788  * 
789  * Everything then works much as normal: the agent runs in an infinite loop
790  * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
791  * is readable on any of the given transports.  This routine then traverses
792  * the library 'Sessions' list to identify the relevant session and eventually
793  * invokes '_sess_read'.  This then processes the incoming packet, calling the
794  * pre_parse, parse, post_parse and callback routines in turn.
795  * 
796  * JBPN 20001117
797  */
798
799 int
800 init_master_agent(void)
801 {
802     netsnmp_transport *transport;
803     char           *cptr;
804     char            buf[SPRINT_MAX_LEN];
805
806     /* default to turning off lookup caching */
807     netsnmp_set_lookup_cache_size(0);
808
809     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
810                                NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
811         DEBUGMSGTL(("snmp_agent",
812                     "init_master_agent; not master agent\n"));
813         return 0;               /*  No error if ! MASTER_AGENT  */
814     }
815 #ifdef USING_AGENTX_MASTER_MODULE
816     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
817                                NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
818         real_init_master();
819 #endif
820
821     /*
822      * Have specific agent ports been specified?  
823      */
824     cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
825                                  NETSNMP_DS_AGENT_PORTS);
826
827     if (cptr) {
828         snprintf(buf, sizeof(buf), "%s", cptr);
829         buf[ sizeof(buf)-1 ] = 0;
830     } else {
831         /*
832          * No, so just specify the default port.  
833          */
834         if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_FLAGS) & SNMP_FLAGS_STREAM_SOCKET) {
835             sprintf(buf, "tcp:%d", SNMP_PORT);
836         } else {
837             sprintf(buf, "udp:%d", SNMP_PORT);
838         }
839     }
840
841     DEBUGMSGTL(("snmp_agent", "final port spec: %s\n", buf));
842     cptr = strtok(buf, ",");
843     while (cptr) {
844         /*
845          * Specification format: 
846          * 
847          * NONE:                      (a pseudo-transport)
848          * UDP:[address:]port        (also default if no transport is specified)
849          * TCP:[address:]port         (if supported)
850          * Unix:pathname              (if supported)
851          * AAL5PVC:itf.vpi.vci        (if supported)
852          * IPX:[network]:node[/port] (if supported)
853          * 
854          */
855
856         DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
857                     cptr));
858
859         if (!cptr || !(*cptr)) {
860             snmp_log(LOG_ERR, "improper port specification\n");
861             return 1;
862         }
863
864         if (strncasecmp(cptr, "none", 4) == 0) {
865             DEBUGMSGTL(("snmp_agent",
866                         "init_master_agent; pseudo-transport \"none\" requested\n"));
867             return 0;
868         }
869         transport = netsnmp_tdomain_transport(cptr, 1, "udp");
870
871         if (transport == NULL) {
872             snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n",
873                      cptr);
874             return 1;
875         }
876
877         if (netsnmp_register_agent_nsap(transport) == 0) {
878             snmp_log(LOG_ERR,
879                      "Error registering specified transport \"%s\" as an agent NSAP\n",
880                      cptr);
881             return 1;
882         } else {
883             DEBUGMSGTL(("snmp_agent",
884                         "init_master_agent; \"%s\" registered as an agent NSAP\n",
885                         cptr));
886         }
887
888         /*
889          * Next transport please...  
890          */
891         cptr = strtok(NULL, ",");
892     }
893
894     return 0;
895 }
896
897
898
899 netsnmp_agent_session *
900 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
901 {
902     netsnmp_agent_session *asp = (netsnmp_agent_session *)
903         calloc(1, sizeof(netsnmp_agent_session));
904
905     if (asp == NULL) {
906         return NULL;
907     }
908
909     DEBUGMSGTL(("snmp_agent","agent_sesion %08p created\n", asp));
910     asp->session = session;
911     asp->pdu = snmp_clone_pdu(pdu);
912     asp->orig_pdu = snmp_clone_pdu(pdu);
913     asp->rw = READ;
914     asp->exact = TRUE;
915     asp->next = NULL;
916     asp->mode = RESERVE1;
917     asp->status = SNMP_ERR_NOERROR;
918     asp->index = 0;
919     asp->oldmode = 0;
920     asp->treecache_num = -1;
921     asp->treecache_len = 0;
922
923     return asp;
924 }
925
926 void
927 free_agent_snmp_session(netsnmp_agent_session *asp)
928 {
929     if (!asp)
930         return;
931
932     DEBUGMSGTL(("snmp_agent","agent_sesion %08p released\n", asp));
933
934     netsnmp_remove_from_delegated(asp);
935     
936     if (asp->orig_pdu)
937         snmp_free_pdu(asp->orig_pdu);
938     if (asp->pdu)
939         snmp_free_pdu(asp->pdu);
940     if (asp->reqinfo)
941         netsnmp_free_agent_request_info(asp->reqinfo);
942     if (asp->treecache) {
943         free(asp->treecache);
944     }
945     if (asp->bulkcache) {
946         free(asp->bulkcache);
947     }
948     if (asp->requests) {
949         int             i;
950         for (i = 0; i < asp->vbcount; i++) {
951             netsnmp_free_request_data_sets(&asp->requests[i]);
952         }
953     }
954     if (asp->requests) {
955         free(asp->requests);
956     }
957     if (asp->cache_store) {
958         netsnmp_free_cachemap(asp->cache_store);
959         asp->cache_store = NULL;
960     }
961     free(asp);
962 }
963
964 int
965 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
966 {
967     int             i;
968     netsnmp_request_info *request;
969
970     if (NULL == asp->treecache)
971         return 0;
972     
973     for (i = 0; i <= asp->treecache_num; i++) {
974         for (request = asp->treecache[i].requests_begin; request;
975              request = request->next) {
976             if (request->delegated)
977                 return 1;
978         }
979     }
980     return 0;
981 }
982
983 int
984 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
985 {
986     netsnmp_agent_session *asptmp;
987     for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
988         if (asptmp == asp)
989             return 1;
990     }
991     return 0;
992 }
993
994 int
995 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
996 {
997     if (netsnmp_check_for_delegated(asp)) {
998         if (!netsnmp_check_delegated_chain_for(asp)) {
999             /*
1000              * add to delegated request chain 
1001              */
1002             asp->next = agent_delegated_list;
1003             agent_delegated_list = asp;
1004             DEBUGMSGTL(("snmp_agent", "delegate session == %08p\n", asp));
1005         }
1006         return 1;
1007     }
1008     return 0;
1009 }
1010
1011 int
1012 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
1013 {
1014     netsnmp_agent_session *curr, *prev = NULL;
1015     
1016     for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
1017         /*
1018          * is this us?
1019          */
1020         if (curr != asp)
1021             continue;
1022         
1023         /*
1024          * remove from queue 
1025          */
1026         if (prev != NULL)
1027             prev->next = asp->next;
1028         else
1029             agent_delegated_list = asp->next;
1030
1031         DEBUGMSGTL(("snmp_agent", "remove delegated session == %08p\n", asp));
1032
1033         return 1;
1034     }
1035
1036     return 0;
1037 }
1038
1039 /*
1040  * netsnmp_remove_delegated_requests_for_session
1041  *
1042  * called when a session is being closed. Check all delegated requests to
1043  * see if the are waiting on this session, and if set, set the status for
1044  * that request to GENERR.
1045  */
1046 int
1047 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
1048 {
1049     netsnmp_agent_session *asp;
1050     int count = 0;
1051     
1052     for (asp = agent_delegated_list; asp; asp = asp->next) {
1053         /*
1054          * check each request
1055          */
1056         netsnmp_request_info *request;
1057         for(request = asp->requests; request; request = request->next) {
1058             /*
1059              * check session
1060              */
1061             netsnmp_assert(NULL!=request->subtree);
1062             if(request->subtree->session != sess)
1063                 continue;
1064
1065             /*
1066              * matched! mark request as done
1067              */
1068             netsnmp_set_mode_request_error(MODE_SET_BEGIN, request,
1069                                            SNMP_ERR_GENERR);
1070             ++count;
1071         }
1072     }
1073
1074     /*
1075      * if we found any, that request may be finished now
1076      */
1077     if(count) {
1078         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
1079                     "%08p\n", count, sess));
1080         netsnmp_check_outstanding_agent_requests();
1081     }
1082     
1083     return count;
1084 }
1085
1086 int
1087 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
1088 {
1089     netsnmp_agent_session *asptmp;
1090     for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
1091         if (asptmp == asp)
1092             return 1;
1093     }
1094     return 0;
1095 }
1096
1097 int
1098 netsnmp_add_queued(netsnmp_agent_session *asp)
1099 {
1100     netsnmp_agent_session *asp_tmp;
1101
1102     /*
1103      * first item?
1104      */
1105     if (NULL == netsnmp_agent_queued_list) {
1106         netsnmp_agent_queued_list = asp;
1107         return 1;
1108     }
1109
1110
1111     /*
1112      * add to end of queued request chain 
1113      */
1114     asp_tmp = netsnmp_agent_queued_list;
1115     for (; asp_tmp; asp_tmp = asp_tmp->next) {
1116         /*
1117          * already in queue?
1118          */
1119         if (asp_tmp == asp)
1120             break;
1121
1122         /*
1123          * end of queue?
1124          */
1125         if (NULL == asp_tmp->next)
1126             asp_tmp->next = asp;
1127     }
1128     return 1;
1129 }
1130
1131
1132 int
1133 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
1134 {
1135     netsnmp_variable_list *var_ptr;
1136     int             i, n = 0, r = 0;
1137
1138     /*
1139      * if this request was a set, clear the global now that we are
1140      * done.
1141      */
1142     if (asp == netsnmp_processing_set) {
1143         DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %08p\n",
1144                     asp));
1145         netsnmp_processing_set = NULL;
1146     }
1147
1148     /*
1149      * some stuff needs to be saved in special subagent cases 
1150      */
1151     if (asp->pdu) {
1152
1153         switch (asp->pdu->command) {
1154             case SNMP_MSG_INTERNAL_SET_BEGIN:
1155             case SNMP_MSG_INTERNAL_SET_RESERVE1:
1156             case SNMP_MSG_INTERNAL_SET_RESERVE2:
1157             case SNMP_MSG_INTERNAL_SET_ACTION:
1158                 save_set_cache(asp);
1159                 break;
1160         }
1161
1162         /*
1163          * if this is a GETBULK response we need to rearrange the varbinds 
1164          */
1165         if (asp->pdu->command == SNMP_MSG_GETBULK) {
1166             int             repeats = asp->pdu->errindex;
1167             int             j;
1168             
1169             if (asp->pdu->errstat < asp->vbcount) {
1170                 n = asp->pdu->errstat;
1171             } else {
1172                 n = asp->vbcount;
1173             }
1174             if ((r = asp->vbcount - n) < 0) {
1175                 r = 0;
1176             }
1177             
1178             for (i = 0; i < r - 1; i++) {
1179                 for (j = 0; j < repeats; j++) {
1180                     asp->bulkcache[i * repeats + j]->next_variable =
1181                         asp->bulkcache[(i + 1) * repeats + j];
1182                 }
1183             }
1184             if (r > 0) {
1185                 for (j = 0; j < repeats - 1; j++) {
1186                     asp->bulkcache[(r - 1) * repeats + j]->next_variable =
1187                         asp->bulkcache[j + 1];
1188                 }
1189             }
1190         }
1191
1192         /*
1193          * May need to "dumb down" a SET error status for a
1194          * v1 query.  See RFC2576 - section 4.3
1195          */
1196         if ((asp->pdu->command == SNMP_MSG_SET) &&
1197             (asp->pdu->version == SNMP_VERSION_1)) {
1198             switch (status) {
1199                 case SNMP_ERR_WRONGVALUE:
1200                 case SNMP_ERR_WRONGENCODING:
1201                 case SNMP_ERR_WRONGTYPE:
1202                 case SNMP_ERR_WRONGLENGTH:
1203                 case SNMP_ERR_INCONSISTENTVALUE:
1204                     status = SNMP_ERR_BADVALUE;
1205                     asp->status = SNMP_ERR_BADVALUE;
1206                     break;
1207                 case SNMP_ERR_NOACCESS:
1208                 case SNMP_ERR_NOTWRITABLE:
1209                 case SNMP_ERR_NOCREATION:
1210                 case SNMP_ERR_INCONSISTENTNAME:
1211                 case SNMP_ERR_AUTHORIZATIONERROR:
1212                     status = SNMP_ERR_NOSUCHNAME;
1213                     asp->status = SNMP_ERR_NOSUCHNAME;
1214                     break;
1215                 case SNMP_ERR_RESOURCEUNAVAILABLE:
1216                 case SNMP_ERR_COMMITFAILED:
1217                 case SNMP_ERR_UNDOFAILED:
1218                     status = SNMP_ERR_GENERR;
1219                     asp->status = SNMP_ERR_GENERR;
1220                     break;
1221             }
1222         }
1223         /*
1224          * Similarly we may need to "dumb down" v2 exception
1225          *  types to throw an error for a v1 query.
1226          *  See RFC2576 - section 4.1.2.3
1227          */
1228         if ((asp->pdu->command != SNMP_MSG_SET) &&
1229             (asp->pdu->version == SNMP_VERSION_1)) {
1230             for (var_ptr = asp->pdu->variables, i = 1;
1231                  var_ptr != NULL; var_ptr = var_ptr->next_variable, i++) {
1232                 switch (var_ptr->type) {
1233                     case SNMP_NOSUCHOBJECT:
1234                     case SNMP_NOSUCHINSTANCE:
1235                     case SNMP_ENDOFMIBVIEW:
1236                     case ASN_COUNTER64:
1237                         status = SNMP_ERR_NOSUCHNAME;
1238                         asp->status = SNMP_ERR_NOSUCHNAME;
1239                         asp->index = i;
1240                         break;
1241                 }
1242             }
1243         }
1244     } /** if asp->pdu */
1245
1246     /*
1247      * Update the snmp error-count statistics
1248      *   XXX - should we include the V2 errors in this or not?
1249      */
1250 #define INCLUDE_V2ERRORS_IN_V1STATS
1251
1252     switch (status) {
1253 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1254     case SNMP_ERR_WRONGVALUE:
1255     case SNMP_ERR_WRONGENCODING:
1256     case SNMP_ERR_WRONGTYPE:
1257     case SNMP_ERR_WRONGLENGTH:
1258     case SNMP_ERR_INCONSISTENTVALUE:
1259 #endif
1260     case SNMP_ERR_BADVALUE:
1261         snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
1262         break;
1263 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1264     case SNMP_ERR_NOACCESS:
1265     case SNMP_ERR_NOTWRITABLE:
1266     case SNMP_ERR_NOCREATION:
1267     case SNMP_ERR_INCONSISTENTNAME:
1268     case SNMP_ERR_AUTHORIZATIONERROR:
1269 #endif
1270     case SNMP_ERR_NOSUCHNAME:
1271         snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
1272         break;
1273 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
1274     case SNMP_ERR_RESOURCEUNAVAILABLE:
1275     case SNMP_ERR_COMMITFAILED:
1276     case SNMP_ERR_UNDOFAILED:
1277 #endif
1278     case SNMP_ERR_GENERR:
1279         snmp_increment_statistic(STAT_SNMPOUTGENERRS);
1280         break;
1281
1282     case SNMP_ERR_TOOBIG:
1283         snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
1284         break;
1285     }
1286
1287     if ((status == SNMP_ERR_NOERROR) && (asp->pdu)) {
1288         snmp_increment_statistic_by((asp->pdu->command == SNMP_MSG_SET ?
1289                                      STAT_SNMPINTOTALSETVARS :
1290                                      STAT_SNMPINTOTALREQVARS),
1291                                     count_varbinds(asp->pdu->variables));
1292     } else {
1293         /*
1294          * Use a copy of the original request
1295          *   to report failures.
1296          */
1297         snmp_free_pdu(asp->pdu);
1298         asp->pdu = asp->orig_pdu;
1299         asp->orig_pdu = NULL;
1300     }
1301     if (asp->pdu) {
1302         asp->pdu->command = SNMP_MSG_RESPONSE;
1303         asp->pdu->errstat = asp->status;
1304         asp->pdu->errindex = asp->index;
1305         if (!snmp_send(asp->session, asp->pdu)) {
1306             snmp_free_pdu(asp->pdu);
1307             asp->pdu = NULL;
1308         }
1309         snmp_increment_statistic(STAT_SNMPOUTPKTS);
1310         snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
1311         asp->pdu = NULL;
1312         netsnmp_remove_and_free_agent_snmp_session(asp);
1313     }
1314     return 1;
1315 }
1316
1317 void
1318 dump_sess_list(void)
1319 {
1320     netsnmp_agent_session *a;
1321
1322     DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
1323     for (a = agent_session_list; a != NULL; a = a->next) {
1324         DEBUGMSG(("snmp_agent", "%08p[session %08p] -> ", a, a->session));
1325     }
1326     DEBUGMSG(("snmp_agent", "[NIL]\n"));
1327 }
1328
1329 void
1330 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
1331 {
1332     netsnmp_agent_session *a, **prevNext = &agent_session_list;
1333
1334     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", asp));
1335
1336     for (a = agent_session_list; a != NULL; a = *prevNext) {
1337         if (a == asp) {
1338             *prevNext = a->next;
1339             a->next = NULL;
1340             free_agent_snmp_session(a);
1341             asp = NULL;
1342             break;
1343         } else {
1344             prevNext = &(a->next);
1345         }
1346     }
1347
1348     if (a == NULL && asp != NULL) {
1349         /*
1350          * We coulnd't find it on the list, so free it anyway.  
1351          */
1352         free_agent_snmp_session(asp);
1353     }
1354 }
1355
1356 void
1357 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
1358                                            void (*free_request)
1359                                            (netsnmp_request_list *))
1360 {
1361     netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
1362
1363     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", sess));
1364
1365     for (a = agent_session_list; a != NULL; a = next) {
1366         if (a->session == sess) {
1367             *prevNext = a->next;
1368             next = a->next;
1369             free_agent_snmp_session(a);
1370         } else {
1371             prevNext = &(a->next);
1372             next = a->next;
1373         }
1374     }
1375 }
1376
1377 /** handles an incoming SNMP packet into the agent */
1378 int
1379 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
1380                    netsnmp_pdu *pdu, void *magic)
1381 {
1382     netsnmp_agent_session *asp;
1383     int             status, access_ret, rc;
1384
1385     /*
1386      * We only support receiving here.  
1387      */
1388     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
1389         return 1;
1390     }
1391
1392     /*
1393      * RESPONSE messages won't get this far, but TRAP-like messages
1394      * might.  
1395      */
1396     if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
1397         pdu->command == SNMP_MSG_TRAP2) {
1398         DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
1399                     pdu->command));
1400         pdu->command = SNMP_MSG_TRAP2;
1401         snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
1402         return 1;
1403     }
1404
1405     if (magic == NULL) {
1406         asp = init_agent_snmp_session(session, pdu);
1407         status = SNMP_ERR_NOERROR;
1408     } else {
1409         asp = (netsnmp_agent_session *) magic;
1410         status = asp->status;
1411     }
1412
1413     if ((access_ret = check_access(pdu)) != 0) {
1414         if (access_ret == VACM_NOSUCHCONTEXT) {
1415             /*
1416              * rfc2573 section 3.2, step 5 says that we increment the
1417              * counter but don't return a response of any kind 
1418              */
1419
1420             /*
1421              * we currently don't support unavailable contexts, as
1422              * there is no reason to that I currently know of 
1423              */
1424             snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
1425
1426             /*
1427              * drop the request 
1428              */
1429             netsnmp_remove_and_free_agent_snmp_session(asp);
1430             return 0;
1431         } else {
1432             /*
1433              * access control setup is incorrect 
1434              */
1435             send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
1436             if (asp->pdu->version != SNMP_VERSION_1
1437                 && asp->pdu->version != SNMP_VERSION_2c) {
1438                 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
1439                 asp->pdu->command = SNMP_MSG_RESPONSE;
1440                 snmp_increment_statistic(STAT_SNMPOUTPKTS);
1441                 if (!snmp_send(asp->session, asp->pdu))
1442                     snmp_free_pdu(asp->pdu);
1443                 asp->pdu = NULL;
1444                 netsnmp_remove_and_free_agent_snmp_session(asp);
1445                 return 1;
1446             } else {
1447                 /*
1448                  * drop the request 
1449                  */
1450                 netsnmp_remove_and_free_agent_snmp_session(asp);
1451                 return 0;
1452             }
1453         }
1454     }
1455
1456     rc = netsnmp_handle_request(asp, status);
1457
1458     /*
1459      * done 
1460      */
1461     DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %08p\n",
1462                 asp));
1463     return rc;
1464 }
1465
1466 netsnmp_request_info *
1467 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
1468                              netsnmp_variable_list * varbind_ptr,
1469                              netsnmp_subtree *tp)
1470 {
1471     netsnmp_request_info *request = NULL;
1472     int             cacheid;
1473
1474     DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
1475     DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
1476                  varbind_ptr->name_length));
1477     DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
1478
1479     if (tp &&
1480         (asp->pdu->command == SNMP_MSG_GETNEXT ||
1481          asp->pdu->command == SNMP_MSG_GETBULK)) {
1482         int result;
1483         int prefix_len;
1484
1485         prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1486                                              tp->start_len,
1487                                              tp->end_a, tp->end_len);
1488         result =
1489             netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
1490
1491         while (result == VACM_NOTINVIEW) {
1492             /* the entire subtree is not in view. Skip it. */
1493             /** @todo make this be more intelligent about ranges.
1494                 Right now we merely take the highest level
1495                 commonality of a registration range and use that.
1496                 At times we might be able to be smarter about
1497                 checking the range itself as opposed to the node
1498                 above where the range exists, but I doubt this will
1499                 come up all that frequently. */
1500             tp = tp->next;
1501             if (tp) {
1502                 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
1503                                                      tp->start_len,
1504                                                      tp->end_a,
1505                                                      tp->end_len);
1506                 result =
1507                     netsnmp_acm_check_subtree(asp->pdu,
1508                                               tp->start_a, prefix_len);
1509             }
1510         }
1511     }
1512     if (tp == NULL) {
1513         /*
1514          * no appropriate registration found 
1515          */
1516         /*
1517          * make up the response ourselves 
1518          */
1519         switch (asp->pdu->command) {
1520         case SNMP_MSG_GETNEXT:
1521         case SNMP_MSG_GETBULK:
1522             varbind_ptr->type = SNMP_ENDOFMIBVIEW;
1523             break;
1524
1525         case SNMP_MSG_SET:
1526             varbind_ptr->type = SNMP_NOSUCHOBJECT;
1527             break;
1528
1529         case SNMP_MSG_GET:
1530             varbind_ptr->type = SNMP_NOSUCHOBJECT;
1531             break;
1532
1533         default:
1534             return NULL;        /* shouldn't get here */
1535         }
1536     } else {
1537         DEBUGMSGTL(("snmp_agent", "tp->start "));
1538         DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
1539         DEBUGMSG(("snmp_agent", ", tp->end "));
1540         DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
1541         DEBUGMSG(("snmp_agent", ", \n"));
1542
1543         /*
1544          * malloc the request structure 
1545          */
1546         request = &(asp->requests[vbcount - 1]);
1547         request->index = vbcount;
1548         request->delegated = 0;
1549         request->processed = 0;
1550         request->status = 0;
1551         request->subtree = tp;
1552         if (request->parent_data) {
1553             netsnmp_free_request_data_sets(request);
1554         }
1555
1556         /*
1557          * for non-SET modes, set the type to NULL 
1558          */
1559         if (!MODE_IS_SET(asp->pdu->command)) {
1560             if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
1561                 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
1562                             request->index));
1563                 request->inclusive = 1;
1564             }
1565             varbind_ptr->type = ASN_NULL;
1566         }
1567
1568         /*
1569          * place them in a cache 
1570          */
1571         if (tp->global_cacheid) {
1572             /*
1573              * we need to merge all marked subtrees together 
1574              */
1575             if (asp->cache_store && -1 !=
1576                 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
1577                                                     tp->global_cacheid))) {
1578             } else {
1579                 cacheid = ++(asp->treecache_num);
1580                 netsnmp_get_or_add_local_cachid(&asp->cache_store,
1581                                                 tp->global_cacheid,
1582                                                 cacheid);
1583                 goto mallocslot;        /* XXX: ick */
1584             }
1585         } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
1586                    asp->treecache[tp->cacheid].subtree == tp) {
1587             /*
1588              * we have already added a request to this tree
1589              * pointer before 
1590              */
1591             cacheid = tp->cacheid;
1592         } else {
1593             cacheid = ++(asp->treecache_num);
1594           mallocslot:
1595             /*
1596              * new slot needed 
1597              */
1598             if (asp->treecache_num >= asp->treecache_len) {
1599                 /*
1600                  * exapand cache array 
1601                  */
1602                 /*
1603                  * WWW: non-linear expansion needed (with cap) 
1604                  */
1605 #define CACHE_GROW_SIZE 16
1606                 asp->treecache_len =
1607                     (asp->treecache_len + CACHE_GROW_SIZE);
1608                 asp->treecache =
1609                     realloc(asp->treecache,
1610                             sizeof(netsnmp_tree_cache) *
1611                             asp->treecache_len);
1612                 if (asp->treecache == NULL)
1613                     return NULL;
1614                 memset(&(asp->treecache[cacheid]), 0x00,
1615                        sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
1616             }
1617             asp->treecache[cacheid].subtree = tp;
1618             asp->treecache[cacheid].requests_begin = request;
1619             tp->cacheid = cacheid;
1620         }
1621
1622         /*
1623          * if this is a search type, get the ending range oid as well 
1624          */
1625         if (asp->pdu->command == SNMP_MSG_GETNEXT ||
1626             asp->pdu->command == SNMP_MSG_GETBULK) {
1627             request->range_end = tp->end_a;
1628             request->range_end_len = tp->end_len;
1629         } else {
1630             request->range_end = NULL;
1631             request->range_end_len = 0;
1632         }
1633
1634         /*
1635          * link into chain 
1636          */
1637         if (asp->treecache[cacheid].requests_end)
1638             asp->treecache[cacheid].requests_end->next = request;
1639         request->next = NULL;
1640         request->prev = asp->treecache[cacheid].requests_end;
1641         asp->treecache[cacheid].requests_end = request;
1642
1643         /*
1644          * add the given request to the list of requests they need
1645          * to handle results for 
1646          */
1647         request->requestvb = varbind_ptr;
1648     }
1649     return request;
1650 }
1651
1652 /*
1653  * check the ACM(s) for the results on each of the varbinds.
1654  * If ACM disallows it, replace the value with type
1655  * 
1656  * Returns number of varbinds with ACM errors
1657  */
1658 int
1659 check_acm(netsnmp_agent_session *asp, u_char type)
1660 {
1661     int             view;
1662     int             i;
1663     netsnmp_request_info *request;
1664     int             ret = 0;
1665     netsnmp_variable_list *vb;
1666
1667     for (i = 0; i <= asp->treecache_num; i++) {
1668         for (request = asp->treecache[i].requests_begin;
1669              request; request = request->next) {
1670             /*
1671              * for each request, run it through in_a_view() 
1672              */
1673             vb = request->requestvb;
1674             if (vb->type == ASN_NULL)   /* not yet processed */
1675                 continue;
1676             view =
1677                 in_a_view(vb->name, &vb->name_length, asp->pdu, vb->type);
1678
1679             /*
1680              * if a ACM error occurs, mark it as type passed in 
1681              */
1682             if (view != VACM_SUCCESS) {
1683                 ret++;
1684                 snmp_set_var_typed_value(vb, type, NULL, 0);
1685             }
1686         }
1687     }
1688     return ret;
1689 }
1690
1691
1692 int
1693 netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
1694 {
1695     netsnmp_subtree *tp;
1696     netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
1697     int             view;
1698     int             vbcount = 0;
1699     int             bulkcount = 0, bulkrep = 0;
1700     int             i = 0, n = 0, r = 0;
1701     netsnmp_request_info *request;
1702
1703     if (asp->treecache == NULL && asp->treecache_len == 0) {
1704         asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
1705         asp->treecache =
1706             calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
1707         if (asp->treecache == NULL)
1708             return SNMP_ERR_GENERR;
1709     }
1710     asp->treecache_num = -1;
1711
1712     if (asp->pdu->command == SNMP_MSG_GETBULK) {
1713         /*
1714          * getbulk prep 
1715          */
1716         int             count = count_varbinds(asp->pdu->variables);
1717
1718         if (asp->pdu->errstat < 0) {
1719             asp->pdu->errstat = 0;
1720         }
1721         if (asp->pdu->errindex < 0) {
1722             asp->pdu->errindex = 0;
1723         }
1724
1725         if (asp->pdu->errstat < count) {
1726             n = asp->pdu->errstat;
1727         } else {
1728             n = count;
1729         }
1730         if ((r = count - n) < 0) {
1731             r = 0;
1732             asp->bulkcache = NULL;
1733         } else {
1734             asp->bulkcache =
1735                 (netsnmp_variable_list **) malloc(asp->pdu->errindex * r *
1736                                                   sizeof(struct
1737                                                          varbind_list *));
1738         }
1739         DEBUGMSGTL(("snmp_agent", "GETBULK N = %d, M = %d, R = %d\n",
1740                     n, asp->pdu->errindex, r));
1741     }
1742
1743     /*
1744      * collect varbinds into their registered trees 
1745      */
1746     prevNext = &(asp->pdu->variables);
1747     for (varbind_ptr = asp->pdu->variables; varbind_ptr;
1748          varbind_ptr = vbsave) {
1749
1750         /*
1751          * getbulk mess with this pointer, so save it 
1752          */
1753         vbsave = varbind_ptr->next_variable;
1754
1755         if (asp->pdu->command == SNMP_MSG_GETBULK) {
1756             if (n > 0) {
1757                 n--;
1758             } else {
1759                 /*
1760                  * repeate request varbinds on GETBULK.  These will
1761                  * have to be properly rearranged later though as
1762                  * responses are supposed to actually be interlaced
1763                  * with each other.  This is done with the asp->bulkcache. 
1764                  */
1765                 bulkrep = asp->pdu->errindex - 1;
1766                 if (asp->pdu->errindex > 0) {
1767                     vbptr = varbind_ptr;
1768                     asp->bulkcache[bulkcount++] = vbptr;
1769
1770                     for (i = 1; i < asp->pdu->errindex; i++) {
1771                         vbptr->next_variable =
1772                             SNMP_MALLOC_STRUCT(variable_list);
1773                         /*
1774                          * don't clone the oid as it's got to be
1775                          * overwwritten anyway 
1776                          */
1777                         if (!vbptr->next_variable) {
1778                             /*
1779                              * XXXWWW: ack!!! 
1780                              */
1781                         } else {
1782                             vbptr = vbptr->next_variable;
1783                             vbptr->name_length = 0;
1784                             vbptr->type = ASN_NULL;
1785                             asp->bulkcache[bulkcount++] = vbptr;
1786                         }
1787                     }
1788                     vbptr->next_variable = vbsave;
1789                 } else {
1790                     /*
1791                      * 0 repeats requested for this varbind, so take it off
1792                      * the list.  
1793                      */
1794                     vbptr = varbind_ptr;
1795                     *prevNext = vbptr->next_variable;
1796                     vbptr->next_variable = NULL;
1797                     snmp_free_varbind(vbptr);
1798                     asp->vbcount--;
1799                     continue;
1800                 }
1801             }
1802         }
1803
1804         /*
1805          * count the varbinds 
1806          */
1807         ++vbcount;
1808
1809         /*
1810          * find the owning tree 
1811          */
1812         tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
1813                                   NULL, asp->pdu->contextName);
1814
1815         /*
1816          * check access control 
1817          */
1818         switch (asp->pdu->command) {
1819         case SNMP_MSG_GET:
1820             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1821                              asp->pdu, varbind_ptr->type);
1822             if (view != VACM_SUCCESS)
1823                 snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
1824                                          NULL, 0);
1825             break;
1826
1827         case SNMP_MSG_SET:
1828             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
1829                              asp->pdu, varbind_ptr->type);
1830             if (view != VACM_SUCCESS)
1831                 return SNMP_ERR_NOTWRITABLE;
1832             break;
1833
1834         case SNMP_MSG_GETNEXT:
1835         case SNMP_MSG_GETBULK:
1836         default:
1837             view = VACM_SUCCESS;
1838             /*
1839              * XXXWWW: check VACM here to see if "tp" is even worthwhile 
1840              */
1841         }
1842         if (view == VACM_SUCCESS) {
1843             request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
1844                                                    tp);
1845             if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
1846                 request->repeat = bulkrep;
1847             }
1848             if (!request)
1849                 return SNMP_ERR_GENERR;
1850         }
1851
1852         prevNext = &(varbind_ptr->next_variable);
1853     }
1854
1855     return SNMPERR_SUCCESS;
1856 }
1857
1858 /*
1859  * this function is only applicable in getnext like contexts 
1860  */
1861 int
1862 netsnmp_reassign_requests(netsnmp_agent_session *asp)
1863 {
1864     /*
1865      * assume all the requests have been filled or rejected by the
1866      * subtrees, so reassign the rejected ones to the next subtree in
1867      * the chain 
1868      */
1869
1870     int             i;
1871
1872     /*
1873      * get old info 
1874      */
1875     netsnmp_tree_cache *old_treecache = asp->treecache;
1876
1877     /*
1878      * malloc new space 
1879      */
1880     asp->treecache =
1881         (netsnmp_tree_cache *) calloc(asp->treecache_len,
1882                                       sizeof(netsnmp_tree_cache));
1883     asp->treecache_num = -1;
1884     if (asp->cache_store) {
1885         netsnmp_free_cachemap(asp->cache_store);
1886         asp->cache_store = NULL;
1887     }
1888
1889     for (i = 0; i < asp->vbcount; i++) {
1890         if (asp->requests[i].requestvb->type == ASN_NULL) {
1891             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1892                                               asp->requests[i].requestvb,
1893                                               asp->requests[i].subtree->next)) {
1894                 if (old_treecache != NULL) {
1895                     free(old_treecache);
1896                 }
1897                 return SNMP_ERR_GENERR;
1898             }
1899         } else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
1900             /*
1901              * re-add the same subtree 
1902              */
1903             asp->requests[i].requestvb->type = ASN_NULL;
1904             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
1905                                               asp->requests[i].requestvb,
1906                                               asp->requests[i].subtree)) {
1907                 if (old_treecache != NULL) {
1908                     free(old_treecache);
1909                 }
1910                 return SNMP_ERR_GENERR;
1911             }
1912         }
1913     }
1914
1915     if (old_treecache != NULL) {
1916         free(old_treecache);
1917     }
1918     return SNMP_ERR_NOERROR;
1919 }
1920
1921 void
1922 netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
1923 {
1924     while (reqlist) {
1925         netsnmp_free_request_data_sets(reqlist);
1926         reqlist = reqlist->next;
1927     }
1928 }
1929
1930 void
1931 netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
1932 {
1933     while (asp->treecache_num >= 0) {
1934         /*
1935          * don't delete subtrees 
1936          */
1937         netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
1938                                      requests_begin);
1939         asp->treecache_num--;
1940     }
1941 }
1942
1943 int
1944 netsnmp_check_requests_status(netsnmp_agent_session *asp,
1945                               netsnmp_request_info *requests,
1946                               int look_for_specific)
1947 {
1948     /*
1949      * find any errors marked in the requests 
1950      */
1951     while (requests) {
1952         if (requests->status != SNMP_ERR_NOERROR &&
1953             (!look_for_specific || requests->status == look_for_specific)
1954             && (look_for_specific || asp->index == 0
1955                 || requests->index < asp->index)) {
1956             asp->index = requests->index;
1957             asp->status = requests->status;
1958         }
1959         requests = requests->next;
1960     }
1961     return asp->status;
1962 }
1963
1964 int
1965 netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
1966                                   int look_for_specific)
1967 {
1968     int             i;
1969     for (i = 0; i <= asp->treecache_num; i++) {
1970         netsnmp_check_requests_status(asp,
1971                                       asp->treecache[i].requests_begin,
1972                                       look_for_specific);
1973     }
1974     return asp->status;
1975 }
1976
1977 int
1978 handle_var_requests(netsnmp_agent_session *asp)
1979 {
1980     int             i, retstatus = SNMP_ERR_NOERROR,
1981         status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
1982     netsnmp_handler_registration *reginfo;
1983
1984     /*
1985      * create the netsnmp_agent_request_info data 
1986      */
1987     if (!asp->reqinfo) {
1988         asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
1989         if (!asp->reqinfo)
1990             return SNMP_ERR_GENERR;
1991     }
1992
1993     asp->reqinfo->asp = asp;
1994     asp->reqinfo->mode = asp->mode;
1995
1996     /*
1997      * now, have the subtrees in the cache go search for their results 
1998      */
1999     for (i = 0; i <= asp->treecache_num; i++) {
2000         reginfo = asp->treecache[i].subtree->reginfo;
2001         status = netsnmp_call_handlers(reginfo, asp->reqinfo,
2002                                        asp->treecache[i].requests_begin);
2003
2004         /*
2005          * find any errors marked in the requests.  For later parts of
2006          * SET processing, only check for new errors specific to that
2007          * set processing directive (which must superceed the previous
2008          * errors).
2009          */
2010         switch (asp->mode) {
2011         case MODE_SET_COMMIT:
2012             retstatus = netsnmp_check_requests_status(asp,
2013                                                       asp->treecache[i].
2014                                                       requests_begin,
2015                                                       SNMP_ERR_COMMITFAILED);
2016             break;
2017
2018         case MODE_SET_UNDO:
2019             retstatus = netsnmp_check_requests_status(asp,
2020                                                       asp->treecache[i].
2021                                                       requests_begin,
2022                                                       SNMP_ERR_UNDOFAILED);
2023             break;
2024
2025         default:
2026             retstatus = netsnmp_check_requests_status(asp,
2027                                                       asp->treecache[i].
2028                                                       requests_begin, 0);
2029             break;
2030         }
2031
2032         /*
2033          * always take lowest varbind if possible 
2034          */
2035         if (retstatus != SNMP_ERR_NOERROR) {
2036             status = retstatus;
2037         }
2038
2039         /*
2040          * other things we know less about (no index) 
2041          */
2042         /*
2043          * WWW: drop support for this? 
2044          */
2045         if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
2046             /*
2047              * we can't break here, since some processing needs to be
2048              * done for all requests anyway (IE, SET handling for UNDO
2049              * needs to be called regardless of previous status
2050              * results.
2051              * WWW:  This should be predictable though and
2052              * breaking should be possible in some cases (eg GET,
2053              * GETNEXT, ...) 
2054              */
2055             final_status = status;
2056         }
2057     }
2058
2059     return final_status;
2060 }
2061
2062 /*
2063  * loop through our sessions known delegated sessions and check to see
2064  * if they've completed yet. If there are no more delegated sessions,
2065  * check for and process any queued requests
2066  */
2067 void
2068 netsnmp_check_outstanding_agent_requests(void)
2069 {
2070     netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
2071
2072     /*
2073      * deal with delegated requests
2074      */
2075     for (asp = agent_delegated_list; asp; prev_asp = asp, asp = next_asp) {
2076         next_asp = asp->next;   /* save in case we clean up asp */
2077         if (!netsnmp_check_for_delegated(asp)) {
2078
2079             /*
2080              * we're done with this one, remove from queue 
2081              */
2082             if (prev_asp != NULL)
2083                 prev_asp->next = asp->next;
2084             else
2085                 agent_delegated_list = asp->next;
2086
2087             /*
2088              * check request status
2089              */
2090             netsnmp_check_all_requests_status(asp, 0);
2091             
2092             /*
2093              * continue processing or finish up 
2094              */
2095             check_delayed_request(asp);
2096         }
2097     }
2098
2099     /*
2100      * if we are processing a set and there are more delegated
2101      * requests, keep waiting before getting to queued requests.
2102      */
2103     if (netsnmp_processing_set && (NULL != agent_delegated_list))
2104         return;
2105
2106     while (netsnmp_agent_queued_list) {
2107
2108         /*
2109          * if we are processing a set, the first item better be
2110          * the set being (or waiting to be) processed.
2111          */
2112         netsnmp_assert((!netsnmp_processing_set) ||
2113                        (netsnmp_processing_set == netsnmp_agent_queued_list));
2114
2115         /*
2116          * if the top request is a set, don't pop it
2117          * off if there are delegated requests
2118          */
2119         if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
2120             (agent_delegated_list)) {
2121
2122             netsnmp_assert(netsnmp_processing_set == NULL);
2123
2124             netsnmp_processing_set = netsnmp_agent_queued_list;
2125             DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
2126                         "delegated requests finish, asp = %08p\n", asp));
2127             break;
2128         }
2129
2130         /*
2131          * pop the first request and process it
2132          */
2133         asp = netsnmp_agent_queued_list;
2134         netsnmp_agent_queued_list = asp->next;
2135         DEBUGMSGTL(("snmp_agent",
2136                     "processing queued request, asp = %08p\n", asp));
2137
2138         netsnmp_handle_request(asp, asp->status);
2139
2140         /*
2141          * if we hit a set, stop
2142          */
2143         if (NULL != netsnmp_processing_set)
2144             break;
2145     }
2146 }
2147
2148 /** Decide if the requested transaction_id is still being processed
2149    within the agent.  This is used to validate whether a delayed cache
2150    (containing possibly freed pointers) is still usable.
2151
2152    returns SNMPERR_SUCCESS if it's still valid, or SNMPERR_GENERR if not. */
2153 int
2154 netsnmp_check_transaction_id(int transaction_id)
2155 {
2156     netsnmp_agent_session *asp, *prev_asp = NULL;
2157
2158     for (asp = agent_delegated_list; asp; prev_asp = asp, asp = asp->next) {
2159         if (asp->pdu->transid == transaction_id)
2160             return SNMPERR_SUCCESS;
2161     }
2162     return SNMPERR_GENERR;
2163 }
2164
2165
2166 /*
2167  * check_delayed_request(asp)
2168  *
2169  * Called to rexamine a set of requests and continue processing them
2170  * once all the previous (delayed) requests have been handled one way
2171  * or another.
2172  */
2173
2174 int
2175 check_delayed_request(netsnmp_agent_session *asp)
2176 {
2177     int             status = SNMP_ERR_NOERROR;
2178
2179     DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %08p\n",
2180                 asp));
2181
2182     switch (asp->mode) {
2183     case SNMP_MSG_GETBULK:
2184     case SNMP_MSG_GETNEXT:
2185         netsnmp_check_all_requests_status(asp, 0);
2186         handle_getnext_loop(asp);
2187         if (netsnmp_check_for_delegated(asp) &&
2188             netsnmp_check_transaction_id(asp->pdu->transid) !=
2189             SNMPERR_SUCCESS) {
2190             /*
2191              * add to delegated request chain 
2192              */
2193             if (!netsnmp_check_delegated_chain_for(asp)) {
2194                 asp->next = agent_delegated_list;
2195                 agent_delegated_list = asp;
2196             }
2197         }
2198         break;
2199
2200     case MODE_SET_COMMIT:
2201         netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
2202         goto settop;
2203
2204     case MODE_SET_UNDO:
2205         netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
2206         goto settop;
2207
2208     case MODE_SET_BEGIN:
2209     case MODE_SET_RESERVE1:
2210     case MODE_SET_RESERVE2:
2211     case MODE_SET_ACTION:
2212     case MODE_SET_FREE:
2213       settop:
2214         handle_set_loop(asp);
2215         if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2216
2217             if (netsnmp_check_for_delegated_and_add(asp)) {
2218                 /*
2219                  * add to delegated request chain 
2220                  */
2221                 if (!asp->status)
2222                     asp->status = status;
2223             }
2224
2225             return SNMP_ERR_NOERROR;
2226         }
2227         break;
2228
2229     default:
2230         break;
2231     }
2232
2233     /*
2234      * if we don't have anything outstanding (delegated), wrap up 
2235      */
2236     if (!netsnmp_check_for_delegated(asp))
2237         return netsnmp_wrap_up_request(asp, status);
2238
2239     return 1;
2240 }
2241
2242 /** returns 1 if there are valid GETNEXT requests left.  Returns 0 if not. */
2243 int
2244 check_getnext_results(netsnmp_agent_session *asp)
2245 {
2246     /*
2247      * get old info 
2248      */
2249     netsnmp_tree_cache *old_treecache = asp->treecache;
2250     int             old_treecache_num = asp->treecache_num;
2251     int             count = 0;
2252     int             i, special = 0;
2253     netsnmp_request_info *request;
2254
2255     if (asp->mode == SNMP_MSG_GET) {
2256         /*
2257          * Special case for doing INCLUSIVE getNext operations in
2258          * AgentX subagents.  
2259          */
2260         DEBUGMSGTL(("snmp_agent",
2261                     "asp->mode == SNMP_MSG_GET in ch_getnext\n"));
2262         asp->mode = asp->oldmode;
2263         special = 1;
2264     }
2265
2266     for (i = 0; i <= old_treecache_num; i++) {
2267         for (request = old_treecache[i].requests_begin; request;
2268              request = request->next) {
2269
2270             /*
2271              * If we have just done the special case AgentX GET, then any
2272              * requests which were not INCLUSIVE will now have a wrong
2273              * response, so junk them and retry from the same place (except
2274              * that this time the handler will be called in "inexact"
2275              * mode).  
2276              */
2277
2278             if (special) {
2279                 if (!request->inclusive) {
2280                     DEBUGMSGTL(("snmp_agent",
2281                                 "request %d wasn't inclusive\n",
2282                                 request->index));
2283                     snmp_set_var_typed_value(request->requestvb,
2284                                              ASN_PRIV_RETRY, NULL, 0);
2285                 } else if (request->requestvb->type == ASN_NULL ||
2286                            request->requestvb->type == SNMP_NOSUCHINSTANCE ||
2287                            request->requestvb->type == SNMP_NOSUCHOBJECT) {
2288                     /*
2289                      * it was inclusive, but no results.  Still retry this
2290                      * search. 
2291                      */
2292                     snmp_set_var_typed_value(request->requestvb,
2293                                              ASN_PRIV_RETRY, NULL, 0);
2294                 }
2295             }
2296
2297             /*
2298              * out of range? 
2299              */
2300             if (snmp_oid_compare(request->requestvb->name,
2301                                  request->requestvb->name_length,
2302                                  request->range_end,
2303                                  request->range_end_len) >= 0) {
2304                 /*
2305                  * ack, it's beyond the accepted end of range. 
2306                  */
2307                 /*
2308                  * fix it by setting the oid to the end of range oid instead 
2309                  */
2310                 DEBUGMSGTL(("check_getnext_results",
2311                             "request response %d out of range\n",
2312                             request->index));
2313                 request->inclusive = 1;
2314                 /*
2315                  * XXX: should set this to the original OID? 
2316                  */
2317                 snmp_set_var_objid(request->requestvb,
2318                                    request->range_end,
2319                                    request->range_end_len);
2320                 snmp_set_var_typed_value(request->requestvb, ASN_NULL,
2321                                          NULL, 0);
2322             }
2323
2324             /*
2325              * mark any existent requests with illegal results as NULL 
2326              */
2327             if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
2328                 /*
2329                  * illegal response from a subagent.  Change it back to NULL 
2330                  */
2331                 request->requestvb->type = ASN_NULL;
2332                 request->inclusive = 1;
2333             }
2334
2335             if (request->requestvb->type == ASN_NULL ||
2336                 request->requestvb->type == ASN_PRIV_RETRY ||
2337                 (asp->reqinfo->mode == MODE_GETBULK
2338                  && request->repeat > 0))
2339                 count++;
2340         }
2341     }
2342     return count;
2343 }
2344
2345 /** repeatedly calls getnext handlers looking for an answer till all
2346    requests are satisified.  It's expected that one pass has been made
2347    before entering this function */
2348 int
2349 handle_getnext_loop(netsnmp_agent_session *asp)
2350 {
2351     int             status;
2352     netsnmp_variable_list *var_ptr;
2353
2354     /*
2355      * loop 
2356      */
2357     while (1) {
2358
2359         /*
2360          * bail for now if anything is delegated. 
2361          */
2362         if (netsnmp_check_for_delegated(asp)) {
2363             return SNMP_ERR_NOERROR;
2364         }
2365
2366         /*
2367          * check vacm against results 
2368          */
2369         check_acm(asp, ASN_PRIV_RETRY);
2370
2371         /*
2372          * need to keep going we're not done yet. 
2373          */
2374         if (!check_getnext_results(asp))
2375             /*
2376              * nothing left, quit now 
2377              */
2378             break;
2379
2380         /*
2381          * never had a request (empty pdu), quit now 
2382          */
2383         /*
2384          * XXXWWW: huh?  this would be too late, no?  shouldn't we
2385          * catch this earlier? 
2386          */
2387         /*
2388          * if (count == 0)
2389          * break; 
2390          */
2391
2392         DEBUGIF("results") {
2393             DEBUGMSGTL(("results",
2394                         "getnext results, before next pass:\n"));
2395             for (var_ptr = asp->pdu->variables; var_ptr;
2396                  var_ptr = var_ptr->next_variable) {
2397                 DEBUGMSGTL(("results", "\t"));
2398                 DEBUGMSGVAR(("results", var_ptr));
2399                 DEBUGMSG(("results", "\n"));
2400             }
2401         }
2402
2403         netsnmp_reassign_requests(asp);
2404         status = handle_var_requests(asp);
2405         if (status != SNMP_ERR_NOERROR) {
2406             return status;      /* should never really happen */
2407         }
2408     }
2409     return SNMP_ERR_NOERROR;
2410 }
2411
2412 int
2413 handle_set(netsnmp_agent_session *asp)
2414 {
2415     int             status;
2416     /*
2417      * SETS require 3-4 passes through the var_op_list.
2418      * The first two
2419      * passes verify that all types, lengths, and values are valid
2420      * and may reserve resources and the third does the set and a
2421      * fourth executes any actions.  Then the identical GET RESPONSE
2422      * packet is returned.
2423      * If either of the first two passes returns an error, another
2424      * pass is made so that any reserved resources can be freed.
2425      * If the third pass returns an error, another pass is
2426      * made so that
2427      * any changes can be reversed.
2428      * If the fourth pass (or any of the error handling passes)
2429      * return an error, we'd rather not know about it!
2430      */
2431     if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
2432         switch (asp->mode) {
2433         case MODE_SET_BEGIN:
2434             snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
2435             asp->rw = WRITE;    /* WWW: still needed? */
2436             asp->mode = MODE_SET_RESERVE1;
2437             asp->status = SNMP_ERR_NOERROR;
2438             break;
2439
2440         case MODE_SET_RESERVE1:
2441
2442             if (asp->status != SNMP_ERR_NOERROR)
2443                 asp->mode = MODE_SET_FREE;
2444             else
2445                 asp->mode = MODE_SET_RESERVE2;
2446             break;
2447
2448         case MODE_SET_RESERVE2:
2449             if (asp->status != SNMP_ERR_NOERROR)
2450                 asp->mode = MODE_SET_FREE;
2451             else
2452                 asp->mode = MODE_SET_ACTION;
2453             break;
2454
2455         case MODE_SET_ACTION:
2456             if (asp->status != SNMP_ERR_NOERROR)
2457                 asp->mode = MODE_SET_UNDO;
2458             else
2459                 asp->mode = MODE_SET_COMMIT;
2460             break;
2461
2462         case MODE_SET_COMMIT:
2463             if (asp->status != SNMP_ERR_NOERROR) {
2464                 asp->mode = FINISHED_FAILURE;
2465            } else {
2466                 asp->mode = FINISHED_SUCCESS;
2467             }
2468             break;
2469
2470         case MODE_SET_UNDO:
2471             asp->mode = FINISHED_FAILURE;
2472             break;
2473
2474         case MODE_SET_FREE:
2475             asp->mode = FINISHED_FAILURE;
2476             break;
2477         }
2478     }
2479
2480     if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
2481         DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
2482                     se_find_label_in_slist("agent_mode", asp->mode)));
2483         status = handle_var_requests(asp);
2484         DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
2485                     asp->mode, status));
2486         if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
2487             status == SNMP_ERR_COMMITFAILED || 
2488             status == SNMP_ERR_UNDOFAILED) {
2489             asp->status = status;
2490         }
2491     }
2492     return asp->status;
2493 }
2494
2495 int
2496 handle_set_loop(netsnmp_agent_session *asp)
2497 {
2498     while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
2499         handle_set(asp);
2500         if (netsnmp_check_for_delegated(asp)) {
2501             return SNMP_ERR_NOERROR;
2502         }
2503         if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
2504             return asp->status;
2505         }
2506     }
2507     return asp->status;
2508 }
2509
2510 int
2511 netsnmp_handle_request(netsnmp_agent_session *asp, int status)
2512 {
2513     /*
2514      * if this isn't a delegated request trying to finish,
2515      * processing of a set request should not start until all
2516      * delegated requests have completed, and no other new requests
2517      * should be processed until the set request completes.
2518      */
2519     if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
2520         (asp != netsnmp_processing_set)) {
2521         /*
2522          * if we are processing a set and this is not a delegated
2523          * request, queue the request
2524          */
2525         if (netsnmp_processing_set) {
2526             netsnmp_add_queued(asp);
2527             DEBUGMSGTL(("snmp_agent",
2528                         "request queued while processing set, "
2529                         "asp = %08p\n", asp));
2530             return 1;
2531         }
2532
2533         /*
2534          * check for set request
2535          */
2536         if (asp->pdu->command == SNMP_MSG_SET) {
2537             netsnmp_processing_set = asp;
2538
2539             /*
2540              * if there are delegated requests, we must wait for them
2541              * to finishd.
2542              */
2543             if (agent_delegated_list) {
2544                 DEBUGMSGTL(("snmp_agent", "SET request queued while "
2545                             "delegated requests finish, asp = %08p\n",
2546                             asp));
2547                 netsnmp_add_queued(asp);
2548                 return 1;
2549             }
2550         }
2551     }
2552
2553     /*
2554      * process the request 
2555      */
2556     status = handle_pdu(asp);
2557
2558     /*
2559      * print the results in appropriate debugging mode 
2560      */
2561     DEBUGIF("results") {
2562         netsnmp_variable_list *var_ptr;
2563         DEBUGMSGTL(("results", "request results (status = %d):\n",
2564                     status));
2565         for (var_ptr = asp->pdu->variables; var_ptr;
2566              var_ptr = var_ptr->next_variable) {
2567             DEBUGMSGTL(("results", "\t"));
2568             DEBUGMSGVAR(("results", var_ptr));
2569             DEBUGMSG(("results", "\n"));
2570         }
2571     }
2572
2573     /*
2574      * check for uncompleted requests 
2575      */
2576     if (netsnmp_check_for_delegated_and_add(asp)) {
2577         /*
2578          * add to delegated request chain 
2579          */
2580         asp->status = status;
2581     } else {
2582         /*
2583          * if we don't have anything outstanding (delegated), wrap up
2584          */
2585         return netsnmp_wrap_up_request(asp, status);
2586     }
2587
2588     return 1;
2589 }
2590
2591 int
2592 handle_pdu(netsnmp_agent_session *asp)
2593 {
2594     int             status, inclusives = 0;
2595     netsnmp_variable_list *v = NULL;
2596
2597     /*
2598      * for illegal requests, mark all nodes as ASN_NULL 
2599      */
2600     switch (asp->pdu->command) {
2601
2602     case SNMP_MSG_INTERNAL_SET_RESERVE2:
2603     case SNMP_MSG_INTERNAL_SET_ACTION:
2604     case SNMP_MSG_INTERNAL_SET_COMMIT:
2605     case SNMP_MSG_INTERNAL_SET_FREE:
2606     case SNMP_MSG_INTERNAL_SET_UNDO:
2607         status = get_set_cache(asp);
2608         if (status != SNMP_ERR_NOERROR)
2609             return status;
2610         break;
2611
2612     case SNMP_MSG_GET:
2613     case SNMP_MSG_GETNEXT:
2614     case SNMP_MSG_GETBULK:
2615         for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
2616             if (v->type == ASN_PRIV_INCL_RANGE) {
2617                 /*
2618                  * Leave the type for now (it gets set to
2619                  * ASN_NULL in netsnmp_add_varbind_to_cache,
2620                  * called by create_subnetsnmp_tree_cache below).
2621                  * If we set it to ASN_NULL now, we wouldn't be
2622                  * able to distinguish INCLUSIVE search
2623                  * ranges.  
2624                  */
2625                 inclusives++;
2626             } else {
2627                 snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
2628             }
2629         }
2630         /*
2631          * fall through 
2632          */
2633
2634     case SNMP_MSG_INTERNAL_SET_BEGIN:
2635     case SNMP_MSG_INTERNAL_SET_RESERVE1:
2636     default:
2637         asp->vbcount = count_varbinds(asp->pdu->variables);
2638         if (asp->vbcount) /* efence doesn't like 0 size allocs */
2639             asp->requests = (netsnmp_request_info *)
2640                 calloc(asp->vbcount, sizeof(netsnmp_request_info));
2641         /*
2642          * collect varbinds 
2643          */
2644         status = netsnmp_create_subtree_cache(asp);
2645         if (status != SNMP_ERR_NOERROR)
2646             return status;
2647     }
2648
2649     asp->mode = asp->pdu->command;
2650     switch (asp->mode) {
2651     case SNMP_MSG_GET:
2652         /*
2653          * increment the message type counter 
2654          */
2655         snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
2656
2657         /*
2658          * check vacm ahead of time 
2659          */
2660         check_acm(asp, SNMP_NOSUCHOBJECT);
2661
2662         /*
2663          * get the results 
2664          */
2665         status = handle_var_requests(asp);
2666
2667         /*
2668          * Deal with unhandled results -> noSuchInstance (rather
2669          * than noSuchObject -- in that case, the type will
2670          * already have been set to noSuchObject when we realised
2671          * we couldn't find an appropriate tree).  
2672          */
2673         if (status == SNMP_ERR_NOERROR)
2674             snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
2675                                    SNMP_NOSUCHINSTANCE);
2676         break;
2677
2678     case SNMP_MSG_GETNEXT:
2679         snmp_increment_statistic(STAT_SNMPINGETNEXTS);
2680         /*
2681          * fall through 
2682          */
2683
2684     case SNMP_MSG_GETBULK:     /* note: there is no getbulk stat */
2685         /*
2686          * loop through our mib tree till we find an
2687          * appropriate response to return to the caller. 
2688          */
2689
2690         if (inclusives) {
2691             /*
2692              * This is a special case for AgentX INCLUSIVE getNext
2693              * requests where a result lexi-equal to the request is okay
2694              * but if such a result does not exist, we still want the
2695              * lexi-next one.  So basically we do a GET first, and if any
2696              * of the INCLUSIVE requests are satisfied, we use that
2697              * value.  Then, unsatisfied INCLUSIVE requests, and
2698              * non-INCLUSIVE requests get done as normal.  
2699              */
2700
2701             DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
2702             asp->oldmode = asp->mode;
2703             asp->mode = SNMP_MSG_GET;
2704         }
2705
2706         /*
2707          * first pass 
2708          */
2709         status = handle_var_requests(asp);
2710         if (status != SNMP_ERR_NOERROR) {
2711             if (!inclusives)
2712                 return status;  /* should never really happen */
2713             else
2714                 asp->status = SNMP_ERR_NOERROR;
2715         }
2716
2717         /*
2718          * loop through our mib tree till we find an
2719          * appropriate response to return to the caller. 
2720          */
2721
2722         status = handle_getnext_loop(asp);
2723         break;
2724
2725     case SNMP_MSG_SET:
2726         /*
2727          * check access permissions first 
2728          */
2729         if (check_acm(asp, SNMP_NOSUCHOBJECT))
2730             return SNMP_ERR_NOTWRITABLE;
2731
2732         asp->mode = MODE_SET_BEGIN;
2733         status = handle_set_loop(asp);
2734
2735         break;
2736
2737     case SNMP_MSG_INTERNAL_SET_BEGIN:
2738     case SNMP_MSG_INTERNAL_SET_RESERVE1:
2739     case SNMP_MSG_INTERNAL_SET_RESERVE2:
2740     case SNMP_MSG_INTERNAL_SET_ACTION:
2741     case SNMP_MSG_INTERNAL_SET_COMMIT:
2742     case SNMP_MSG_INTERNAL_SET_FREE:
2743     case SNMP_MSG_INTERNAL_SET_UNDO:
2744         asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
2745         status = handle_set_loop(asp);
2746         /*
2747          * asp related cache is saved in cleanup 
2748          */
2749         break;
2750
2751     case SNMP_MSG_RESPONSE:
2752         snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
2753         return SNMP_ERR_NOERROR;
2754
2755     case SNMP_MSG_TRAP:
2756     case SNMP_MSG_TRAP2:
2757         snmp_increment_statistic(STAT_SNMPINTRAPS);
2758         return SNMP_ERR_NOERROR;
2759
2760     default:
2761         /*
2762          * WWW: are reports counted somewhere ? 
2763          */
2764         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
2765         return SNMPERR_GENERR;  /* shouldn't get here */
2766         /*
2767          * WWW 
2768          */
2769     }
2770     return status;
2771 }
2772
2773 int
2774 netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
2775                           netsnmp_request_info *request, int error_value)
2776 {
2777     if (!request || !reqinfo)
2778         return error_value;
2779
2780     return netsnmp_set_mode_request_error(reqinfo->mode, request,
2781                                           error_value);
2782 }
2783
2784 int
2785 netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
2786                                int error_value)
2787 {
2788     if (!request)
2789         return error_value;
2790
2791     request->processed = 1;
2792     request->delegated = REQUEST_IS_NOT_DELEGATED;
2793
2794     switch (error_value) {
2795     case SNMP_NOSUCHOBJECT:
2796     case SNMP_NOSUCHINSTANCE:
2797     case SNMP_ENDOFMIBVIEW:
2798         /*
2799          * these are exceptions that should be put in the varbind
2800          * in the case of a GET but should be translated for a SET
2801          * into a real error status code and put in the request 
2802          */
2803         switch (mode) {
2804         case MODE_GET:
2805             request->requestvb->type = error_value;
2806             return error_value;
2807
2808         case MODE_GETNEXT:
2809         case MODE_GETBULK:
2810             /*
2811              * ignore these.  They're illegal to set by the
2812              * client APIs for these modes 
2813              */
2814             snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
2815                      error_value, mode);
2816             return error_value;
2817
2818         default:
2819             request->status = SNMP_ERR_NOSUCHNAME;      /* WWW: correct? */
2820             return error_value;
2821         }
2822         break;                  /* never get here */
2823
2824     default:
2825         if (request->status < 0) {
2826             /*
2827              * illegal local error code.  translate to generr 
2828              */
2829             /*
2830              * WWW: full translation map? 
2831              */
2832             snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
2833                      error_value, SNMP_ERR_GENERR);
2834             request->status = SNMP_ERR_GENERR;
2835         } else {
2836             /*
2837              * WWW: translations and mode checking? 
2838              */
2839             request->status = error_value;
2840         }
2841         return error_value;
2842     }
2843     return error_value;
2844 }
2845
2846 int
2847 netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
2848                                netsnmp_request_info *requests,
2849                                int error_value)
2850 {
2851     while (requests) {
2852         netsnmp_set_request_error(reqinfo, requests, error_value);
2853         requests = requests->next;
2854     }
2855     return error_value;
2856 }
2857
2858 extern struct timeval starttime;
2859
2860                 /*
2861                  * Return the value of 'sysUpTime' at the given marker 
2862                  */
2863 u_long
2864 netsnmp_marker_uptime(marker_t pm)
2865 {
2866     u_long          res;
2867     marker_t        start = (marker_t) & starttime;
2868
2869     res = uatime_hdiff(start, pm);
2870     return res;                 /* atime_diff works in msec, not csec */
2871 }
2872
2873                         /*
2874                          * struct timeval equivalents of these 
2875                          */
2876 u_long
2877 netsnmp_timeval_uptime(struct timeval * tv)
2878 {
2879     return netsnmp_marker_uptime((marker_t) tv);
2880 }
2881
2882                 /*
2883                  * Return the current value of 'sysUpTime' 
2884                  */
2885 u_long
2886 netsnmp_get_agent_uptime(void)
2887 {
2888     struct timeval  now;
2889     gettimeofday(&now, NULL);
2890
2891     return netsnmp_timeval_uptime(&now);
2892 }
2893
2894
2895
2896 NETSNMP_INLINE void
2897 netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
2898                             netsnmp_data_list *node)
2899 {
2900     if (ari) {
2901         if (ari->agent_data) {
2902             netsnmp_add_list_data(&ari->agent_data, node);
2903         } else {
2904             ari->agent_data = node;
2905         }
2906     }
2907 }
2908
2909 NETSNMP_INLINE void    *
2910 netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
2911                             const char *name)
2912 {
2913     if (ari) {
2914         return netsnmp_get_list_data(ari->agent_data, name);
2915     }
2916     return NULL;
2917 }
2918
2919 NETSNMP_INLINE void
2920 netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
2921 {
2922     if (ari) {
2923         netsnmp_free_list_data(ari->agent_data);
2924     }
2925 }
2926
2927 NETSNMP_INLINE void
2928 netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
2929 {
2930     if (ari) {
2931         netsnmp_free_all_list_data(ari->agent_data);
2932     }
2933 }
2934
2935 NETSNMP_INLINE void
2936 netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
2937 {
2938     if (ari) {
2939         if (ari->agent_data) {
2940             netsnmp_free_all_list_data(ari->agent_data);
2941         }
2942         free(ari);
2943     }
2944 }