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