net-snmp  5.4.1
snmp_agent.c
00001 /*
00002  * snmp_agent.c
00003  *
00004  * Simple Network Management Protocol (RFC 1067).
00005  */
00006 /* Portions of this file are subject to the following copyright(s).  See
00007  * the Net-SNMP's COPYING file for more details and other copyrights
00008  * that may apply:
00009  */
00010 /* Portions of this file are subject to the following copyrights.  See
00011  * the Net-SNMP's COPYING file for more details and other copyrights
00012  * that may apply:
00013  */
00014 /***********************************************************
00015         Copyright 1988, 1989 by Carnegie Mellon University
00016 
00017                       All Rights Reserved
00018 
00019 Permission to use, copy, modify, and distribute this software and its 
00020 documentation for any purpose and without fee is hereby granted, 
00021 provided that the above copyright notice appear in all copies and that
00022 both that copyright notice and this permission notice appear in 
00023 supporting documentation, and that the name of CMU not be
00024 used in advertising or publicity pertaining to distribution of the
00025 software without specific, written prior permission.  
00026 
00027 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00028 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00029 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00030 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00031 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00032 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00033 SOFTWARE.
00034 ******************************************************************/
00035 /*
00036  * Portions of this file are copyrighted by:
00037  * Copyright © 2003 Sun Microsystems, Inc. All rights 
00038  * reserved.  Use is subject to license terms specified in the 
00039  * COPYING file distributed with the Net-SNMP package.
00040  */
00046 #include <net-snmp/net-snmp-config.h>
00047 
00048 #include <sys/types.h>
00049 #ifdef HAVE_LIMITS_H
00050 #include <limits.h>
00051 #endif
00052 #ifdef HAVE_STDLIB_H
00053 #include <stdlib.h>
00054 #endif
00055 #if HAVE_UNISTD_H
00056 #include <unistd.h>
00057 #endif
00058 #if HAVE_STRING_H
00059 #include <string.h>
00060 #endif
00061 #if TIME_WITH_SYS_TIME
00062 # ifdef WIN32
00063 #  include <sys/timeb.h>
00064 # else
00065 #  include <sys/time.h>
00066 # endif
00067 # include <time.h>
00068 #else
00069 # if HAVE_SYS_TIME_H
00070 #  include <sys/time.h>
00071 # else
00072 #  include <time.h>
00073 # endif
00074 #endif
00075 #if HAVE_SYS_SELECT_H
00076 #include <sys/select.h>
00077 #endif
00078 #if HAVE_NETINET_IN_H
00079 #include <netinet/in.h>
00080 #endif
00081 #include <errno.h>
00082 #if HAVE_WINSOCK_H
00083 #include <winsock.h>
00084 #endif
00085 
00086 #define SNMP_NEED_REQUEST_LIST
00087 #include <net-snmp/net-snmp-includes.h>
00088 #include <net-snmp/agent/net-snmp-agent-includes.h>
00089 #include <net-snmp/library/snmp_assert.h>
00090 
00091 #if HAVE_SYSLOG_H
00092 #include <syslog.h>
00093 #endif
00094 
00095 #ifdef NETSNMP_USE_LIBWRAP
00096 #include <tcpd.h>
00097 int             allow_severity = LOG_INFO;
00098 int             deny_severity = LOG_WARNING;
00099 #endif
00100 
00101 #include "snmpd.h"
00102 #include "mibgroup/struct.h"
00103 #include "mibgroup/util_funcs.h"
00104 #include <net-snmp/agent/mib_module_config.h>
00105 #include <net-snmp/agent/mib_modules.h>
00106 
00107 #ifdef USING_AGENTX_PROTOCOL_MODULE
00108 #include "agentx/protocol.h"
00109 #endif
00110 
00111 #ifdef USING_AGENTX_MASTER_MODULE
00112 #include "agentx/master.h"
00113 #endif
00114 
00115 #ifdef USING_SMUX_MODULE
00116 #include "smux/smux.h"
00117 #endif
00118 
00119 oid      version_sysoid[] = { NETSNMP_SYSTEM_MIB };
00120 int      version_sysoid_len = OID_LENGTH(version_sysoid);
00121 
00122 #define SNMP_ADDRCACHE_SIZE 10
00123 #define SNMP_ADDRCACHE_MAXAGE 300 /* in seconds */
00124 
00125 enum {
00126     SNMP_ADDRCACHE_UNUSED = 0,
00127     SNMP_ADDRCACHE_USED = 1
00128 };
00129 
00130 struct addrCache {
00131     char           *addr;
00132     int            status;
00133     struct timeval lastHit;
00134 };
00135 
00136 static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
00137 int             log_addresses = 0;
00138 
00139 
00140 
00141 typedef struct _agent_nsap {
00142     int             handle;
00143     netsnmp_transport *t;
00144     void           *s;          /*  Opaque internal session pointer.  */
00145     struct _agent_nsap *next;
00146 } agent_nsap;
00147 
00148 static agent_nsap *agent_nsap_list = NULL;
00149 static netsnmp_agent_session *agent_session_list = NULL;
00150 netsnmp_agent_session *netsnmp_processing_set = NULL;
00151 netsnmp_agent_session *agent_delegated_list = NULL;
00152 netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
00153 
00154 
00155 int             netsnmp_agent_check_packet(netsnmp_session *,
00156                                            struct netsnmp_transport_s *,
00157                                            void *, int);
00158 int             netsnmp_agent_check_parse(netsnmp_session *, netsnmp_pdu *,
00159                                           int);
00160 void            delete_subnetsnmp_tree_cache(netsnmp_agent_session *asp);
00161 int             handle_pdu(netsnmp_agent_session *asp);
00162 int             netsnmp_handle_request(netsnmp_agent_session *asp,
00163                                        int status);
00164 int             netsnmp_wrap_up_request(netsnmp_agent_session *asp,
00165                                         int status);
00166 int             check_delayed_request(netsnmp_agent_session *asp);
00167 int             handle_getnext_loop(netsnmp_agent_session *asp);
00168 int             handle_set_loop(netsnmp_agent_session *asp);
00169 
00170 int             netsnmp_check_queued_chain_for(netsnmp_agent_session *asp);
00171 int             netsnmp_add_queued(netsnmp_agent_session *asp);
00172 int             netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
00173 
00174 
00175 static int      current_globalid = 0;
00176 
00177 int      netsnmp_running = 1;
00178 
00179 int
00180 netsnmp_allocate_globalcacheid(void)
00181 {
00182     return ++current_globalid;
00183 }
00184 
00185 int
00186 netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
00187 {
00188     while (cache_store != NULL) {
00189         if (cache_store->globalid == globalid)
00190             return cache_store->cacheid;
00191         cache_store = cache_store->next;
00192     }
00193     return -1;
00194 }
00195 
00196 netsnmp_cachemap *
00197 netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
00198                                 int globalid, int localid)
00199 {
00200     netsnmp_cachemap *tmpp;
00201 
00202     tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
00203     if (*cache_store) {
00204         tmpp->next = *cache_store;
00205         *cache_store = tmpp;
00206     } else {
00207         *cache_store = tmpp;
00208     }
00209 
00210     tmpp->globalid = globalid;
00211     tmpp->cacheid = localid;
00212     return tmpp;
00213 }
00214 
00215 void
00216 netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
00217 {
00218     netsnmp_cachemap *tmpp;
00219     while (cache_store) {
00220         tmpp = cache_store;
00221         cache_store = cache_store->next;
00222         SNMP_FREE(tmpp);
00223     }
00224 }
00225 
00226 
00227 typedef struct agent_set_cache_s {
00228     /*
00229      * match on these 2 
00230      */
00231     int             transID;
00232     netsnmp_session *sess;
00233 
00234     /*
00235      * store this info 
00236      */
00237     netsnmp_tree_cache *treecache;
00238     int             treecache_len;
00239     int             treecache_num;
00240 
00241     int             vbcount;
00242     netsnmp_request_info *requests;
00243     netsnmp_variable_list *saved_vars;
00244     netsnmp_data_list *agent_data;
00245 
00246     /*
00247      * list 
00248      */
00249     struct agent_set_cache_s *next;
00250 } agent_set_cache;
00251 
00252 static agent_set_cache *Sets = NULL;
00253 
00254 agent_set_cache *
00255 save_set_cache(netsnmp_agent_session *asp)
00256 {
00257     agent_set_cache *ptr;
00258 
00259     if (!asp || !asp->reqinfo || !asp->pdu)
00260         return NULL;
00261 
00262     ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
00263     if (ptr == NULL)
00264         return NULL;
00265 
00266     /*
00267      * Save the important information 
00268      */
00269     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p saved in cache (mode %d)\n",
00270                 asp, asp->reqinfo, asp->pdu->command));
00271     ptr->transID = asp->pdu->transid;
00272     ptr->sess = asp->session;
00273     ptr->treecache = asp->treecache;
00274     ptr->treecache_len = asp->treecache_len;
00275     ptr->treecache_num = asp->treecache_num;
00276     ptr->agent_data = asp->reqinfo->agent_data;
00277     ptr->requests = asp->requests;
00278     ptr->saved_vars = asp->pdu->variables; /* requests contains pointers to variables */
00279     ptr->vbcount = asp->vbcount;
00280 
00281     /*
00282      * make the agent forget about what we've saved 
00283      */
00284     asp->treecache = NULL;
00285     asp->reqinfo->agent_data = NULL;
00286     asp->pdu->variables = NULL;
00287     asp->requests = NULL;
00288 
00289     ptr->next = Sets;
00290     Sets = ptr;
00291 
00292     return ptr;
00293 }
00294 
00295 int
00296 get_set_cache(netsnmp_agent_session *asp)
00297 {
00298     agent_set_cache *ptr, *prev = NULL;
00299 
00300     for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
00301         if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
00302             /*
00303              * remove this item from list
00304              */
00305             if (prev)
00306                 prev->next = ptr->next;
00307             else
00308                 Sets = ptr->next;
00309 
00310             /*
00311              * found it.  Get the needed data 
00312              */
00313             asp->treecache = ptr->treecache;
00314             asp->treecache_len = ptr->treecache_len;
00315             asp->treecache_num = ptr->treecache_num;
00316 
00317             /*
00318              * Free previously allocated requests before overwriting by
00319              * cached ones, otherwise memory leaks!
00320              */
00321             if (asp->requests) {
00322                 /*
00323                  * I don't think this case should ever happen. Please email
00324                  * the net-snmp-coders@lists.sourceforge.net if you have
00325                  * a test case that hits this assert. -- rstory
00326                  */
00327                 int i;
00328                 netsnmp_assert(NULL == asp->requests); /* see note above */
00329                 for (i = 0; i < asp->vbcount; i++) {
00330                     netsnmp_free_request_data_sets(&asp->requests[i]);
00331                 }
00332                 free(asp->requests);
00333             }
00334             /*
00335              * If we replace asp->requests with the info from the set cache,
00336              * we should replace asp->pdu->variables also with the cached
00337              * info, as asp->requests contains pointers to them.  And we
00338              * should also free the current asp->pdu->variables list...
00339              */
00340             if (ptr->saved_vars) {
00341                 if (asp->pdu->variables)
00342                     snmp_free_varbind(asp->pdu->variables);
00343                 asp->pdu->variables = ptr->saved_vars;
00344                 asp->vbcount = ptr->vbcount;
00345             } else {
00346                 /*
00347                  * when would we not have saved variables? someone
00348                  * let me know if they hit this assert. -- rstory
00349                  */
00350                 netsnmp_assert(NULL != ptr->saved_vars);
00351             }
00352             asp->requests = ptr->requests;
00353 
00354             netsnmp_assert(NULL != asp->reqinfo);
00355             asp->reqinfo->asp = asp;
00356             asp->reqinfo->agent_data = ptr->agent_data;
00357             
00358             /*
00359              * update request reqinfo, if it's out of date.
00360              * yyy-rks: investigate when/why sometimes they match,
00361              * sometimes they don't.
00362              */
00363             if(asp->requests->agent_req_info != asp->reqinfo) {
00364                 /*
00365                  * - one don't match case: agentx subagents. prev asp & reqinfo
00366                  *   freed, request reqinfo ptrs not cleared.
00367                  */
00368                 netsnmp_request_info *tmp = asp->requests;
00369                 DEBUGMSGTL(("verbose:asp",
00370                             "  reqinfo %p doesn't match cached reqinfo %p\n",
00371                             asp->reqinfo, asp->requests->agent_req_info));
00372                 for(; tmp; tmp = tmp->next)
00373                     tmp->agent_req_info = asp->reqinfo;
00374             } else {
00375                 /*
00376                  * - match case: ?
00377                  */
00378                 DEBUGMSGTL(("verbose:asp",
00379                             "  reqinfo %p matches cached reqinfo %p\n",
00380                             asp->reqinfo, asp->requests->agent_req_info));
00381             }
00382 
00383             SNMP_FREE(ptr);
00384             return SNMP_ERR_NOERROR;
00385         }
00386         prev = ptr;
00387     }
00388     return SNMP_ERR_GENERR;
00389 }
00390 
00391 /* Bulkcache holds the values for the *repeating* varbinds (only),
00392  *   but ordered "by column" - i.e. the repetitions for each
00393  *   repeating varbind follow on immediately from one another,
00394  *   rather than being interleaved, as required by the protocol.
00395  *
00396  * So we need to rearrange the varbind list so it's ordered "by row".
00397  *
00398  * In the following code chunk:
00399  *     n            = # non-repeating varbinds
00400  *     r            = # repeating varbinds
00401  *     asp->vbcount = # varbinds in the incoming PDU
00402  *         (So asp->vbcount = n+r)
00403  *
00404  *     repeats = Desired # of repetitions (of 'r' varbinds)
00405  */
00406 NETSNMP_STATIC_INLINE void
00407 _reorder_getbulk(netsnmp_agent_session *asp)
00408 {
00409     int             i, n = 0, r = 0;
00410     int             repeats = asp->pdu->errindex;
00411     int             j, k;
00412     int             all_eoMib;
00413     netsnmp_variable_list *prev = NULL, *curr;
00414             
00415     if (asp->vbcount == 0)  /* Nothing to do! */
00416         return;
00417 
00418     if (asp->pdu->errstat < asp->vbcount) {
00419         n = asp->pdu->errstat;
00420     } else {
00421         n = asp->vbcount;
00422     }
00423     if ((r = asp->vbcount - n) < 0) {
00424         r = 0;
00425     }
00426 
00427     /* we do nothing if there is nothing repeated */
00428     if (r == 0)
00429         return;
00430             
00431     /* Fix endOfMibView entries. */
00432     for (i = 0; i < r; i++) {
00433         prev = NULL;
00434         for (j = 0; j < repeats; j++) {
00435             curr = asp->bulkcache[i * repeats + j];
00436             /*
00437              *  If we don't have a valid name for a given repetition
00438              *   (and probably for all the ones that follow as well),
00439              *   extend the previous result to indicate 'endOfMibView'.
00440              *  Or if the repetition already has type endOfMibView make
00441              *   sure it has the correct objid (i.e. that of the previous
00442              *   entry or that of the original request).
00443              */
00444             if (curr->name_length == 0 || curr->type == SNMP_ENDOFMIBVIEW) {
00445                 if (prev == NULL) {
00446                     /* Use objid from original pdu. */
00447                     prev = asp->orig_pdu->variables;
00448                     for (k = i; prev && k > 0; k--)
00449                         prev = prev->next_variable;
00450                 }
00451                 if (prev) {
00452                     snmp_set_var_objid(curr, prev->name, prev->name_length);
00453                     snmp_set_var_typed_value(curr, SNMP_ENDOFMIBVIEW, NULL, 0);
00454                 }
00455             }
00456             prev = curr;
00457         }
00458     }
00459 
00460     /*
00461      * For each of the original repeating varbinds (except the last),
00462      *  go through the block of results for that varbind,
00463      *  and link each instance to the corresponding instance
00464      *  in the next block.
00465      */
00466     for (i = 0; i < r - 1; i++) {
00467         for (j = 0; j < repeats; j++) {
00468             asp->bulkcache[i * repeats + j]->next_variable =
00469                 asp->bulkcache[(i + 1) * repeats + j];
00470         }
00471     }
00472 
00473     /*
00474      * For the last of the original repeating varbinds,
00475      *  go through that block of results, and link each
00476      *  instance to the *next* instance in the *first* block.
00477      *
00478      * The very last instance of this block is left untouched
00479      *  since it (correctly) points to the end of the list.
00480      */
00481     for (j = 0; j < repeats - 1; j++) {
00482         asp->bulkcache[(r - 1) * repeats + j]->next_variable = 
00483             asp->bulkcache[j + 1];
00484     }
00485 
00486     /*
00487      * If we've got a full row of endOfMibViews, then we
00488      *  can truncate the result varbind list after that.
00489      *
00490      * Look for endOfMibView exception values in the list of
00491      *  repetitions for the first varbind, and check the 
00492      *  corresponding instances for the other varbinds
00493      *  (following the next_variable links).
00494      *
00495      * If they're all endOfMibView too, then we can terminate
00496      *  the linked list there, and free any redundant varbinds.
00497      */
00498     all_eoMib = 0;
00499     for (i = 0; i < repeats; i++) {
00500         if (asp->bulkcache[i]->type == SNMP_ENDOFMIBVIEW) {
00501             all_eoMib = 1;
00502             for (j = 1, prev=asp->bulkcache[i];
00503                  j < r;
00504                  j++, prev=prev->next_variable) {
00505                 if (prev->type != SNMP_ENDOFMIBVIEW) {
00506                     all_eoMib = 0;
00507                     break;      /* Found a real value */
00508                 }
00509             }
00510             if (all_eoMib) {
00511                 /*
00512                  * This is indeed a full endOfMibView row.
00513                  * Terminate the list here & free the rest.
00514                  */
00515                 snmp_free_varbind( prev->next_variable );
00516                 prev->next_variable = NULL;
00517                 break;
00518             }
00519         }
00520     }
00521 }
00522 
00523 
00524 /* EndOfMibView replies to a GETNEXT request should according to RFC3416
00525  *  have the object ID set to that of the request. Our tree search 
00526  *  algorithm will sometimes break that requirement. This function will
00527  *  fix that.
00528  */
00529 NETSNMP_STATIC_INLINE void
00530 _fix_endofmibview(netsnmp_agent_session *asp)
00531 {
00532     netsnmp_variable_list *vb, *ovb;
00533 
00534     if (asp->vbcount == 0)  /* Nothing to do! */
00535         return;
00536 
00537     for (vb = asp->pdu->variables, ovb = asp->orig_pdu->variables;
00538          vb && ovb; vb = vb->next_variable, ovb = ovb->next_variable) {
00539         if (vb->type == SNMP_ENDOFMIBVIEW)
00540             snmp_set_var_objid(vb, ovb->name, ovb->name_length);
00541     }
00542 }
00543 
00544 
00545 int
00546 getNextSessID()
00547 {
00548     static int      SessionID = 0;
00549 
00550     return ++SessionID;
00551 }
00552 
00565 int
00566 agent_check_and_process(int block)
00567 {
00568     int             numfds;
00569     fd_set          fdset;
00570     struct timeval  timeout = { LONG_MAX, 0 }, *tvp = &timeout;
00571     int             count;
00572     int             fakeblock = 0;
00573 
00574     numfds = 0;
00575     FD_ZERO(&fdset);
00576     snmp_select_info(&numfds, &fdset, tvp, &fakeblock);
00577     if (block != 0 && fakeblock != 0) {
00578         /*
00579          * There are no alarms registered, and the caller asked for blocking, so
00580          * let select() block forever.  
00581          */
00582 
00583         tvp = NULL;
00584     } else if (block != 0 && fakeblock == 0) {
00585         /*
00586          * The caller asked for blocking, but there is an alarm due sooner than
00587          * LONG_MAX seconds from now, so use the modified timeout returned by
00588          * snmp_select_info as the timeout for select().  
00589          */
00590 
00591     } else if (block == 0) {
00592         /*
00593          * The caller does not want us to block at all.  
00594          */
00595 
00596         tvp->tv_sec = 0;
00597         tvp->tv_usec = 0;
00598     }
00599 
00600     count = select(numfds, &fdset, 0, 0, tvp);
00601 
00602     if (count > 0) {
00603         /*
00604          * packets found, process them 
00605          */
00606         snmp_read(&fdset);
00607     } else
00608         switch (count) {
00609         case 0:
00610             snmp_timeout();
00611             break;
00612         case -1:
00613             if (errno != EINTR) {
00614                 snmp_log_perror("select");
00615             }
00616             return -1;
00617         default:
00618             snmp_log(LOG_ERR, "select returned %d\n", count);
00619             return -1;
00620         }                       /* endif -- count>0 */
00621 
00622     /*
00623      * Run requested alarms.  
00624      */
00625     run_alarms();
00626 
00627     netsnmp_check_outstanding_agent_requests();
00628 
00629     return count;
00630 }
00631 
00632 
00633 /*
00634  * Set up the address cache.  
00635  */
00636 void
00637 netsnmp_addrcache_initialise(void)
00638 {
00639     int             i = 0;
00640 
00641     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
00642         addrCache[i].addr = NULL;
00643         addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
00644     }
00645 }
00646 
00647 /*
00648  * Adds a new entry to the cache of addresses that
00649  * have recently made connections to the agent.
00650  * Returns 0 if the entry already exists (but updates
00651  * the entry with a new timestamp) and 1 if the
00652  * entry did not previously exist.
00653  *
00654  * Implements a simple LRU cache replacement
00655  * policy. Uses a linear search, which should be
00656  * okay, as long as SNMP_ADDRCACHE_SIZE remains
00657  * relatively small.
00658  *
00659  * @retval 0 : updated existing entry
00660  * @retval 1 : added new entry
00661  */
00662 int
00663 netsnmp_addrcache_add(const char *addr)
00664 {
00665     int oldest = -1; /* Index of the oldest cache entry */
00666     int unused = -1; /* Index of the first free cache entry */
00667     int i; /* Looping variable */
00668     int rc = -1;
00669     struct timeval now; /* What time is it now? */
00670     struct timeval aged; /* Oldest allowable cache entry */
00671 
00672     /*
00673      * First get the current and oldest allowable timestamps
00674      */
00675     gettimeofday(&now, (struct timezone*) NULL);
00676     aged.tv_sec = now.tv_sec - SNMP_ADDRCACHE_MAXAGE;
00677     aged.tv_usec = now.tv_usec;
00678 
00679     /*
00680      * Now look for a place to put this thing
00681      */
00682     for(i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
00683         if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) { /* If unused */
00684             /*
00685              * remember this location, in case addr isn't in the cache
00686              */
00687             if (unused < 0)
00688                 unused = i;
00689         }
00690         else { /* If used */
00691             if ((NULL != addr) && (strcmp(addrCache[i].addr, addr) == 0)) {
00692                 /*
00693                  * found a match
00694                  */
00695                 memcpy(&addrCache[i].lastHit, &now, sizeof(struct timeval));
00696                 if (timercmp(&addrCache[i].lastHit, &aged, <))
00697                     rc = 1; /* should have expired, so is new */
00698                 else
00699                     rc = 0; /* not expired, so is existing entry */
00700                 break;
00701             }
00702             else {
00703                 /*
00704                  * Used, but not this address. check if it's stale.
00705                  */
00706                 if (timercmp(&addrCache[i].lastHit, &aged, <)) {
00707                     /*
00708                      * Stale, reuse
00709                      */
00710                     SNMP_FREE(addrCache[i].addr);
00711                     addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
00712                     /*
00713                      * remember this location, in case addr isn't in the cache
00714                      */
00715                     if (unused < 0)
00716                         unused = i;
00717                 }
00718                 else {
00719                     /*
00720                      * Still fresh, but a candidate for LRU replacement
00721                      */
00722                     if (oldest < 0)
00723                         oldest = i;
00724                     else if (timercmp(&addrCache[i].lastHit,
00725                                       &addrCache[oldest].lastHit, <))
00726                         oldest = i;
00727                 } /* fresh */
00728             } /* used, no match */
00729         } /* used */
00730     } /* for loop */
00731 
00732     if ((-1 == rc) && (NULL != addr)) {
00733         /*
00734          * We didn't find the entry in the cache
00735          */
00736         if (unused >= 0) {
00737             /*
00738              * If we have a slot free anyway, use it
00739              */
00740             addrCache[unused].addr = strdup(addr);
00741             addrCache[unused].status = SNMP_ADDRCACHE_USED;
00742             memcpy(&addrCache[unused].lastHit, &now, sizeof(struct timeval));
00743         }
00744         else { /* Otherwise, replace oldest entry */
00745             if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00746                                        NETSNMP_DS_AGENT_VERBOSE))
00747                 snmp_log(LOG_INFO, "Purging address from address cache: %s",
00748                          addrCache[oldest].addr);
00749             
00750             free(addrCache[oldest].addr);
00751             addrCache[oldest].addr = strdup(addr);
00752             memcpy(&addrCache[oldest].lastHit, &now, sizeof(struct timeval));
00753         }
00754         rc = 1;
00755     }
00756     if ((log_addresses && (1 == rc)) ||
00757         netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00758                                NETSNMP_DS_AGENT_VERBOSE)) {
00759         snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n", addr);
00760      }
00761 
00762     return rc;
00763 }
00764 
00765 /*
00766  * Age the entries in the address cache.  
00767  *
00768  * backwards compatability; not used anywhere
00769  */
00770 void
00771 netsnmp_addrcache_age(void)
00772 {
00773     (void)netsnmp_addrcache_add(NULL);
00774 }
00775 
00776 /*******************************************************************-o-******
00777  * netsnmp_agent_check_packet
00778  *
00779  * Parameters:
00780  *      session, transport, transport_data, transport_data_length
00781  *      
00782  * Returns:
00783  *      1       On success.
00784  *      0       On error.
00785  *
00786  * Handler for all incoming messages (a.k.a. packets) for the agent.  If using
00787  * the libwrap utility, log the connection and deny/allow the access. Print
00788  * output when appropriate, and increment the incoming counter.
00789  *
00790  */
00791 
00792 int
00793 netsnmp_agent_check_packet(netsnmp_session * session,
00794                            netsnmp_transport *transport,
00795                            void *transport_data, int transport_data_length)
00796 {
00797     char           *addr_string = NULL;
00798 #ifdef  NETSNMP_USE_LIBWRAP
00799     char *tcpudpaddr, *name;
00800     short not_log_connection;
00801 
00802     name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
00803                                  NETSNMP_DS_LIB_APPTYPE);
00804 
00805     /* not_log_connection will be 1 if we should skip the messages */
00806     not_log_connection = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00807                                                 NETSNMP_DS_AGENT_DONT_LOG_TCPWRAPPERS_CONNECTS);
00808 
00809     /*
00810      * handle the error case
00811      * default to logging the messages
00812      */
00813     if (not_log_connection == SNMPERR_GENERR) not_log_connection = 0;
00814 #endif
00815 
00816     /*
00817      * Log the message and/or dump the message.
00818      * Optionally cache the network address of the sender.
00819      */
00820 
00821     if (transport != NULL && transport->f_fmtaddr != NULL) {
00822         /*
00823          * Okay I do know how to format this address for logging.  
00824          */
00825         addr_string = transport->f_fmtaddr(transport, transport_data,
00826                                            transport_data_length);
00827         /*
00828          * Don't forget to free() it.  
00829          */
00830     }
00831 #ifdef  NETSNMP_USE_LIBWRAP
00832     /* Catch udp,udp6,tcp,tcp6 transports using "[" */
00833     tcpudpaddr = strstr(addr_string, "[");
00834     if ( tcpudpaddr != 0 ) {
00835         char sbuf[64];
00836         char *xp;
00837         strncpy(sbuf, tcpudpaddr + 1, sizeof(sbuf));
00838         sbuf[sizeof(sbuf)-1] = '\0';
00839         xp = strstr(sbuf, "]");
00840         if (xp)
00841             *xp = '\0';
00842  
00843         if (hosts_ctl(name, STRING_UNKNOWN, sbuf, STRING_UNKNOWN)) {
00844             if (!not_log_connection) {
00845                 snmp_log(allow_severity, "Connection from %s\n", addr_string);
00846             }
00847         } else {
00848             snmp_log(deny_severity, "Connection from %s REFUSED\n",
00849                      addr_string);
00850             SNMP_FREE(addr_string);
00851             return 0;
00852         }
00853     } else {
00854         /*
00855          * don't log callback connections.
00856          * What about 'Local IPC', 'IPX' and 'AAL5 PVC'?
00857          */
00858         if (0 == strncmp(addr_string, "callback", 8))
00859             ;
00860         else if (hosts_ctl(name, STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
00861             if (!not_log_connection) {
00862                 snmp_log(allow_severity, "Connection from <UNKNOWN> (%s)\n", addr_string);
00863             };
00864             SNMP_FREE(addr_string);
00865             addr_string = strdup("<UNKNOWN>");
00866         } else {
00867             snmp_log(deny_severity, "Connection from <UNKNOWN> (%s) REFUSED\n", addr_string);
00868             SNMP_FREE(addr_string);
00869             return 0;
00870         }
00871     }
00872 #endif                          /*NETSNMP_USE_LIBWRAP */
00873 
00874     snmp_increment_statistic(STAT_SNMPINPKTS);
00875 
00876     if (addr_string != NULL) {
00877         netsnmp_addrcache_add(addr_string);
00878         SNMP_FREE(addr_string);
00879     }
00880     return 1;
00881 }
00882 
00883 
00884 int
00885 netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
00886                           int result)
00887 {
00888     if (result == 0) {
00889         if (snmp_get_do_logging() &&
00890             netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
00891                                    NETSNMP_DS_AGENT_VERBOSE)) {
00892             netsnmp_variable_list *var_ptr;
00893 
00894             switch (pdu->command) {
00895             case SNMP_MSG_GET:
00896                 snmp_log(LOG_DEBUG, "  GET message\n");
00897                 break;
00898             case SNMP_MSG_GETNEXT:
00899                 snmp_log(LOG_DEBUG, "  GETNEXT message\n");
00900                 break;
00901             case SNMP_MSG_RESPONSE:
00902                 snmp_log(LOG_DEBUG, "  RESPONSE message\n");
00903                 break;
00904             case SNMP_MSG_SET:
00905                 snmp_log(LOG_DEBUG, "  SET message\n");
00906                 break;
00907             case SNMP_MSG_TRAP:
00908                 snmp_log(LOG_DEBUG, "  TRAP message\n");
00909                 break;
00910             case SNMP_MSG_GETBULK:
00911                 snmp_log(LOG_DEBUG, "  GETBULK message, non-rep=%ld, max_rep=%ld\n",
00912                          pdu->errstat, pdu->errindex);
00913                 break;
00914             case SNMP_MSG_INFORM:
00915                 snmp_log(LOG_DEBUG, "  INFORM message\n");
00916                 break;
00917             case SNMP_MSG_TRAP2:
00918                 snmp_log(LOG_DEBUG, "  TRAP2 message\n");
00919                 break;
00920             case SNMP_MSG_REPORT:
00921                 snmp_log(LOG_DEBUG, "  REPORT message\n");
00922                 break;
00923 
00924             case SNMP_MSG_INTERNAL_SET_RESERVE1:
00925                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE1 message\n");
00926                 break;
00927 
00928             case SNMP_MSG_INTERNAL_SET_RESERVE2:
00929                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE2 message\n");
00930                 break;
00931 
00932             case SNMP_MSG_INTERNAL_SET_ACTION:
00933                 snmp_log(LOG_DEBUG, "  INTERNAL ACTION message\n");
00934                 break;
00935 
00936             case SNMP_MSG_INTERNAL_SET_COMMIT:
00937                 snmp_log(LOG_DEBUG, "  INTERNAL COMMIT message\n");
00938                 break;
00939 
00940             case SNMP_MSG_INTERNAL_SET_FREE:
00941                 snmp_log(LOG_DEBUG, "  INTERNAL FREE message\n");
00942                 break;
00943 
00944             case SNMP_MSG_INTERNAL_SET_UNDO:
00945                 snmp_log(LOG_DEBUG, "  INTERNAL UNDO message\n");
00946                 break;
00947 
00948             default:
00949                 snmp_log(LOG_DEBUG, "  UNKNOWN message, type=%02X\n",
00950                          pdu->command);
00951                 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
00952                 return 0;
00953             }
00954 
00955             for (var_ptr = pdu->variables; var_ptr != NULL;
00956                  var_ptr = var_ptr->next_variable) {
00957                 size_t          c_oidlen = 256, c_outlen = 0;
00958                 u_char         *c_oid = (u_char *) malloc(c_oidlen);
00959 
00960                 if (c_oid) {
00961                     if (!sprint_realloc_objid
00962                         (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
00963                          var_ptr->name_length)) {
00964                         snmp_log(LOG_DEBUG, "    -- %s [TRUNCATED]\n",
00965                                  c_oid);
00966                     } else {
00967                         snmp_log(LOG_DEBUG, "    -- %s\n", c_oid);
00968                     }
00969                     SNMP_FREE(c_oid);
00970                 }
00971             }
00972         }
00973         return 1;
00974     }
00975     return 0;                   /* XXX: does it matter what the return value
00976                                  * is?  Yes: if we return 0, then the PDU is
00977                                  * dumped.  */
00978 }
00979 
00980 
00981 /*
00982  * Global access to the primary session structure for this agent.
00983  * for Index Allocation use initially. 
00984  */
00985 
00986 /*
00987  * I don't understand what this is for at the moment.  AFAICS as long as it
00988  * gets set and points at a session, that's fine.  ???  
00989  */
00990 
00991 netsnmp_session *main_session = NULL;
00992 
00993 
00994 
00995 /*
00996  * Set up an agent session on the given transport.  Return a handle
00997  * which may later be used to de-register this transport.  A return
00998  * value of -1 indicates an error.  
00999  */
01000 
01001 int
01002 netsnmp_register_agent_nsap(netsnmp_transport *t)
01003 {
01004     netsnmp_session *s, *sp = NULL;
01005     agent_nsap     *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
01006     int             handle = 0;
01007     void           *isp = NULL;
01008 
01009     if (t == NULL) {
01010         return -1;
01011     }
01012 
01013     DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
01014 
01015     n = (agent_nsap *) malloc(sizeof(agent_nsap));
01016     if (n == NULL) {
01017         return -1;
01018     }
01019     s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
01020     if (s == NULL) {
01021         SNMP_FREE(n);
01022         return -1;
01023     }
01024     memset(s, 0, sizeof(netsnmp_session));
01025     snmp_sess_init(s);
01026 
01027     /*
01028      * Set up the session appropriately for an agent.  
01029      */
01030 
01031     s->version = SNMP_DEFAULT_VERSION;
01032     s->callback = handle_snmp_packet;
01033     s->authenticator = NULL;
01034     s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 
01035                                   NETSNMP_DS_AGENT_FLAGS);
01036     s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
01037 
01038     sp = snmp_add(s, t, netsnmp_agent_check_packet,
01039                   netsnmp_agent_check_parse);
01040     if (sp == NULL) {
01041         SNMP_FREE(s);
01042         SNMP_FREE(n);
01043         return -1;
01044     }
01045 
01046     isp = snmp_sess_pointer(sp);
01047     if (isp == NULL) {          /*  over-cautious  */
01048         SNMP_FREE(s);
01049         SNMP_FREE(n);
01050         return -1;
01051     }
01052 
01053     n->s = isp;
01054     n->t = t;
01055 
01056     if (main_session == NULL) {
01057         main_session = snmp_sess_session(isp);
01058     }
01059 
01060     for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
01061          a = a->next) {
01062         handle = a->handle;
01063         prevNext = &(a->next);
01064     }
01065 
01066     if (handle < INT_MAX) {
01067         n->handle = handle + 1;
01068         n->next = a;
01069         *prevNext = n;
01070         SNMP_FREE(s);
01071         return n->handle;
01072     } else {
01073         SNMP_FREE(s);
01074         SNMP_FREE(n);
01075         return -1;
01076     }
01077 }
01078 
01079 void
01080 netsnmp_deregister_agent_nsap(int handle)
01081 {
01082     agent_nsap     *a = NULL, **prevNext = &agent_nsap_list;
01083     int             main_session_deregistered = 0;
01084 
01085     DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
01086 
01087     for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
01088         prevNext = &(a->next);
01089     }
01090 
01091     if (a != NULL && a->handle == handle) {
01092         *prevNext = a->next;
01093         if (main_session == snmp_sess_session(a->s)) {
01094             main_session_deregistered = 1;
01095         }
01096         snmp_close(snmp_sess_session(a->s));
01097         /*
01098          * The above free()s the transport and session pointers.  
01099          */
01100         SNMP_FREE(a);
01101     }
01102 
01103     /*
01104      * If we've deregistered the session that main_session used to point to,
01105      * then make it point to another one, or in the last resort, make it equal
01106      * to NULL.  Basically this shouldn't ever happen in normal operation
01107      * because main_session starts off pointing at the first session added by
01108      * init_master_agent(), which then discards the handle.  
01109      */
01110 
01111     if (main_session_deregistered) {
01112         if (agent_nsap_list != NULL) {
01113             DEBUGMSGTL(("snmp_agent",
01114                         "WARNING: main_session ptr changed from %p to %p\n",
01115                         main_session, snmp_sess_session(agent_nsap_list->s)));
01116             main_session = snmp_sess_session(agent_nsap_list->s);
01117         } else {
01118             DEBUGMSGTL(("snmp_agent",
01119                         "WARNING: main_session ptr changed from %p to NULL\n",
01120                         main_session));
01121             main_session = NULL;
01122         }
01123     }
01124 }
01125 
01126 
01127 
01128 /*
01129  * 
01130  * This function has been modified to use the experimental
01131  * netsnmp_register_agent_nsap interface.  The major responsibility of this
01132  * function now is to interpret a string specified to the agent (via -p on the
01133  * command line, or from a configuration file) as a list of agent NSAPs on
01134  * which to listen for SNMP packets.  Typically, when you add a new transport
01135  * domain "foo", you add code here such that if the "foo" code is compiled
01136  * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
01137  * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
01138  * transport descriptor.  netsnmp_register_agent_nsap is then called with that
01139  * transport descriptor and sets up a listening agent session on it.
01140  * 
01141  * Everything then works much as normal: the agent runs in an infinite loop
01142  * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
01143  * is readable on any of the given transports.  This routine then traverses
01144  * the library 'Sessions' list to identify the relevant session and eventually
01145  * invokes '_sess_read'.  This then processes the incoming packet, calling the
01146  * pre_parse, parse, post_parse and callback routines in turn.
01147  * 
01148  * JBPN 20001117
01149  */
01150 
01151 int
01152 init_master_agent(void)
01153 {
01154     netsnmp_transport *transport;
01155     char           *cptr;
01156     char            buf[SPRINT_MAX_LEN];
01157     char           *st;
01158 
01159     /* default to a default cache size */
01160     netsnmp_set_lookup_cache_size(-1);
01161 
01162     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
01163                                NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
01164         DEBUGMSGTL(("snmp_agent",
01165                     "init_master_agent; not master agent\n"));
01166 
01167         netsnmp_assert("agent role !master && !sub_agent");
01168         
01169         return 0;               /*  No error if ! MASTER_AGENT  */
01170     }
01171 #ifdef USING_AGENTX_MASTER_MODULE
01172     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
01173                                NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
01174         real_init_master();
01175 #endif
01176 #ifdef USING_SMUX_MODULE
01177     if(should_init("smux"))
01178     real_init_smux();
01179 #endif
01180 
01181     /*
01182      * Have specific agent ports been specified?  
01183      */
01184     cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
01185                                  NETSNMP_DS_AGENT_PORTS);
01186 
01187     if (cptr) {
01188         snprintf(buf, sizeof(buf), "%s", cptr);
01189         buf[ sizeof(buf)-1 ] = 0;
01190     } else {
01191         /*
01192          * No, so just specify the default port.  
01193          */
01194         buf[0] = 0;
01195     }
01196 
01197     DEBUGMSGTL(("snmp_agent", "final port spec: \"%s\"\n", buf));
01198     st = buf;
01199     do {
01200         /*
01201          * Specification format: 
01202          * 
01203          * NONE:                      (a pseudo-transport)
01204          * UDP:[address:]port        (also default if no transport is specified)
01205          * TCP:[address:]port         (if supported)
01206          * Unix:pathname              (if supported)
01207          * AAL5PVC:itf.vpi.vci        (if supported)
01208          * IPX:[network]:node[/port] (if supported)
01209          * 
01210          */
01211 
01212         cptr = st;
01213         st = strchr(st, ',');
01214         if (st)
01215             *st++ = '\0';
01216 
01217         DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
01218                     cptr));
01219 
01220         if (strncasecmp(cptr, "none", 4) == 0) {
01221             DEBUGMSGTL(("snmp_agent",
01222                         "init_master_agent; pseudo-transport \"none\" "
01223                         "requested\n"));
01224             return 0;
01225         }
01226         transport = netsnmp_transport_open_server("snmp", cptr);
01227 
01228         if (transport == NULL) {
01229             snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n",
01230                      cptr);
01231             return 1;
01232         }
01233 
01234         if (netsnmp_register_agent_nsap(transport) == 0) {
01235             snmp_log(LOG_ERR,
01236                      "Error registering specified transport \"%s\" as an "
01237                      "agent NSAP\n", cptr);
01238             return 1;
01239         } else {
01240             DEBUGMSGTL(("snmp_agent",
01241                         "init_master_agent; \"%s\" registered as an agent "
01242                         "NSAP\n", cptr));
01243         }
01244     } while(st && *st != '\0');
01245 
01246     return 0;
01247 }
01248 
01249 void
01250 clear_nsap_list(void)
01251 {
01252     DEBUGMSGTL(("clear_nsap_list", "clear the nsap list\n"));
01253 
01254     while (agent_nsap_list != NULL)
01255         netsnmp_deregister_agent_nsap(agent_nsap_list->handle);
01256 }
01257 
01258 void
01259 shutdown_master_agent(void)
01260 {
01261     clear_nsap_list();
01262 }
01263 
01264 
01265 netsnmp_agent_session *
01266 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
01267 {
01268     netsnmp_agent_session *asp = (netsnmp_agent_session *)
01269         calloc(1, sizeof(netsnmp_agent_session));
01270 
01271     if (asp == NULL) {
01272         return NULL;
01273     }
01274 
01275     DEBUGMSGTL(("snmp_agent","agent_sesion %08p created\n", asp));
01276     asp->session = session;
01277     asp->pdu = snmp_clone_pdu(pdu);
01278     asp->orig_pdu = snmp_clone_pdu(pdu);
01279     asp->rw = READ;
01280     asp->exact = TRUE;
01281     asp->next = NULL;
01282     asp->mode = RESERVE1;
01283     asp->status = SNMP_ERR_NOERROR;
01284     asp->index = 0;
01285     asp->oldmode = 0;
01286     asp->treecache_num = -1;
01287     asp->treecache_len = 0;
01288     asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
01289     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p created\n",
01290                 asp, asp->reqinfo));
01291 
01292     return asp;
01293 }
01294 
01295 void
01296 free_agent_snmp_session(netsnmp_agent_session *asp)
01297 {
01298     if (!asp)
01299         return;
01300 
01301     DEBUGMSGTL(("snmp_agent","agent_session %08p released\n", asp));
01302 
01303     netsnmp_remove_from_delegated(asp);
01304     
01305     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p freed\n",
01306                 asp, asp->reqinfo));
01307     if (asp->orig_pdu)
01308         snmp_free_pdu(asp->orig_pdu);
01309     if (asp->pdu)
01310         snmp_free_pdu(asp->pdu);
01311     if (asp->reqinfo)
01312         netsnmp_free_agent_request_info(asp->reqinfo);
01313     if (asp->treecache) {
01314         SNMP_FREE(asp->treecache);
01315     }
01316     if (asp->bulkcache) {
01317         SNMP_FREE(asp->bulkcache);
01318     }
01319     if (asp->requests) {
01320         int             i;
01321         for (i = 0; i < asp->vbcount; i++) {
01322             netsnmp_free_request_data_sets(&asp->requests[i]);
01323         }
01324         SNMP_FREE(asp->requests);
01325     }
01326     if (asp->cache_store) {
01327         netsnmp_free_cachemap(asp->cache_store);
01328         asp->cache_store = NULL;
01329     }
01330     SNMP_FREE(asp);
01331 }
01332 
01333 int
01334 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
01335 {
01336     int             i;
01337     netsnmp_request_info *request;
01338 
01339     if (NULL == asp->treecache)
01340         return 0;
01341     
01342     for (i = 0; i <= asp->treecache_num; i++) {
01343         for (request = asp->treecache[i].requests_begin; request;
01344              request = request->next) {
01345             if (request->delegated)
01346                 return 1;
01347         }
01348     }
01349     return 0;
01350 }
01351 
01352 int
01353 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
01354 {
01355     netsnmp_agent_session *asptmp;
01356     for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
01357         if (asptmp == asp)
01358             return 1;
01359     }
01360     return 0;
01361 }
01362 
01363 int
01364 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
01365 {
01366     if (netsnmp_check_for_delegated(asp)) {
01367         if (!netsnmp_check_delegated_chain_for(asp)) {
01368             /*
01369              * add to delegated request chain 
01370              */
01371             asp->next = agent_delegated_list;
01372             agent_delegated_list = asp;
01373             DEBUGMSGTL(("snmp_agent", "delegate session == %08p\n", asp));
01374         }
01375         return 1;
01376     }
01377     return 0;
01378 }
01379 
01380 int
01381 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
01382 {
01383     netsnmp_agent_session *curr, *prev = NULL;
01384     
01385     for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
01386         /*
01387          * is this us?
01388          */
01389         if (curr != asp)
01390             continue;
01391         
01392         /*
01393          * remove from queue 
01394          */
01395         if (prev != NULL)
01396             prev->next = asp->next;
01397         else
01398             agent_delegated_list = asp->next;
01399 
01400         DEBUGMSGTL(("snmp_agent", "remove delegated session == %08p\n", asp));
01401 
01402         return 1;
01403     }
01404 
01405     return 0;
01406 }
01407 
01408 /*
01409  * netsnmp_remove_delegated_requests_for_session
01410  *
01411  * called when a session is being closed. Check all delegated requests to
01412  * see if the are waiting on this session, and if set, set the status for
01413  * that request to GENERR.
01414  */
01415 int
01416 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
01417 {
01418     netsnmp_agent_session *asp;
01419     int count = 0;
01420     
01421     for (asp = agent_delegated_list; asp; asp = asp->next) {
01422         /*
01423          * check each request
01424          */
01425         netsnmp_request_info *request;
01426         for(request = asp->requests; request; request = request->next) {
01427             /*
01428              * check session
01429              */
01430             netsnmp_assert(NULL!=request->subtree);
01431             if(request->subtree->session != sess)
01432                 continue;
01433 
01434             /*
01435              * matched! mark request as done
01436              */
01437             netsnmp_request_set_error(request, SNMP_ERR_GENERR);
01438             ++count;
01439         }
01440     }
01441 
01442     /*
01443      * if we found any, that request may be finished now
01444      */
01445     if(count) {
01446         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
01447                     "%08p\n", count, sess));
01448         netsnmp_check_outstanding_agent_requests();
01449     }
01450     
01451     return count;
01452 }
01453 
01454 int
01455 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
01456 {
01457     netsnmp_agent_session *asptmp;
01458     for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
01459         if (asptmp == asp)
01460             return 1;
01461     }
01462     return 0;
01463 }
01464 
01465 int
01466 netsnmp_add_queued(netsnmp_agent_session *asp)
01467 {
01468     netsnmp_agent_session *asp_tmp;
01469 
01470     /*
01471      * first item?
01472      */
01473     if (NULL == netsnmp_agent_queued_list) {
01474         netsnmp_agent_queued_list = asp;
01475         return 1;
01476     }
01477 
01478 
01479     /*
01480      * add to end of queued request chain 
01481      */
01482     asp_tmp = netsnmp_agent_queued_list;
01483     for (; asp_tmp; asp_tmp = asp_tmp->next) {
01484         /*
01485          * already in queue?
01486          */
01487         if (asp_tmp == asp)
01488             break;
01489 
01490         /*
01491          * end of queue?
01492          */
01493         if (NULL == asp_tmp->next)
01494             asp_tmp->next = asp;
01495     }
01496     return 1;
01497 }
01498 
01499 
01500 int
01501 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
01502 {
01503     netsnmp_variable_list *var_ptr;
01504     int             i;
01505 
01506     /*
01507      * if this request was a set, clear the global now that we are
01508      * done.
01509      */
01510     if (asp == netsnmp_processing_set) {
01511         DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %08p\n",
01512                     asp));
01513         netsnmp_processing_set = NULL;
01514     }
01515 
01516     if (asp->pdu) {
01517         /*
01518          * If we've got an error status, then this needs to be
01519          *  passed back up to the higher levels....
01520          */
01521         if ( status != 0  && asp->status == 0 )
01522             asp->status = status;
01523 
01524         switch (asp->pdu->command) {
01525             case SNMP_MSG_INTERNAL_SET_BEGIN:
01526             case SNMP_MSG_INTERNAL_SET_RESERVE1:
01527             case SNMP_MSG_INTERNAL_SET_RESERVE2:
01528             case SNMP_MSG_INTERNAL_SET_ACTION:
01529                 /*
01530                  * some stuff needs to be saved in special subagent cases 
01531                  */
01532                 save_set_cache(asp);
01533                 break;
01534 
01535             case SNMP_MSG_GETNEXT:
01536                 _fix_endofmibview(asp);
01537                 break;
01538 
01539             case SNMP_MSG_GETBULK:
01540                 /*
01541                  * for a GETBULK response we need to rearrange the varbinds 
01542                  */
01543                 _reorder_getbulk(asp);
01544                 break;
01545         }
01546 
01547         /*
01548          * May need to "dumb down" a SET error status for a
01549          * v1 query.  See RFC2576 - section 4.3
01550          */
01551 #ifndef NETSNMP_DISABLE_SNMPV1
01552         if ((asp->pdu->command == SNMP_MSG_SET) &&
01553             (asp->pdu->version == SNMP_VERSION_1)) {
01554             switch (asp->status) {
01555                 case SNMP_ERR_WRONGVALUE:
01556                 case SNMP_ERR_WRONGENCODING:
01557                 case SNMP_ERR_WRONGTYPE:
01558                 case SNMP_ERR_WRONGLENGTH:
01559                 case SNMP_ERR_INCONSISTENTVALUE:
01560                     status = SNMP_ERR_BADVALUE;
01561                     asp->status = SNMP_ERR_BADVALUE;
01562                     break;
01563                 case SNMP_ERR_NOACCESS:
01564                 case SNMP_ERR_NOTWRITABLE:
01565                 case SNMP_ERR_NOCREATION:
01566                 case SNMP_ERR_INCONSISTENTNAME:
01567                 case SNMP_ERR_AUTHORIZATIONERROR:
01568                     status = SNMP_ERR_NOSUCHNAME;
01569                     asp->status = SNMP_ERR_NOSUCHNAME;
01570                     break;
01571                 case SNMP_ERR_RESOURCEUNAVAILABLE:
01572                 case SNMP_ERR_COMMITFAILED:
01573                 case SNMP_ERR_UNDOFAILED:
01574                     status = SNMP_ERR_GENERR;
01575                     asp->status = SNMP_ERR_GENERR;
01576                     break;
01577             }
01578         }
01579         /*
01580          * Similarly we may need to "dumb down" v2 exception
01581          *  types to throw an error for a v1 query.
01582          *  See RFC2576 - section 4.1.2.3
01583          */
01584         if ((asp->pdu->command != SNMP_MSG_SET) &&
01585             (asp->pdu->version == SNMP_VERSION_1)) {
01586             for (var_ptr = asp->pdu->variables, i = 1;
01587                  var_ptr != NULL; var_ptr = var_ptr->next_variable, i++) {
01588                 switch (var_ptr->type) {
01589                     case SNMP_NOSUCHOBJECT:
01590                     case SNMP_NOSUCHINSTANCE:
01591                     case SNMP_ENDOFMIBVIEW:
01592                     case ASN_COUNTER64:
01593                         status = SNMP_ERR_NOSUCHNAME;
01594                         asp->status = SNMP_ERR_NOSUCHNAME;
01595                         asp->index = i;
01596                         break;
01597                 }
01598             }
01599         }
01600 #endif /* snmpv1 support */
01601     } 
01603     /*
01604      * Update the snmp error-count statistics
01605      *   XXX - should we include the V2 errors in this or not?
01606      */
01607 #define INCLUDE_V2ERRORS_IN_V1STATS
01608 
01609     switch (status) {
01610 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01611     case SNMP_ERR_WRONGVALUE:
01612     case SNMP_ERR_WRONGENCODING:
01613     case SNMP_ERR_WRONGTYPE:
01614     case SNMP_ERR_WRONGLENGTH:
01615     case SNMP_ERR_INCONSISTENTVALUE:
01616 #endif
01617     case SNMP_ERR_BADVALUE:
01618         snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
01619         break;
01620 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01621     case SNMP_ERR_NOACCESS:
01622     case SNMP_ERR_NOTWRITABLE:
01623     case SNMP_ERR_NOCREATION:
01624     case SNMP_ERR_INCONSISTENTNAME:
01625     case SNMP_ERR_AUTHORIZATIONERROR:
01626 #endif
01627     case SNMP_ERR_NOSUCHNAME:
01628         snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
01629         break;
01630 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01631     case SNMP_ERR_RESOURCEUNAVAILABLE:
01632     case SNMP_ERR_COMMITFAILED:
01633     case SNMP_ERR_UNDOFAILED:
01634 #endif
01635     case SNMP_ERR_GENERR:
01636         snmp_increment_statistic(STAT_SNMPOUTGENERRS);
01637         break;
01638 
01639     case SNMP_ERR_TOOBIG:
01640         snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
01641         break;
01642     }
01643 
01644     if ((status == SNMP_ERR_NOERROR) && (asp->pdu)) {
01645         snmp_increment_statistic_by((asp->pdu->command == SNMP_MSG_SET ?
01646                                      STAT_SNMPINTOTALSETVARS :
01647                                      STAT_SNMPINTOTALREQVARS),
01648                                     count_varbinds(asp->pdu->variables));
01649     } else {
01650         /*
01651          * Use a copy of the original request
01652          *   to report failures.
01653          */
01654         snmp_free_pdu(asp->pdu);
01655         asp->pdu = asp->orig_pdu;
01656         asp->orig_pdu = NULL;
01657     }
01658     if (asp->pdu) {
01659         asp->pdu->command = SNMP_MSG_RESPONSE;
01660         asp->pdu->errstat = asp->status;
01661         asp->pdu->errindex = asp->index;
01662         if (!snmp_send(asp->session, asp->pdu)) {
01663             netsnmp_variable_list *var_ptr;
01664             snmp_perror("send response");
01665             for (var_ptr = asp->pdu->variables; var_ptr != NULL;
01666                      var_ptr = var_ptr->next_variable) {
01667                 size_t  c_oidlen = 256, c_outlen = 0;
01668                 u_char *c_oid = (u_char *) malloc(c_oidlen);
01669 
01670                 if (c_oid) {
01671                     if (!sprint_realloc_objid (&c_oid, &c_oidlen, &c_outlen, 1,
01672                                                var_ptr->name,
01673                                                var_ptr->name_length)) {
01674                         snmp_log(LOG_ERR, "    -- %s [TRUNCATED]\n", c_oid);
01675                     } else {
01676                         snmp_log(LOG_ERR, "    -- %s\n", c_oid);
01677                     }
01678                     SNMP_FREE(c_oid);
01679                 }
01680             }
01681             snmp_free_pdu(asp->pdu);
01682             asp->pdu = NULL;
01683         }
01684         snmp_increment_statistic(STAT_SNMPOUTPKTS);
01685         snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
01686         asp->pdu = NULL; /* yyy-rks: redundant, no? */
01687         netsnmp_remove_and_free_agent_snmp_session(asp);
01688     }
01689     return 1;
01690 }
01691 
01692 void
01693 dump_sess_list(void)
01694 {
01695     netsnmp_agent_session *a;
01696 
01697     DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
01698     for (a = agent_session_list; a != NULL; a = a->next) {
01699         DEBUGMSG(("snmp_agent", "%08p[session %08p] -> ", a, a->session));
01700     }
01701     DEBUGMSG(("snmp_agent", "[NIL]\n"));
01702 }
01703 
01704 void
01705 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
01706 {
01707     netsnmp_agent_session *a, **prevNext = &agent_session_list;
01708 
01709     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", asp));
01710 
01711     for (a = agent_session_list; a != NULL; a = *prevNext) {
01712         if (a == asp) {
01713             *prevNext = a->next;
01714             a->next = NULL;
01715             free_agent_snmp_session(a);
01716             asp = NULL;
01717             break;
01718         } else {
01719             prevNext = &(a->next);
01720         }
01721     }
01722 
01723     if (a == NULL && asp != NULL) {
01724         /*
01725          * We coulnd't find it on the list, so free it anyway.  
01726          */
01727         free_agent_snmp_session(asp);
01728     }
01729 }
01730 
01731 void
01732 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
01733                                            void (*free_request)
01734                                            (netsnmp_request_list *))
01735 {
01736     netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
01737 
01738     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", sess));
01739 
01740     for (a = agent_session_list; a != NULL; a = next) {
01741         if (a->session == sess) {
01742             *prevNext = a->next;
01743             next = a->next;
01744             free_agent_snmp_session(a);
01745         } else {
01746             prevNext = &(a->next);
01747             next = a->next;
01748         }
01749     }
01750 }
01751 
01753 int
01754 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
01755                    netsnmp_pdu *pdu, void *magic)
01756 {
01757     netsnmp_agent_session *asp;
01758     int             status, access_ret, rc;
01759 
01760     /*
01761      * We only support receiving here.  
01762      */
01763     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
01764         return 1;
01765     }
01766 
01767     /*
01768      * RESPONSE messages won't get this far, but TRAP-like messages
01769      * might.  
01770      */
01771     if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
01772         pdu->command == SNMP_MSG_TRAP2) {
01773         DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
01774                     pdu->command));
01775         pdu->command = SNMP_MSG_TRAP2;
01776         snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
01777         return 1;
01778     }
01779 
01780     /*
01781      * send snmpv3 authfail trap.
01782      */
01783     if (pdu->version  == SNMP_VERSION_3 && 
01784         session->s_snmp_errno == SNMPERR_USM_AUTHENTICATIONFAILURE) {
01785            send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
01786            return 1;
01787     } 
01788         
01789     if (magic == NULL) {
01790         asp = init_agent_snmp_session(session, pdu);
01791         status = SNMP_ERR_NOERROR;
01792     } else {
01793         asp = (netsnmp_agent_session *) magic;
01794         status = asp->status;
01795     }
01796 
01797     if ((access_ret = check_access(asp->pdu)) != 0) {
01798         if (access_ret == VACM_NOSUCHCONTEXT) {
01799             /*
01800              * rfc3413 section 3.2, step 5 says that we increment the
01801              * counter but don't return a response of any kind 
01802              */
01803 
01804             /*
01805              * we currently don't support unavailable contexts, as
01806              * there is no reason to that I currently know of 
01807              */
01808             snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
01809 
01810             /*
01811              * drop the request 
01812              */
01813             netsnmp_remove_and_free_agent_snmp_session(asp);
01814             return 0;
01815         } else {
01816             /*
01817              * access control setup is incorrect 
01818              */
01819             send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
01820 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
01821 #if defined(NETSNMP_DISABLE_SNMPV1)
01822             if (asp->pdu->version != SNMP_VERSION_2c) {
01823 #else
01824 #if defined(NETSNMP_DISABLE_SNMPV2C)
01825             if (asp->pdu->version != SNMP_VERSION_1) {
01826 #else
01827             if (asp->pdu->version != SNMP_VERSION_1
01828                 && asp->pdu->version != SNMP_VERSION_2c) {
01829 #endif
01830 #endif
01831                 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
01832                 asp->pdu->command = SNMP_MSG_RESPONSE;
01833                 snmp_increment_statistic(STAT_SNMPOUTPKTS);
01834                 if (!snmp_send(asp->session, asp->pdu))
01835                     snmp_free_pdu(asp->pdu);
01836                 asp->pdu = NULL;
01837                 netsnmp_remove_and_free_agent_snmp_session(asp);
01838                 return 1;
01839             } else {
01840 #endif /* support for community based SNMP */
01841                 /*
01842                  * drop the request 
01843                  */
01844                 netsnmp_remove_and_free_agent_snmp_session(asp);
01845                 return 0;
01846 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
01847             }
01848 #endif /* support for community based SNMP */
01849         }
01850     }
01851 
01852     rc = netsnmp_handle_request(asp, status);
01853 
01854     /*
01855      * done 
01856      */
01857     DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %08p\n",
01858                 asp));
01859     return rc;
01860 }
01861 
01862 netsnmp_request_info *
01863 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
01864                              netsnmp_variable_list * varbind_ptr,
01865                              netsnmp_subtree *tp)
01866 {
01867     netsnmp_request_info *request = NULL;
01868     int             cacheid;
01869 
01870     DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
01871     DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
01872                  varbind_ptr->name_length));
01873     DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
01874 
01875     if (tp &&
01876         (asp->pdu->command == SNMP_MSG_GETNEXT ||
01877          asp->pdu->command == SNMP_MSG_GETBULK)) {
01878         int result;
01879         int prefix_len;
01880 
01881         prefix_len = netsnmp_oid_find_prefix(tp->start_a,
01882                                              tp->start_len,
01883                                              tp->end_a, tp->end_len);
01884         if (prefix_len < 1) {
01885             result = VACM_NOTINVIEW; /* ack...  bad bad thing happened */
01886         } else {
01887             result =
01888                 netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
01889         }
01890 
01891         while (result == VACM_NOTINVIEW) {
01892             /* the entire subtree is not in view. Skip it. */
01900             tp = tp->next;
01901             if (tp) {
01902                 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
01903                                                      tp->start_len,
01904                                                      tp->end_a,
01905                                                      tp->end_len);
01906                 if (prefix_len < 1) {
01907                     /* ack...  bad bad thing happened */
01908                     result = VACM_NOTINVIEW;
01909                 } else {
01910                     result =
01911                         netsnmp_acm_check_subtree(asp->pdu,
01912                                                   tp->start_a, prefix_len);
01913                 }
01914             }
01915             else
01916                 break;
01917         }
01918     }
01919     if (tp == NULL) {
01920         /*
01921          * no appropriate registration found 
01922          */
01923         /*
01924          * make up the response ourselves 
01925          */
01926         switch (asp->pdu->command) {
01927         case SNMP_MSG_GETNEXT:
01928         case SNMP_MSG_GETBULK:
01929             varbind_ptr->type = SNMP_ENDOFMIBVIEW;
01930             break;
01931 
01932         case SNMP_MSG_SET:
01933         case SNMP_MSG_GET:
01934             varbind_ptr->type = SNMP_NOSUCHOBJECT;
01935             break;
01936 
01937         default:
01938             return NULL;        /* shouldn't get here */
01939         }
01940     } else {
01941         DEBUGMSGTL(("snmp_agent", "tp->start "));
01942         DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
01943         DEBUGMSG(("snmp_agent", ", tp->end "));
01944         DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
01945         DEBUGMSG(("snmp_agent", ", \n"));
01946 
01947         /*
01948          * malloc the request structure 
01949          */
01950         request = &(asp->requests[vbcount - 1]);
01951         request->index = vbcount;
01952         request->delegated = 0;
01953         request->processed = 0;
01954         request->status = 0;
01955         request->subtree = tp;
01956         request->agent_req_info = asp->reqinfo;
01957         if (request->parent_data) {
01958             netsnmp_free_request_data_sets(request);
01959         }
01960         DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
01961                     asp, asp->reqinfo));
01962 
01963         /*
01964          * for non-SET modes, set the type to NULL 
01965          */
01966         if (!MODE_IS_SET(asp->pdu->command)) {
01967         DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
01968                     asp, asp->reqinfo));
01969             if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
01970                 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
01971                             request->index));
01972                 request->inclusive = 1;
01973             }
01974             varbind_ptr->type = ASN_NULL;
01975         }
01976 
01977         /*
01978          * place them in a cache 
01979          */
01980         if (tp->global_cacheid) {
01981             /*
01982              * we need to merge all marked subtrees together 
01983              */
01984             if (asp->cache_store && -1 !=
01985                 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
01986                                                     tp->global_cacheid))) {
01987             } else {
01988                 cacheid = ++(asp->treecache_num);
01989                 netsnmp_get_or_add_local_cachid(&asp->cache_store,
01990                                                 tp->global_cacheid,
01991                                                 cacheid);
01992                 goto mallocslot;        /* XXX: ick */
01993             }
01994         } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
01995                    asp->treecache[tp->cacheid].subtree == tp) {
01996             /*
01997              * we have already added a request to this tree
01998              * pointer before 
01999              */
02000             cacheid = tp->cacheid;
02001         } else {
02002             cacheid = ++(asp->treecache_num);
02003           mallocslot:
02004             /*
02005              * new slot needed 
02006              */
02007             if (asp->treecache_num >= asp->treecache_len) {
02008                 /*
02009                  * exapand cache array 
02010                  */
02011                 /*
02012                  * WWW: non-linear expansion needed (with cap) 
02013                  */
02014 #define CACHE_GROW_SIZE 16
02015                 asp->treecache_len =
02016                     (asp->treecache_len + CACHE_GROW_SIZE);
02017                 asp->treecache =
02018                     realloc(asp->treecache,
02019                             sizeof(netsnmp_tree_cache) *
02020                             asp->treecache_len);
02021                 if (asp->treecache == NULL)
02022                     return NULL;
02023                 memset(&(asp->treecache[cacheid]), 0x00,
02024                        sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
02025             }
02026             asp->treecache[cacheid].subtree = tp;
02027             asp->treecache[cacheid].requests_begin = request;
02028             tp->cacheid = cacheid;
02029         }
02030 
02031         /*
02032          * if this is a search type, get the ending range oid as well 
02033          */
02034         if (asp->pdu->command == SNMP_MSG_GETNEXT ||
02035             asp->pdu->command == SNMP_MSG_GETBULK) {
02036             request->range_end = tp->end_a;
02037             request->range_end_len = tp->end_len;
02038         } else {
02039             request->range_end = NULL;
02040             request->range_end_len = 0;
02041         }
02042 
02043         /*
02044          * link into chain 
02045          */
02046         if (asp->treecache[cacheid].requests_end)
02047             asp->treecache[cacheid].requests_end->next = request;
02048         request->next = NULL;
02049         request->prev = asp->treecache[cacheid].requests_end;
02050         asp->treecache[cacheid].requests_end = request;
02051 
02052         /*
02053          * add the given request to the list of requests they need
02054          * to handle results for 
02055          */
02056         request->requestvb = request->requestvb_start = varbind_ptr;
02057     }
02058     return request;
02059 }
02060 
02061 /*
02062  * check the ACM(s) for the results on each of the varbinds.
02063  * If ACM disallows it, replace the value with type
02064  * 
02065  * Returns number of varbinds with ACM errors
02066  */
02067 int
02068 check_acm(netsnmp_agent_session *asp, u_char type)
02069 {
02070     int             view;
02071     int             i, j, k;
02072     netsnmp_request_info *request;
02073     int             ret = 0;
02074     netsnmp_variable_list *vb, *vb2, *vbc;
02075     int             earliest = 0;
02076 
02077     for (i = 0; i <= asp->treecache_num; i++) {
02078         for (request = asp->treecache[i].requests_begin;
02079              request; request = request->next) {
02080             /*
02081              * for each request, run it through in_a_view() 
02082              */
02083             earliest = 0;
02084             for(j = request->repeat, vb = request->requestvb_start;
02085                 vb && j > -1;
02086                 j--, vb = vb->next_variable) {
02087                 if (vb->type != ASN_NULL &&
02088                     vb->type != ASN_PRIV_RETRY) { /* not yet processed */
02089                     view =
02090                         in_a_view(vb->name, &vb->name_length,
02091                                   asp->pdu, vb->type);
02092 
02093                     /*
02094                      * if a ACM error occurs, mark it as type passed in 
02095                      */
02096                     if (view != VACM_SUCCESS) {
02097                         ret++;
02098                         if (request->repeat < request->orig_repeat) {
02099                             /* basically this means a GETBULK */
02100                             request->repeat++;
02101                             if (!earliest) {
02102                                 request->requestvb = vb;
02103                                 earliest = 1;
02104                             }
02105 
02106                             /* ugh.  if a whole now exists, we need to
02107                                move the contents up the chain and fill
02108                                in at the end else we won't end up
02109                                lexographically sorted properly */
02110                             if (j > -1 && vb->next_variable &&
02111                                 vb->next_variable->type != ASN_NULL &&
02112                                 vb->next_variable->type != ASN_PRIV_RETRY) {
02113                                 for(k = j, vbc = vb, vb2 = vb->next_variable;
02114                                     k > -2 && vbc && vb2;
02115                                     k--, vbc = vb2, vb2 = vb2->next_variable) {
02116                                     /* clone next into the current */
02117                                     snmp_clone_var(vb2, vbc);
02118                                     vbc->next_variable = vb2;
02119                                 }
02120                             }
02121                         }
02122                         snmp_set_var_typed_value(vb, type, NULL, 0);
02123                     }
02124                 }
02125             }
02126         }
02127     }
02128     return ret;
02129 }
02130 
02131 
02132 int
02133 netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
02134 {
02135     netsnmp_subtree *tp;
02136     netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
02137     int             view;
02138     int             vbcount = 0;
02139     int             bulkcount = 0, bulkrep = 0;
02140     int             i = 0, n = 0, r = 0;
02141     netsnmp_request_info *request;
02142 
02143     if (asp->treecache == NULL && asp->treecache_len == 0) {
02144         asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
02145         asp->treecache =
02146             calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
02147         if (asp->treecache == NULL)
02148             return SNMP_ERR_GENERR;
02149     }
02150     asp->treecache_num = -1;
02151 
02152     if (asp->pdu->command == SNMP_MSG_GETBULK) {
02153         /*
02154          * getbulk prep 
02155          */
02156         int             count = count_varbinds(asp->pdu->variables);
02157         if (asp->pdu->errstat < 0) {
02158             asp->pdu->errstat = 0;
02159         }
02160         if (asp->pdu->errindex < 0) {
02161             asp->pdu->errindex = 0;
02162         }
02163 
02164         if (asp->pdu->errstat < count) {
02165             n = asp->pdu->errstat;
02166         } else {
02167             n = count;
02168         }
02169         if ((r = count - n) <= 0) {
02170             r = 0;
02171             asp->bulkcache = NULL;
02172         } else {
02173             int numresponses;
02174             int           maxbulk =
02175                 netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
02176                                    NETSNMP_DS_AGENT_MAX_GETBULKREPEATS);
02177             int maxresponses =
02178                 netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
02179                                    NETSNMP_DS_AGENT_MAX_GETBULKRESPONSES);
02180 
02181             if (maxresponses == 0)
02182                 maxresponses = 100;   /* more than reasonable default */
02183 
02184             if (maxbulk == 0)
02185                 maxbulk = -1;
02186 
02187             /* limit getbulk number of repeats to a configured size */
02188             if (asp->pdu->errindex > maxbulk && maxbulk != -1) {
02189                 asp->pdu->errindex = maxbulk;
02190             }
02191 
02192             numresponses = asp->pdu->errindex * r;
02193 
02194             /* limit getbulk number of getbulk responses to a configured size */
02195             if (maxresponses != -1 && numresponses > maxresponses) {
02196                 /* attempt to truncate this */
02197                 asp->pdu->errindex = maxresponses/r;
02198                 numresponses = asp->pdu->errindex * r;
02199                 DEBUGMSGTL(("snmp_agent", "truncating number of getbulk repeats to %d\n", asp->pdu->errindex));
02200             }
02201 
02202             asp->bulkcache =
02203                 (netsnmp_variable_list **) malloc(numresponses *
02204                                                   sizeof(struct
02205                                                          varbind_list *));
02206             if (!asp->bulkcache) {
02207                 DEBUGMSGTL(("snmp_agent", "Bulkcache malloc failed\n"));
02208                 return SNMP_ERR_GENERR;
02209             }
02210         }
02211         DEBUGMSGTL(("snmp_agent", "GETBULK N = %d, M = %d, R = %d\n",
02212                     n, asp->pdu->errindex, r));
02213     }
02214 
02215     /*
02216      * collect varbinds into their registered trees 
02217      */
02218     prevNext = &(asp->pdu->variables);
02219     for (varbind_ptr = asp->pdu->variables; varbind_ptr;
02220          varbind_ptr = vbsave) {
02221 
02222         /*
02223          * getbulk mess with this pointer, so save it 
02224          */
02225         vbsave = varbind_ptr->next_variable;
02226 
02227         if (asp->pdu->command == SNMP_MSG_GETBULK) {
02228             if (n > 0) {
02229                 n--;
02230             } else {
02231                 /*
02232                  * repeate request varbinds on GETBULK.  These will
02233                  * have to be properly rearranged later though as
02234                  * responses are supposed to actually be interlaced
02235                  * with each other.  This is done with the asp->bulkcache. 
02236                  */
02237                 bulkrep = asp->pdu->errindex - 1;
02238                 if (asp->pdu->errindex > 0) {
02239                     vbptr = varbind_ptr;
02240                     asp->bulkcache[bulkcount++] = vbptr;
02241 
02242                     for (i = 1; i < asp->pdu->errindex; i++) {
02243                         vbptr->next_variable =
02244                             SNMP_MALLOC_STRUCT(variable_list);
02245                         /*
02246                          * don't clone the oid as it's got to be
02247                          * overwritten anyway 
02248                          */
02249                         if (!vbptr->next_variable) {
02250                             /*
02251                              * XXXWWW: ack!!! 
02252                              */
02253                             DEBUGMSGTL(("snmp_agent", "NextVar malloc failed\n"));
02254                         } else {
02255                             vbptr = vbptr->next_variable;
02256                             vbptr->name_length = 0;
02257                             vbptr->type = ASN_NULL;
02258                             asp->bulkcache[bulkcount++] = vbptr;
02259                         }
02260                     }
02261                     vbptr->next_variable = vbsave;
02262                 } else {
02263                     /*
02264                      * 0 repeats requested for this varbind, so take it off
02265                      * the list.  
02266                      */
02267                     vbptr = varbind_ptr;
02268                     *prevNext = vbptr->next_variable;
02269                     vbptr->next_variable = NULL;
02270                     snmp_free_varbind(vbptr);
02271                     asp->vbcount--;
02272                     continue;
02273                 }
02274             }
02275         }
02276 
02277         /*
02278          * count the varbinds 
02279          */
02280         ++vbcount;
02281 
02282         /*
02283          * find the owning tree 
02284          */
02285         tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
02286                                   NULL, asp->pdu->contextName);
02287 
02288         /*
02289          * check access control 
02290          */
02291         switch (asp->pdu->command) {
02292         case SNMP_MSG_GET:
02293             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
02294                              asp->pdu, varbind_ptr->type);
02295             if (view != VACM_SUCCESS)
02296                 snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
02297                                          NULL, 0);
02298             break;
02299 
02300         case SNMP_MSG_SET:
02301             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
02302                              asp->pdu, varbind_ptr->type);
02303             if (view != VACM_SUCCESS)
02304                 return SNMP_ERR_NOACCESS;
02305             break;
02306 
02307         case SNMP_MSG_GETNEXT:
02308         case SNMP_MSG_GETBULK:
02309         default:
02310             view = VACM_SUCCESS;
02311             /*
02312              * XXXWWW: check VACM here to see if "tp" is even worthwhile 
02313              */
02314         }
02315         if (view == VACM_SUCCESS) {
02316             request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
02317                                                    tp);
02318             if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
02319                 request->repeat = request->orig_repeat = bulkrep;
02320             }
02321         }
02322 
02323         prevNext = &(varbind_ptr->next_variable);
02324     }
02325 
02326     return SNMPERR_SUCCESS;
02327 }
02328 
02329 /*
02330  * this function is only applicable in getnext like contexts 
02331  */
02332 int
02333 netsnmp_reassign_requests(netsnmp_agent_session *asp)
02334 {
02335     /*
02336      * assume all the requests have been filled or rejected by the
02337      * subtrees, so reassign the rejected ones to the next subtree in
02338      * the chain 
02339      */
02340 
02341     int             i;
02342 
02343     /*
02344      * get old info 
02345      */
02346     netsnmp_tree_cache *old_treecache = asp->treecache;
02347 
02348     /*
02349      * malloc new space 
02350      */
02351     asp->treecache =
02352         (netsnmp_tree_cache *) calloc(asp->treecache_len,
02353                                       sizeof(netsnmp_tree_cache));
02354     asp->treecache_num = -1;
02355     if (asp->cache_store) {
02356         netsnmp_free_cachemap(asp->cache_store);
02357         asp->cache_store = NULL;
02358     }
02359 
02360     for (i = 0; i < asp->vbcount; i++) {
02361         if (asp->requests[i].requestvb == NULL) {
02362             /*
02363              * Occurs when there's a mixture of still active
02364              *   and "endOfMibView" repetitions
02365              */
02366             continue;
02367         }
02368         if (asp->requests[i].requestvb->type == ASN_NULL) {
02369             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
02370                                               asp->requests[i].requestvb,
02371                                               asp->requests[i].subtree->next)) {
02372                 if (old_treecache != NULL) {
02373                     SNMP_FREE(old_treecache);
02374                     old_treecache = NULL;
02375                 }
02376             }
02377         } else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
02378             /*
02379              * re-add the same subtree 
02380              */
02381             asp->requests[i].requestvb->type = ASN_NULL;
02382             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
02383                                               asp->requests[i].requestvb,
02384                                               asp->requests[i].subtree)) {
02385                 if (old_treecache != NULL) {
02386                     SNMP_FREE(old_treecache);
02387                     old_treecache = NULL;
02388                 }
02389             }
02390         }
02391     }
02392 
02393     if (old_treecache != NULL) {
02394         SNMP_FREE(old_treecache);
02395     }
02396     return SNMP_ERR_NOERROR;
02397 }
02398 
02399 void
02400 netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
02401 {
02402     while (reqlist) {
02403         netsnmp_free_request_data_sets(reqlist);
02404         reqlist = reqlist->next;
02405     }
02406 }
02407 
02408 void
02409 netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
02410 {
02411     while (asp->treecache_num >= 0) {
02412         /*
02413          * don't delete subtrees 
02414          */
02415         netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
02416                                      requests_begin);
02417         asp->treecache_num--;
02418     }
02419 }
02420 
02421 /*
02422  * check all requests for errors
02423  *
02424  * @Note:
02425  * This function is a little different from the others in that
02426  * it does not use any linked lists, instead using the original
02427  * asp requests array. This is of particular importance for
02428  * cases where the linked lists are unreliable. One known instance
02429  * of this scenario occurs when the row_merge helper is used, which
02430  * may temporarily disrupts linked lists during its (and its childrens)
02431  * handling of requests.
02432  */
02433 int
02434 netsnmp_check_all_requests_error(netsnmp_agent_session *asp,
02435                                  int look_for_specific)
02436 {
02437     int i;
02438 
02439     /*
02440      * find any errors marked in the requests 
02441      */
02442     for( i = 0; i < asp->vbcount; ++i ) {
02443         if ((SNMP_ERR_NOERROR != asp->requests[i].status) &&
02444             (!look_for_specific ||
02445              asp->requests[i].status == look_for_specific))
02446             return asp->requests[i].status;
02447     }
02448 
02449     return SNMP_ERR_NOERROR;
02450 }
02451 
02452 int
02453 netsnmp_check_requests_error(netsnmp_request_info *requests)
02454 {
02455     /*
02456      * find any errors marked in the requests 
02457      */
02458     for (;requests;requests = requests->next) {
02459         if (requests->status != SNMP_ERR_NOERROR)
02460             return requests->status;
02461     }
02462     return SNMP_ERR_NOERROR;
02463 }
02464 
02465 int
02466 netsnmp_check_requests_status(netsnmp_agent_session *asp,
02467                               netsnmp_request_info *requests,
02468                               int look_for_specific)
02469 {
02470     /*
02471      * find any errors marked in the requests 
02472      */
02473     while (requests) {
02474         if(requests->agent_req_info != asp->reqinfo) {
02475             DEBUGMSGTL(("verbose:asp",
02476                         "**reqinfo %p doesn't match cached reqinfo %p\n",
02477                         asp->reqinfo, requests->agent_req_info));
02478         }
02479         if (requests->status != SNMP_ERR_NOERROR &&
02480             (!look_for_specific || requests->status == look_for_specific)
02481             && (look_for_specific || asp->index == 0
02482                 || requests->index < asp->index)) {
02483             asp->index = requests->index;
02484             asp->status = requests->status;
02485         }
02486         requests = requests->next;
02487     }
02488     return asp->status;
02489 }
02490 
02491 int
02492 netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
02493                                   int look_for_specific)
02494 {
02495     int             i;
02496     for (i = 0; i <= asp->treecache_num; i++) {
02497         netsnmp_check_requests_status(asp,
02498                                       asp->treecache[i].requests_begin,
02499                                       look_for_specific);
02500     }
02501     return asp->status;
02502 }
02503 
02504 int
02505 handle_var_requests(netsnmp_agent_session *asp)
02506 {
02507     int             i, retstatus = SNMP_ERR_NOERROR,
02508         status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
02509     netsnmp_handler_registration *reginfo;
02510 
02511     asp->reqinfo->asp = asp;
02512     asp->reqinfo->mode = asp->mode;
02513 
02514     /*
02515      * now, have the subtrees in the cache go search for their results 
02516      */
02517     for (i = 0; i <= asp->treecache_num; i++) {
02518         /*
02519          * don't call handlers w/null reginfo.
02520          * - when is this? sub agent disconnected while request processing?
02521          * - should this case encompass more of this subroutine?
02522          *   - does check_request_status make send if handlers weren't called?
02523          */
02524         if(NULL != asp->treecache[i].subtree->reginfo) {
02525             reginfo = asp->treecache[i].subtree->reginfo;
02526             status = netsnmp_call_handlers(reginfo, asp->reqinfo,
02527                                            asp->treecache[i].requests_begin);
02528         }
02529         else
02530             status = SNMP_ERR_GENERR;
02531 
02532         /*
02533          * find any errors marked in the requests.  For later parts of
02534          * SET processing, only check for new errors specific to that
02535          * set processing directive (which must superceed the previous
02536          * errors).
02537          */
02538         switch (asp->mode) {
02539         case MODE_SET_COMMIT:
02540             retstatus = netsnmp_check_requests_status(asp,
02541                                                       asp->treecache[i].
02542                                                       requests_begin,
02543                                                       SNMP_ERR_COMMITFAILED);
02544             break;
02545 
02546         case MODE_SET_UNDO:
02547             retstatus = netsnmp_check_requests_status(asp,
02548                                                       asp->treecache[i].
02549                                                       requests_begin,
02550                                                       SNMP_ERR_UNDOFAILED);
02551             break;
02552 
02553         default:
02554             retstatus = netsnmp_check_requests_status(asp,
02555                                                       asp->treecache[i].
02556                                                       requests_begin, 0);
02557             break;
02558         }
02559 
02560         /*
02561          * always take lowest varbind if possible 
02562          */
02563         if (retstatus != SNMP_ERR_NOERROR) {
02564             status = retstatus;
02565         }
02566 
02567         /*
02568          * other things we know less about (no index) 
02569          */
02570         /*
02571          * WWW: drop support for this? 
02572          */
02573         if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
02574             /*
02575              * we can't break here, since some processing needs to be
02576              * done for all requests anyway (IE, SET handling for UNDO
02577              * needs to be called regardless of previous status
02578              * results.
02579              * WWW:  This should be predictable though and
02580              * breaking should be possible in some cases (eg GET,
02581              * GETNEXT, ...) 
02582              */
02583             final_status = status;
02584         }
02585     }
02586 
02587     return final_status;
02588 }
02589 
02590 /*
02591  * loop through our sessions known delegated sessions and check to see
02592  * if they've completed yet. If there are no more delegated sessions,
02593  * check for and process any queued requests
02594  */
02595 void
02596 netsnmp_check_outstanding_agent_requests(void)
02597 {
02598     netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
02599 
02600     /*
02601      * deal with delegated requests
02602      */
02603     for (asp = agent_delegated_list; asp; asp = next_asp) {
02604         next_asp = asp->next;   /* save in case we clean up asp */
02605         if (!netsnmp_check_for_delegated(asp)) {
02606 
02607             /*
02608              * we're done with this one, remove from queue 
02609              */
02610             if (prev_asp != NULL)
02611                 prev_asp->next = asp->next;
02612             else
02613                 agent_delegated_list = asp->next;
02614             asp->next = NULL;
02615 
02616             /*
02617              * check request status
02618              */
02619             netsnmp_check_all_requests_status(asp, 0);
02620             
02621             /*
02622              * continue processing or finish up 
02623              */
02624             check_delayed_request(asp);
02625 
02626             /*
02627              * if head was removed, don't drop it if it
02628              * was it re-queued
02629              */
02630             if ((prev_asp == NULL) && (agent_delegated_list == asp)) {
02631                 prev_asp = asp;
02632             }
02633         } else {
02634 
02635             /*
02636              * asp is still on the queue
02637              */
02638             prev_asp = asp;
02639         }
02640     }
02641 
02642     /*
02643      * if we are processing a set and there are more delegated
02644      * requests, keep waiting before getting to queued requests.
02645      */
02646     if (netsnmp_processing_set && (NULL != agent_delegated_list))
02647         return;
02648 
02649     while (netsnmp_agent_queued_list) {
02650 
02651         /*
02652          * if we are processing a set, the first item better be
02653          * the set being (or waiting to be) processed.
02654          */
02655         netsnmp_assert((!netsnmp_processing_set) ||
02656                        (netsnmp_processing_set == netsnmp_agent_queued_list));
02657 
02658         /*
02659          * if the top request is a set, don't pop it
02660          * off if there are delegated requests
02661          */
02662         if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
02663             (agent_delegated_list)) {
02664 
02665             netsnmp_assert(netsnmp_processing_set == NULL);
02666 
02667             netsnmp_processing_set = netsnmp_agent_queued_list;
02668             DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
02669                         "delegated requests finish, asp = %08p\n", asp));
02670             break;
02671         }
02672 
02673         /*
02674          * pop the first request and process it
02675          */
02676         asp = netsnmp_agent_queued_list;
02677         netsnmp_agent_queued_list = asp->next;
02678         DEBUGMSGTL(("snmp_agent",
02679                     "processing queued request, asp = %08p\n", asp));
02680 
02681         netsnmp_handle_request(asp, asp->status);
02682 
02683         /*
02684          * if we hit a set, stop
02685          */
02686         if (NULL != netsnmp_processing_set)
02687             break;
02688     }
02689 }
02690 
02696 int
02697 netsnmp_check_transaction_id(int transaction_id)
02698 {
02699     netsnmp_agent_session *asp, *prev_asp = NULL;
02700 
02701     for (asp = agent_delegated_list; asp; prev_asp = asp, asp = asp->next) {
02702         if (asp->pdu->transid == transaction_id)
02703             return SNMPERR_SUCCESS;
02704     }
02705     return SNMPERR_GENERR;
02706 }
02707 
02708 
02709 /*
02710  * check_delayed_request(asp)
02711  *
02712  * Called to rexamine a set of requests and continue processing them
02713  * once all the previous (delayed) requests have been handled one way
02714  * or another.
02715  */
02716 
02717 int
02718 check_delayed_request(netsnmp_agent_session *asp)
02719 {
02720     int             status = SNMP_ERR_NOERROR;
02721 
02722     DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %08p\n",
02723                 asp));
02724 
02725     switch (asp->mode) {
02726     case SNMP_MSG_GETBULK:
02727     case SNMP_MSG_GETNEXT:
02728         netsnmp_check_all_requests_status(asp, 0);
02729         handle_getnext_loop(asp);
02730         if (netsnmp_check_for_delegated(asp) &&
02731             netsnmp_check_transaction_id(asp->pdu->transid) !=
02732             SNMPERR_SUCCESS) {
02733             /*
02734              * add to delegated request chain 
02735              */
02736             if (!netsnmp_check_delegated_chain_for(asp)) {
02737                 asp->next = agent_delegated_list;
02738                 agent_delegated_list = asp;
02739             }
02740         }
02741         break;
02742 
02743     case MODE_SET_COMMIT:
02744         netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
02745         goto settop;
02746 
02747     case MODE_SET_UNDO:
02748         netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
02749         goto settop;
02750 
02751     case MODE_SET_BEGIN:
02752     case MODE_SET_RESERVE1:
02753     case MODE_SET_RESERVE2:
02754     case MODE_SET_ACTION:
02755     case MODE_SET_FREE:
02756       settop:
02757         /* If we should do only one pass, this mean we */
02758         /* should not reenter this function */
02759         if ((asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
02760             /* We should have finished the processing after the first */
02761             /* handle_set_loop, so just wrap up */
02762             break;
02763         }
02764         handle_set_loop(asp);
02765         if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
02766 
02767             if (netsnmp_check_for_delegated_and_add(asp)) {
02768                 /*
02769                  * add to delegated request chain 
02770                  */
02771                 if (!asp->status)
02772                     asp->status = status;
02773             }
02774 
02775             return SNMP_ERR_NOERROR;
02776         }
02777         break;
02778 
02779     default:
02780         break;
02781     }
02782 
02783     /*
02784      * if we don't have anything outstanding (delegated), wrap up 
02785      */
02786     if (!netsnmp_check_for_delegated(asp))
02787         return netsnmp_wrap_up_request(asp, status);
02788 
02789     return 1;
02790 }
02791 
02793 int
02794 check_getnext_results(netsnmp_agent_session *asp)
02795 {
02796     /*
02797      * get old info 
02798      */
02799     netsnmp_tree_cache *old_treecache = asp->treecache;
02800     int             old_treecache_num = asp->treecache_num;
02801     int             count = 0;
02802     int             i, special = 0;
02803     netsnmp_request_info *request;
02804 
02805     if (asp->mode == SNMP_MSG_GET) {
02806         /*
02807          * Special case for doing INCLUSIVE getNext operations in
02808          * AgentX subagents.  
02809          */
02810         DEBUGMSGTL(("snmp_agent",
02811                     "asp->mode == SNMP_MSG_GET in ch_getnext\n"));
02812         asp->mode = asp->oldmode;
02813         special = 1;
02814     }
02815 
02816     for (i = 0; i <= old_treecache_num; i++) {
02817         for (request = old_treecache[i].requests_begin; request;
02818              request = request->next) {
02819 
02820             /*
02821              * If we have just done the special case AgentX GET, then any
02822              * requests which were not INCLUSIVE will now have a wrong
02823              * response, so junk them and retry from the same place (except
02824              * that this time the handler will be called in "inexact"
02825              * mode).  
02826              */
02827 
02828             if (special) {
02829                 if (!request->inclusive) {
02830                     DEBUGMSGTL(("snmp_agent",
02831                                 "request %d wasn't inclusive\n",
02832                                 request->index));
02833                     snmp_set_var_typed_value(request->requestvb,
02834                                              ASN_PRIV_RETRY, NULL, 0);
02835                 } else if (request->requestvb->type == ASN_NULL ||
02836                            request->requestvb->type == SNMP_NOSUCHINSTANCE ||
02837                            request->requestvb->type == SNMP_NOSUCHOBJECT) {
02838                     /*
02839                      * it was inclusive, but no results.  Still retry this
02840                      * search. 
02841                      */
02842                     snmp_set_var_typed_value(request->requestvb,
02843                                              ASN_PRIV_RETRY, NULL, 0);
02844                 }
02845             }
02846 
02847             /*
02848              * out of range? 
02849              */
02850             if (snmp_oid_compare(request->requestvb->name,
02851                                  request->requestvb->name_length,
02852                                  request->range_end,
02853                                  request->range_end_len) >= 0) {
02854                 /*
02855                  * ack, it's beyond the accepted end of range. 
02856                  */
02857                 /*
02858                  * fix it by setting the oid to the end of range oid instead 
02859                  */
02860                 DEBUGMSGTL(("check_getnext_results",
02861                             "request response %d out of range\n",
02862                             request->index));
02863                 /*
02864                  * I'm not sure why inclusive is set unconditionally here (see
02865                  * comments for revision 1.161), but it causes a problem for
02866                  * GETBULK over an overridden variable. The bulk-to-next
02867                  * handler re-uses the same request for multiple varbinds,
02868                  * and once inclusive was set, it was never cleared. So, a
02869                  * hack. Instead of setting it to 1, set it to 2, so bulk-to
02870                  * next can clear it later. As of the time of this hack, all
02871                  * checks of this var are boolean checks (not == 1), so this
02872                  * should be safe. Cross your fingers.
02873                  */
02874                 request->inclusive = 2;
02875                 /*
02876                  * XXX: should set this to the original OID? 
02877                  */
02878                 snmp_set_var_objid(request->requestvb,
02879                                    request->range_end,
02880                                    request->range_end_len);
02881                 snmp_set_var_typed_value(request->requestvb, ASN_NULL,
02882                                          NULL, 0);
02883             }
02884 
02885             /*
02886              * mark any existent requests with illegal results as NULL 
02887              */
02888             if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
02889                 /*
02890                  * illegal response from a subagent.  Change it back to NULL 
02891                  *  xxx-rks: err, how do we know this is a subagent?
02892                  */
02893                 request->requestvb->type = ASN_NULL;
02894                 request->inclusive = 1;
02895             }
02896 
02897             if (request->requestvb->type == ASN_NULL ||
02898                 request->requestvb->type == ASN_PRIV_RETRY ||
02899                 (asp->reqinfo->mode == MODE_GETBULK
02900                  && request->repeat > 0))
02901                 count++;
02902         }
02903     }
02904     return count;
02905 }
02906 
02910 int
02911 handle_getnext_loop(netsnmp_agent_session *asp)
02912 {
02913     int             status;
02914     netsnmp_variable_list *var_ptr;
02915 
02916     /*
02917      * loop 
02918      */
02919     while (netsnmp_running) {
02920 
02921         /*
02922          * bail for now if anything is delegated. 
02923          */
02924         if (netsnmp_check_for_delegated(asp)) {
02925             return SNMP_ERR_NOERROR;
02926         }
02927 
02928         /*
02929          * check vacm against results 
02930          */
02931         check_acm(asp, ASN_PRIV_RETRY);
02932 
02933         /*
02934          * need to keep going we're not done yet. 
02935          */
02936         if (!check_getnext_results(asp))
02937             /*
02938              * nothing left, quit now 
02939              */
02940             break;
02941 
02942         /*
02943          * never had a request (empty pdu), quit now 
02944          */
02945         /*
02946          * XXXWWW: huh?  this would be too late, no?  shouldn't we
02947          * catch this earlier? 
02948          */
02949         /*
02950          * if (count == 0)
02951          * break; 
02952          */
02953 
02954         DEBUGIF("results") {
02955             DEBUGMSGTL(("results",
02956                         "getnext results, before next pass:\n"));
02957             for (var_ptr = asp->pdu->variables; var_ptr;
02958                  var_ptr = var_ptr->next_variable) {
02959                 DEBUGMSGTL(("results", "\t"));
02960                 DEBUGMSGVAR(("results", var_ptr));
02961                 DEBUGMSG(("results", "\n"));
02962             }
02963         }
02964 
02965         netsnmp_reassign_requests(asp);
02966         status = handle_var_requests(asp);
02967         if (status != SNMP_ERR_NOERROR) {
02968             return status;      /* should never really happen */
02969         }
02970     }
02971     return SNMP_ERR_NOERROR;
02972 }
02973 
02974 int
02975 handle_set(netsnmp_agent_session *asp)
02976 {
02977     int             status;
02978     /*
02979      * SETS require 3-4 passes through the var_op_list.
02980      * The first two
02981      * passes verify that all types, lengths, and values are valid
02982      * and may reserve resources and the third does the set and a
02983      * fourth executes any actions.  Then the identical GET RESPONSE
02984      * packet is returned.
02985      * If either of the first two passes returns an error, another
02986      * pass is made so that any reserved resources can be freed.
02987      * If the third pass returns an error, another pass is
02988      * made so that
02989      * any changes can be reversed.
02990      * If the fourth pass (or any of the error handling passes)
02991      * return an error, we'd rather not know about it!
02992      */
02993     if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
02994         switch (asp->mode) {
02995         case MODE_SET_BEGIN:
02996             snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
02997             asp->rw = WRITE;    /* WWW: still needed? */
02998             asp->mode = MODE_SET_RESERVE1;
02999             asp->status = SNMP_ERR_NOERROR;
03000             break;
03001 
03002         case MODE_SET_RESERVE1:
03003 
03004             if (asp->status != SNMP_ERR_NOERROR)
03005                 asp->mode = MODE_SET_FREE;
03006             else
03007                 asp->mode = MODE_SET_RESERVE2;
03008             break;
03009 
03010         case MODE_SET_RESERVE2:
03011             if (asp->status != SNMP_ERR_NOERROR)
03012                 asp->mode = MODE_SET_FREE;
03013             else
03014                 asp->mode = MODE_SET_ACTION;
03015             break;
03016 
03017         case MODE_SET_ACTION:
03018             if (asp->status != SNMP_ERR_NOERROR)
03019                 asp->mode = MODE_SET_UNDO;
03020             else
03021                 asp->mode = MODE_SET_COMMIT;
03022             break;
03023 
03024         case MODE_SET_COMMIT:
03025             if (asp->status != SNMP_ERR_NOERROR) {
03026                 asp->mode = FINISHED_FAILURE;
03027             } else {
03028                 asp->mode = FINISHED_SUCCESS;
03029             }
03030             break;
03031 
03032         case MODE_SET_UNDO:
03033             asp->mode = FINISHED_FAILURE;
03034             break;
03035 
03036         case MODE_SET_FREE:
03037             asp->mode = FINISHED_FAILURE;
03038             break;
03039         }
03040     }
03041 
03042     if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
03043         DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
03044                     se_find_label_in_slist("agent_mode", asp->mode)));
03045         status = handle_var_requests(asp);
03046         DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
03047                     asp->mode, status));
03048         if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
03049             status == SNMP_ERR_COMMITFAILED || 
03050             status == SNMP_ERR_UNDOFAILED) {
03051             asp->status = status;
03052         }
03053     }
03054     return asp->status;
03055 }
03056 
03057 int
03058 handle_set_loop(netsnmp_agent_session *asp)
03059 {
03060     while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
03061         handle_set(asp);
03062         if (netsnmp_check_for_delegated(asp)) {
03063             return SNMP_ERR_NOERROR;
03064         }
03065         if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
03066             return asp->status;
03067         }
03068     }
03069     return asp->status;
03070 }
03071 
03072 int
03073 netsnmp_handle_request(netsnmp_agent_session *asp, int status)
03074 {
03075     /*
03076      * if this isn't a delegated request trying to finish,
03077      * processing of a set request should not start until all
03078      * delegated requests have completed, and no other new requests
03079      * should be processed until the set request completes.
03080      */
03081     if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
03082         (asp != netsnmp_processing_set)) {
03083         /*
03084          * if we are processing a set and this is not a delegated
03085          * request, queue the request
03086          */
03087         if (netsnmp_processing_set) {
03088             netsnmp_add_queued(asp);
03089             DEBUGMSGTL(("snmp_agent",
03090                         "request queued while processing set, "
03091                         "asp = %08p\n", asp));
03092             return 1;
03093         }
03094 
03095         /*
03096          * check for set request
03097          */
03098         if (asp->pdu->command == SNMP_MSG_SET) {
03099             netsnmp_processing_set = asp;
03100 
03101             /*
03102              * if there are delegated requests, we must wait for them
03103              * to finish.
03104              */
03105             if (agent_delegated_list) {
03106                 DEBUGMSGTL(("snmp_agent", "SET request queued while "
03107                             "delegated requests finish, asp = %08p\n",
03108                             asp));
03109                 netsnmp_add_queued(asp);
03110                 return 1;
03111             }
03112         }
03113     }
03114 
03115     /*
03116      * process the request 
03117      */
03118     status = handle_pdu(asp);
03119 
03120     /*
03121      * print the results in appropriate debugging mode 
03122      */
03123     DEBUGIF("results") {
03124         netsnmp_variable_list *var_ptr;
03125         DEBUGMSGTL(("results", "request results (status = %d):\n",
03126                     status));
03127         for (var_ptr = asp->pdu->variables; var_ptr;
03128              var_ptr = var_ptr->next_variable) {
03129             DEBUGMSGTL(("results", "\t"));
03130             DEBUGMSGVAR(("results", var_ptr));
03131             DEBUGMSG(("results", "\n"));
03132         }
03133     }
03134 
03135     /*
03136      * check for uncompleted requests 
03137      */
03138     if (netsnmp_check_for_delegated_and_add(asp)) {
03139         /*
03140          * add to delegated request chain 
03141          */
03142         asp->status = status;
03143     } else {
03144         /*
03145          * if we don't have anything outstanding (delegated), wrap up
03146          */
03147         return netsnmp_wrap_up_request(asp, status);
03148     }
03149 
03150     return 1;
03151 }
03152 
03204 int
03205 handle_pdu(netsnmp_agent_session *asp)
03206 {
03207     int             status, inclusives = 0;
03208     netsnmp_variable_list *v = NULL;
03209 
03210     /*
03211      * for illegal requests, mark all nodes as ASN_NULL 
03212      */
03213     switch (asp->pdu->command) {
03214 
03215     case SNMP_MSG_INTERNAL_SET_RESERVE2:
03216     case SNMP_MSG_INTERNAL_SET_ACTION:
03217     case SNMP_MSG_INTERNAL_SET_COMMIT:
03218     case SNMP_MSG_INTERNAL_SET_FREE:
03219     case SNMP_MSG_INTERNAL_SET_UNDO:
03220         status = get_set_cache(asp);
03221         if (status != SNMP_ERR_NOERROR)
03222             return status;
03223         break;
03224 
03225     case SNMP_MSG_GET:
03226     case SNMP_MSG_GETNEXT:
03227     case SNMP_MSG_GETBULK:
03228         for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
03229             if (v->type == ASN_PRIV_INCL_RANGE) {
03230                 /*
03231                  * Leave the type for now (it gets set to
03232                  * ASN_NULL in netsnmp_add_varbind_to_cache,
03233                  * called by create_subnetsnmp_tree_cache below).
03234                  * If we set it to ASN_NULL now, we wouldn't be
03235                  * able to distinguish INCLUSIVE search
03236                  * ranges.  
03237                  */
03238                 inclusives++;
03239             } else {
03240                 snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
03241             }
03242         }
03243         /*
03244          * fall through 
03245          */
03246 
03247     case SNMP_MSG_INTERNAL_SET_BEGIN:
03248     case SNMP_MSG_INTERNAL_SET_RESERVE1:
03249     default:
03250         asp->vbcount = count_varbinds(asp->pdu->variables);
03251         if (asp->vbcount) /* efence doesn't like 0 size allocs */
03252             asp->requests = (netsnmp_request_info *)
03253                 calloc(asp->vbcount, sizeof(netsnmp_request_info));
03254         /*
03255          * collect varbinds 
03256          */
03257         status = netsnmp_create_subtree_cache(asp);
03258         if (status != SNMP_ERR_NOERROR)
03259             return status;
03260     }
03261 
03262     asp->mode = asp->pdu->command;
03263     switch (asp->mode) {
03264     case SNMP_MSG_GET:
03265         /*
03266          * increment the message type counter 
03267          */
03268         snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
03269 
03270         /*
03271          * check vacm ahead of time 
03272          */
03273         check_acm(asp, SNMP_NOSUCHOBJECT);
03274 
03275         /*
03276          * get the results 
03277          */
03278         status = handle_var_requests(asp);
03279 
03280         /*
03281          * Deal with unhandled results -> noSuchInstance (rather
03282          * than noSuchObject -- in that case, the type will
03283          * already have been set to noSuchObject when we realised
03284          * we couldn't find an appropriate tree).  
03285          */
03286         if (status == SNMP_ERR_NOERROR)
03287             snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
03288                                    SNMP_NOSUCHINSTANCE);
03289         break;
03290 
03291     case SNMP_MSG_GETNEXT:
03292         snmp_increment_statistic(STAT_SNMPINGETNEXTS);
03293         /*
03294          * fall through 
03295          */
03296 
03297     case SNMP_MSG_GETBULK:     /* note: there is no getbulk stat */
03298         /*
03299          * loop through our mib tree till we find an
03300          * appropriate response to return to the caller. 
03301          */
03302 
03303         if (inclusives) {
03304             /*
03305              * This is a special case for AgentX INCLUSIVE getNext
03306              * requests where a result lexi-equal to the request is okay
03307              * but if such a result does not exist, we still want the
03308              * lexi-next one.  So basically we do a GET first, and if any
03309              * of the INCLUSIVE requests are satisfied, we use that
03310              * value.  Then, unsatisfied INCLUSIVE requests, and
03311              * non-INCLUSIVE requests get done as normal.  
03312              */
03313 
03314             DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
03315             asp->oldmode = asp->mode;
03316             asp->mode = SNMP_MSG_GET;
03317         }
03318 
03319         /*
03320          * first pass 
03321          */
03322         status = handle_var_requests(asp);
03323         if (status != SNMP_ERR_NOERROR) {
03324             if (!inclusives)
03325                 return status;  /* should never really happen */
03326             else
03327                 asp->status = SNMP_ERR_NOERROR;
03328         }
03329 
03330         /*
03331          * loop through our mib tree till we find an
03332          * appropriate response to return to the caller. 
03333          */
03334 
03335         status = handle_getnext_loop(asp);
03336         break;
03337 
03338     case SNMP_MSG_SET:
03339 #ifdef NETSNMP_DISABLE_SET_SUPPORT
03340         return SNMP_ERR_NOTWRITABLE;
03341 #else
03342         /*
03343          * check access permissions first 
03344          */
03345         if (check_acm(asp, SNMP_NOSUCHOBJECT))
03346             return SNMP_ERR_NOTWRITABLE;
03347 
03348         asp->mode = MODE_SET_BEGIN;
03349         status = handle_set_loop(asp);
03350 #endif
03351         break;
03352 
03353     case SNMP_MSG_INTERNAL_SET_BEGIN:
03354     case SNMP_MSG_INTERNAL_SET_RESERVE1:
03355     case SNMP_MSG_INTERNAL_SET_RESERVE2:
03356     case SNMP_MSG_INTERNAL_SET_ACTION:
03357     case SNMP_MSG_INTERNAL_SET_COMMIT:
03358     case SNMP_MSG_INTERNAL_SET_FREE:
03359     case SNMP_MSG_INTERNAL_SET_UNDO:
03360         asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
03361         status = handle_set_loop(asp);
03362         /*
03363          * asp related cache is saved in cleanup 
03364          */
03365         break;
03366 
03367     case SNMP_MSG_RESPONSE:
03368         snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
03369         return SNMP_ERR_NOERROR;
03370 
03371     case SNMP_MSG_TRAP:
03372     case SNMP_MSG_TRAP2:
03373         snmp_increment_statistic(STAT_SNMPINTRAPS);
03374         return SNMP_ERR_NOERROR;
03375 
03376     default:
03377         /*
03378          * WWW: are reports counted somewhere ? 
03379          */
03380         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
03381         return SNMPERR_GENERR;  /* shouldn't get here */
03382         /*
03383          * WWW 
03384          */
03385     }
03386     return status;
03387 }
03388 
03392 NETSNMP_STATIC_INLINE int
03393 _request_set_error(netsnmp_request_info *request, int mode, int error_value)
03394 {
03395     if (!request)
03396         return SNMPERR_NO_VARS;
03397 
03398     request->processed = 1;
03399     request->delegated = REQUEST_IS_NOT_DELEGATED;
03400 
03401     switch (error_value) {
03402     case SNMP_NOSUCHOBJECT:
03403     case SNMP_NOSUCHINSTANCE:
03404     case SNMP_ENDOFMIBVIEW:
03405         /*
03406          * these are exceptions that should be put in the varbind
03407          * in the case of a GET but should be translated for a SET
03408          * into a real error status code and put in the request 
03409          */
03410         switch (mode) {
03411         case MODE_GET:
03412         case MODE_GETNEXT:
03413         case MODE_GETBULK:
03414             request->requestvb->type = error_value;
03415             return SNMPERR_SUCCESS;
03416 
03417             /*
03418              * These are technically illegal to set by the
03419              * client APIs for these modes.  But accepting
03420              * them here allows the 'sparse_table' helper to
03421              * provide some common table handling processing
03422              *
03423             snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
03424                      error_value, mode);
03425             return SNMPERR_VALUE;
03426              */
03427 
03428         default:
03429             request->status = SNMP_ERR_NOSUCHNAME;      /* WWW: correct? */
03430             return SNMPERR_SUCCESS;
03431         }
03432         break;                  /* never get here */
03433 
03434     default:
03435         if (error_value < 0) {
03436             /*
03437              * illegal local error code.  translate to generr 
03438              */
03439             /*
03440              * WWW: full translation map? 
03441              */
03442             snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
03443                      error_value, SNMP_ERR_GENERR);
03444             request->status = SNMP_ERR_GENERR;
03445         } else {
03446             /*
03447              * WWW: translations and mode checking? 
03448              */
03449             request->status = error_value;
03450         }
03451         return SNMPERR_SUCCESS;
03452     }
03453     return SNMPERR_SUCCESS;
03454 }
03455 
03460 int
03461 netsnmp_request_set_error(netsnmp_request_info *request, int error_value)
03462 {
03463     if (!request || !request->agent_req_info)
03464         return SNMPERR_NO_VARS;
03465 
03466     return _request_set_error(request, request->agent_req_info->mode,
03467                               error_value);
03468 }
03469 
03475 int
03476 netsnmp_request_set_error_idx(netsnmp_request_info *request,
03477                               int error_value, int idx)
03478 {
03479     int i;
03480     netsnmp_request_info *req = request;
03481 
03482     if (!request || !request->agent_req_info)
03483         return SNMPERR_NO_VARS;
03484 
03485     /*
03486      * Skip to the indicated varbind
03487      */
03488     for ( i=2; i<idx; i++) {
03489         req = req->next;
03490         if (!req)
03491             return SNMPERR_NO_VARS;
03492     }
03493     
03494     return _request_set_error(req, request->agent_req_info->mode,
03495                               error_value);
03496 }
03497 
03503 NETSNMP_INLINE int
03504 netsnmp_request_set_error_all( netsnmp_request_info *requests, int error)
03505 {
03506     int mode, rc, result = SNMPERR_SUCCESS;
03507 
03508     if((NULL == requests) || (NULL == requests->agent_req_info))
03509         return SNMPERR_NO_VARS;
03510     
03511     mode = requests->agent_req_info->mode; /* every req has same mode */
03512     
03513     for(; requests ; requests = requests->next) {
03514 
03516         netsnmp_assert(NULL != requests->agent_req_info);
03517         netsnmp_assert(mode == requests->agent_req_info->mode);
03518 
03519         /*
03520          * set error for this request. Log any errors, save the last
03521          * to return to the user.
03522          */
03523         if((rc = _request_set_error(requests, mode, error))) {
03524             snmp_log(LOG_WARNING,"got %d while setting request error\n", rc);
03525             result = rc;
03526         }
03527     }
03528     return result;
03529 }
03530 
03531 extern struct timeval starttime;
03532 
03533                 /*
03534                  * Return the value of 'sysUpTime' at the given marker 
03535                  */
03536 u_long
03537 netsnmp_marker_uptime(marker_t pm)
03538 {
03539     u_long          res;
03540     marker_t        start = (marker_t) & starttime;
03541 
03542     res = uatime_hdiff(start, pm);
03543     return res;                 /* atime_diff works in msec, not csec */
03544 }
03545 
03546                         /*
03547                          * struct timeval equivalents of these 
03548                          */
03549 u_long
03550 netsnmp_timeval_uptime(struct timeval * tv)
03551 {
03552     return netsnmp_marker_uptime((marker_t) tv);
03553 }
03554 
03555                 /*
03556                  * Return the current value of 'sysUpTime' 
03557                  */
03558 u_long
03559 netsnmp_get_agent_uptime(void)
03560 {
03561     struct timeval  now;
03562     gettimeofday(&now, NULL);
03563 
03564     return netsnmp_timeval_uptime(&now);
03565 }
03566 
03567 
03568 
03569 NETSNMP_INLINE void
03570 netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
03571                             netsnmp_data_list *node)
03572 {
03573     if (ari) {
03574         if (ari->agent_data) {
03575             netsnmp_add_list_data(&ari->agent_data, node);
03576         } else {
03577             ari->agent_data = node;
03578         }
03579     }
03580 }
03581 
03582 NETSNMP_INLINE int
03583 netsnmp_agent_remove_list_data(netsnmp_agent_request_info *ari,
03584                                const char * name)
03585 {
03586     if ((NULL == ari) || (NULL == ari->agent_data))
03587         return 1;
03588 
03589     return netsnmp_remove_list_node(&ari->agent_data, name);
03590 }
03591 
03592 NETSNMP_INLINE void    *
03593 netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
03594                             const char *name)
03595 {
03596     if (ari) {
03597         return netsnmp_get_list_data(ari->agent_data, name);
03598     }
03599     return NULL;
03600 }
03601 
03602 NETSNMP_INLINE void
03603 netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
03604 {
03605     if (ari) {
03606         netsnmp_free_list_data(ari->agent_data);
03607     }
03608 }
03609 
03610 NETSNMP_INLINE void
03611 netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
03612 {
03613     if (ari) {
03614         netsnmp_free_all_list_data(ari->agent_data);
03615     }
03616 }
03617 
03618 NETSNMP_INLINE void
03619 netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
03620 {
03621     if (ari) {
03622         if (ari->agent_data) {
03623             netsnmp_free_all_list_data(ari->agent_data);
03624         }
03625         SNMP_FREE(ari);
03626     }
03627 }
03628 
03629 /*************************************************************************
03630  *
03631  * deprecated functions
03632  *
03633  */
03634 
03642 int
03643 netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
03644                           netsnmp_request_info *request, int error_value)
03645 {
03646     if (!request || !reqinfo)
03647         return error_value;
03648 
03649     _request_set_error(request, reqinfo->mode, error_value);
03650     
03651     return error_value;
03652 }
03653 
03661 int
03662 netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
03663                                int error_value)
03664 {
03665     _request_set_error(request, mode, error_value);
03666     
03667     return error_value;
03668 }
03669 
03677 int
03678 netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
03679                                netsnmp_request_info *requests,
03680                                int error_value)
03681 {
03682     netsnmp_request_set_error_all(requests, error_value);
03683     return error_value;
03684 }