Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

snmp_agent.c

00001 /*
00002  * snmp_agent.c
00003  *
00004  * Simple Network Management Protocol (RFC 1067).
00005  */
00006 /* Portions of this file are subject to the following copyright(s).  See
00007  * the Net-SNMP's COPYING file for more details and other copyrights
00008  * that may apply:
00009  */
00010 /* Portions of this file are subject to the following copyrights.  See
00011  * the Net-SNMP's COPYING file for more details and other copyrights
00012  * that may apply:
00013  */
00014 /***********************************************************
00015         Copyright 1988, 1989 by Carnegie Mellon University
00016 
00017                       All Rights Reserved
00018 
00019 Permission to use, copy, modify, and distribute this software and its 
00020 documentation for any purpose and without fee is hereby granted, 
00021 provided that the above copyright notice appear in all copies and that
00022 both that copyright notice and this permission notice appear in 
00023 supporting documentation, and that the name of CMU not be
00024 used in advertising or publicity pertaining to distribution of the
00025 software without specific, written prior permission.  
00026 
00027 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00028 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00029 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00030 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00031 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00032 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00033 SOFTWARE.
00034 ******************************************************************/
00035 /*
00036  * Portions of this file are copyrighted by:
00037  * Copyright © 2003 Sun Microsystems, Inc. All rights 
00038  * reserved.  Use is subject to license terms specified in the 
00039  * COPYING file distributed with the Net-SNMP package.
00040  */
00046 #include <net-snmp/net-snmp-config.h>
00047 
00048 #include <sys/types.h>
00049 #ifdef HAVE_LIMITS_H
00050 #include <limits.h>
00051 #endif
00052 #ifdef HAVE_STDLIB_H
00053 #include <stdlib.h>
00054 #endif
00055 #if HAVE_UNISTD_H
00056 #include <unistd.h>
00057 #endif
00058 #if HAVE_STRING_H
00059 #include <string.h>
00060 #endif
00061 #if TIME_WITH_SYS_TIME
00062 # ifdef WIN32
00063 #  include <sys/timeb.h>
00064 # else
00065 #  include <sys/time.h>
00066 # endif
00067 # include <time.h>
00068 #else
00069 # if HAVE_SYS_TIME_H
00070 #  include <sys/time.h>
00071 # else
00072 #  include <time.h>
00073 # endif
00074 #endif
00075 #if HAVE_SYS_SELECT_H
00076 #include <sys/select.h>
00077 #endif
00078 #if HAVE_NETINET_IN_H
00079 #include <netinet/in.h>
00080 #endif
00081 #include <errno.h>
00082 #if HAVE_WINSOCK_H
00083 #include <winsock.h>
00084 #endif
00085 
00086 #define SNMP_NEED_REQUEST_LIST
00087 #include <net-snmp/net-snmp-includes.h>
00088 #include <net-snmp/agent/net-snmp-agent-includes.h>
00089 #include <net-snmp/library/snmp_assert.h>
00090 
00091 #if HAVE_SYSLOG_H
00092 #include <syslog.h>
00093 #endif
00094 
00095 #ifdef USE_LIBWRAP
00096 #include <tcpd.h>
00097 int             allow_severity = LOG_INFO;
00098 int             deny_severity = LOG_WARNING;
00099 #endif
00100 
00101 #include "snmpd.h"
00102 #include "mibgroup/struct.h"
00103 #include "mibgroup/util_funcs.h"
00104 #include <net-snmp/agent/mib_module_config.h>
00105 #include <net-snmp/agent/mib_modules.h>
00106 
00107 #ifdef USING_AGENTX_PROTOCOL_MODULE
00108 #include "agentx/protocol.h"
00109 #endif
00110 
00111 #ifdef USING_AGENTX_MASTER_MODULE
00112 #include "agentx/master.h"
00113 #endif
00114 
00115 #ifdef USING_SMUX_MODULE
00116 #include "smux/smux.h"
00117 #endif
00118 
00119 oid      version_sysoid[] = { SYSTEM_MIB };
00120 int      version_sysoid_len = OID_LENGTH(version_sysoid);
00121 
00122 #define SNMP_ADDRCACHE_SIZE 10
00123 #define SNMP_ADDRCACHE_MAXAGE 300 /* in seconds */
00124 
00125 enum {
00126     SNMP_ADDRCACHE_UNUSED = 0,
00127     SNMP_ADDRCACHE_USED = 1
00128 };
00129 
00130 struct addrCache {
00131     char           *addr;
00132     int            status;
00133     struct timeval lastHit;
00134 };
00135 
00136 static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
00137 int             log_addresses = 0;
00138 
00139 
00140 
00141 typedef struct _agent_nsap {
00142     int             handle;
00143     netsnmp_transport *t;
00144     void           *s;          /*  Opaque internal session pointer.  */
00145     struct _agent_nsap *next;
00146 } agent_nsap;
00147 
00148 static agent_nsap *agent_nsap_list = NULL;
00149 static netsnmp_agent_session *agent_session_list = NULL;
00150 static netsnmp_agent_session *netsnmp_processing_set = NULL;
00151 netsnmp_agent_session *agent_delegated_list = NULL;
00152 netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
00153 
00154 
00155 int             netsnmp_agent_check_packet(netsnmp_session *,
00156                                            struct netsnmp_transport_s *,
00157                                            void *, int);
00158 int             netsnmp_agent_check_parse(netsnmp_session *, netsnmp_pdu *,
00159                                           int);
00160 void            delete_subnetsnmp_tree_cache(netsnmp_agent_session *asp);
00161 int             handle_pdu(netsnmp_agent_session *asp);
00162 int             netsnmp_handle_request(netsnmp_agent_session *asp,
00163                                        int status);
00164 int             netsnmp_wrap_up_request(netsnmp_agent_session *asp,
00165                                         int status);
00166 int             check_delayed_request(netsnmp_agent_session *asp);
00167 int             handle_getnext_loop(netsnmp_agent_session *asp);
00168 int             handle_set_loop(netsnmp_agent_session *asp);
00169 
00170 int             netsnmp_check_queued_chain_for(netsnmp_agent_session *asp);
00171 int             netsnmp_add_queued(netsnmp_agent_session *asp);
00172 int             netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
00173 
00174 
00175 static int      current_globalid = 0;
00176 
00177 int      netsnmp_running = 1;
00178 
00179 int
00180 netsnmp_allocate_globalcacheid(void)
00181 {
00182     return ++current_globalid;
00183 }
00184 
00185 int
00186 netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
00187 {
00188     while (cache_store != NULL) {
00189         if (cache_store->globalid == globalid)
00190             return cache_store->cacheid;
00191         cache_store = cache_store->next;
00192     }
00193     return -1;
00194 }
00195 
00196 netsnmp_cachemap *
00197 netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
00198                                 int globalid, int localid)
00199 {
00200     netsnmp_cachemap *tmpp;
00201 
00202     tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
00203     if (*cache_store) {
00204         tmpp->next = *cache_store;
00205         *cache_store = tmpp;
00206     } else {
00207         *cache_store = tmpp;
00208     }
00209 
00210     tmpp->globalid = globalid;
00211     tmpp->cacheid = localid;
00212     return tmpp;
00213 }
00214 
00215 void
00216 netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
00217 {
00218     netsnmp_cachemap *tmpp;
00219     while (cache_store) {
00220         tmpp = cache_store;
00221         cache_store = cache_store->next;
00222         SNMP_FREE(tmpp);
00223     }
00224 }
00225 
00226 
00227 typedef struct agent_set_cache_s {
00228     /*
00229      * match on these 2 
00230      */
00231     int             transID;
00232     netsnmp_session *sess;
00233 
00234     /*
00235      * store this info 
00236      */
00237     netsnmp_tree_cache *treecache;
00238     int             treecache_len;
00239     int             treecache_num;
00240 
00241     int             vbcount;
00242     netsnmp_request_info *requests;
00243     netsnmp_variable_list *saved_vars;
00244     netsnmp_data_list *agent_data;
00245 
00246     /*
00247      * list 
00248      */
00249     struct agent_set_cache_s *next;
00250 } agent_set_cache;
00251 
00252 static agent_set_cache *Sets = NULL;
00253 
00254 agent_set_cache *
00255 save_set_cache(netsnmp_agent_session *asp)
00256 {
00257     agent_set_cache *ptr;
00258 
00259     if (!asp || !asp->reqinfo || !asp->pdu)
00260         return NULL;
00261 
00262     ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
00263     if (ptr == NULL)
00264         return NULL;
00265 
00266     /*
00267      * Save the important information 
00268      */
00269     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p saved in cache (mode %d)\n",
00270                 asp, asp->reqinfo, asp->pdu->command));
00271     ptr->transID = asp->pdu->transid;
00272     ptr->sess = asp->session;
00273     ptr->treecache = asp->treecache;
00274     ptr->treecache_len = asp->treecache_len;
00275     ptr->treecache_num = asp->treecache_num;
00276     ptr->agent_data = asp->reqinfo->agent_data;
00277     ptr->requests = asp->requests;
00278     ptr->saved_vars = asp->pdu->variables; /* requests contains pointers to variables */
00279     ptr->vbcount = asp->vbcount;
00280 
00281     /*
00282      * make the agent forget about what we've saved 
00283      */
00284     asp->treecache = NULL;
00285     asp->reqinfo->agent_data = NULL;
00286     asp->pdu->variables = NULL;
00287     asp->requests = NULL;
00288 
00289     ptr->next = Sets;
00290     Sets = ptr;
00291 
00292     return ptr;
00293 }
00294 
00295 int
00296 get_set_cache(netsnmp_agent_session *asp)
00297 {
00298     agent_set_cache *ptr, *prev = NULL;
00299 
00300     for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
00301         if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
00302             /*
00303              * remove this item from list
00304              */
00305             if (prev)
00306                 prev->next = ptr->next;
00307             else
00308                 Sets = ptr->next;
00309 
00310             /*
00311              * found it.  Get the needed data 
00312              */
00313             asp->treecache = ptr->treecache;
00314             asp->treecache_len = ptr->treecache_len;
00315             asp->treecache_num = ptr->treecache_num;
00316 
00317             /*
00318              * Free previously allocated requests before overwriting by
00319              * cached ones, otherwise memory leaks!
00320              */
00321             if (asp->requests) {
00322                 /*
00323                  * I don't think this case should ever happen. Please email
00324                  * the net-snmp-coders@lists.sourceforge.net if you have
00325                  * a test case that hits this assert. -- rstory
00326                  */
00327                 int i;
00328                 netsnmp_assert(NULL == asp->requests); /* see note above */
00329                 for (i = 0; i < asp->vbcount; i++) {
00330                     netsnmp_free_request_data_sets(&asp->requests[i]);
00331                 }
00332                 free(asp->requests);
00333             }
00334             /*
00335              * If we replace asp->requests with the info from the set cache,
00336              * we should replace asp->pdu->variables also with the cached
00337              * info, as asp->requests contains pointers to them.  And we
00338              * should also free the current asp->pdu->variables list...
00339              */
00340             if (ptr->saved_vars) {
00341                 if (asp->pdu->variables)
00342                     snmp_free_varbind(asp->pdu->variables);
00343                 asp->pdu->variables = ptr->saved_vars;
00344                 asp->vbcount = ptr->vbcount;
00345             } else {
00346                 /*
00347                  * when would we not have saved variables? someone
00348                  * let me know if they hit this assert. -- rstory
00349                  */
00350                 netsnmp_assert(NULL != ptr->saved_vars);
00351             }
00352             asp->requests = ptr->requests;
00353 
00354             netsnmp_assert(NULL != asp->reqinfo);
00355             asp->reqinfo->asp = asp;
00356             asp->reqinfo->agent_data = ptr->agent_data;
00357             
00358             /*
00359              * update request reqinfo, if it's out of date.
00360              * yyy-rks: investigate when/why sometimes they match,
00361              * sometimes they don't.
00362              */
00363             if(asp->requests->agent_req_info != asp->reqinfo) {
00364                 /*
00365                  * - one don't match case: agentx subagents. prev asp & reqinfo
00366                  *   freed, request reqinfo ptrs not cleared.
00367                  */
00368                 netsnmp_request_info *tmp = asp->requests;
00369                 DEBUGMSGTL(("verbose:asp",
00370                             "  reqinfo %p doesn't match cached reqinfo %p\n",
00371                             asp->reqinfo, asp->requests->agent_req_info));
00372                 for(; tmp; tmp = tmp->next)
00373                     tmp->agent_req_info = asp->reqinfo;
00374             } else {
00375                 /*
00376                  * - match case: ?
00377                  */
00378                 DEBUGMSGTL(("verbose:asp",
00379                             "  reqinfo %p matches cached reqinfo %p\n",
00380                             asp->reqinfo, asp->requests->agent_req_info));
00381             }
00382 
00383             SNMP_FREE(ptr);
00384             return SNMP_ERR_NOERROR;
00385         }
00386         prev = ptr;
00387     }
00388     return SNMP_ERR_GENERR;
00389 }
00390 
00391 /* Bulkcache holds the values for the *repeating* varbinds (only),
00392  *   but ordered "by column" - i.e. the repetitions for each
00393  *   repeating varbind follow on immediately from one another,
00394  *   rather than being interleaved, as required by the protocol.
00395  *
00396  * So we need to rearrange the varbind list so it's ordered "by row".
00397  *
00398  * In the following code chunk:
00399  *     n            = # non-repeating varbinds
00400  *     r            = # repeating varbinds
00401  *     asp->vbcount = # varbinds in the incoming PDU
00402  *         (So asp->vbcount = n+r)
00403  *
00404  *     repeats = Desired # of repetitions (of 'r' varbinds)
00405  */
00406 NETSNMP_STATIC_INLINE void
00407 _reorder_getbulk(netsnmp_agent_session *asp)
00408 {
00409     int             i, n = 0, r = 0;
00410     int             repeats = asp->pdu->errindex;
00411     int             j;
00412     int             all_eoMib;
00413     netsnmp_variable_list *prev = NULL;
00414             
00415     if (asp->vbcount == 0)  /* Nothing to do! */
00416         return;
00417 
00418     if (asp->pdu->errstat < asp->vbcount) {
00419         n = asp->pdu->errstat;
00420     } else {
00421         n = asp->vbcount;
00422     }
00423     if ((r = asp->vbcount - n) < 0) {
00424         r = 0;
00425     }
00426 
00427     /* we do nothing if there is nothing repeated */
00428     if (r == 0)
00429         return;
00430             
00431     /*
00432      * For each of the original repeating varbinds (except the last),
00433      *  go through the block of results for that varbind,
00434      *  and link each instance to the corresponding instance
00435      *  in the next block.
00436      */
00437     for (i = 0; i < r - 1; i++) {
00438         prev = NULL;
00439         for (j = 0; j < repeats; j++) {
00440             /*
00441              *  If we don't have a valid name for a given repetition
00442              *   (and probably for all the ones that follow as well),
00443              *   extend the previous result to indicate 'endOfMibView'
00444              */
00445             if (asp->bulkcache[i * repeats + j]->name_length == 0
00446                 && prev) {
00447                 snmp_set_var_objid(
00448                     asp->bulkcache[i * repeats + j],
00449                     prev->name, prev->name_length);
00450                 snmp_set_var_typed_value(
00451                     asp->bulkcache[i * repeats + j],
00452                     SNMP_ENDOFMIBVIEW, NULL, 0);
00453             }
00454             prev = asp->bulkcache[i * repeats + j];
00455 
00456             asp->bulkcache[i * repeats + j]->next_variable =
00457                 asp->bulkcache[(i + 1) * repeats + j];
00458         }
00459     }
00460     /*
00461      * For the last of the original repeating varbinds,
00462      *  go through that block of results, and link each
00463      *  instance to the *next* instance in the *first* block.
00464      *
00465      * The very last instance of this block is left untouched
00466      *  since it (correctly) points to the end of the list.
00467      */
00468     if (r > 0) {
00469         prev = NULL;
00470         for (j = 0; j < repeats - 1; j++) {
00471             /*
00472              *  Fill in missing names with 'endOfMibView' as above...
00473              */
00474             if (asp->bulkcache[(r - 1) * repeats + j]->name_length == 0
00475                 && prev) {
00476                 snmp_set_var_objid(
00477                     asp->bulkcache[(r - 1) * repeats + j],
00478                     prev->name, prev->name_length);
00479                 snmp_set_var_typed_value(
00480                     asp->bulkcache[(r - 1) * repeats + j],
00481                     SNMP_ENDOFMIBVIEW, NULL, 0);
00482             }
00483             prev = asp->bulkcache[(r - 1) * repeats + j];
00484             asp->bulkcache[(r - 1) * repeats + j]->next_variable =
00485                 asp->bulkcache[j + 1];
00486         }
00487         /*
00488          *  ... Not forgetting the very last entry
00489          */
00490         if (asp->bulkcache[r * repeats - 1]->name_length == 0
00491             && prev) {
00492             snmp_set_var_objid(
00493                 asp->bulkcache[r * repeats - 1],
00494                 prev->name, prev->name_length);
00495             snmp_set_var_typed_value(
00496                 asp->bulkcache[r * repeats - 1],
00497                 SNMP_ENDOFMIBVIEW, NULL, 0);
00498         }
00499     }
00500 
00501     /*
00502      * If we've got a full row of endOfMibViews, then we
00503      *  can truncate the result varbind list after that.
00504      *
00505      * Look for endOfMibView exception values in the list of
00506      *  repetitions for the first varbind, and check the 
00507      *  corresponding instances for the other varbinds
00508      *  (following the next_variable links).
00509      *
00510      * If they're all endOfMibView too, then we can terminate
00511      *  the linked list there, and free any redundant varbinds.
00512      */
00513     all_eoMib = 0;
00514     for (i = 0; i < repeats; i++) {
00515         if (asp->bulkcache[i]->type == SNMP_ENDOFMIBVIEW) {
00516             all_eoMib = 1;
00517             for (j = 1, prev=asp->bulkcache[i];
00518                  j < r;
00519                  j++, prev=prev->next_variable) {
00520                 if (prev->type != SNMP_ENDOFMIBVIEW) {
00521                     all_eoMib = 0;
00522                     break;      /* Found a real value */
00523                 }
00524             }
00525             if (all_eoMib) {
00526                 /*
00527                  * This is indeed a full endOfMibView row.
00528                  * Terminate the list here & free the rest.
00529                  */
00530                 snmp_free_varbind( prev->next_variable );
00531                 prev->next_variable = NULL;
00532                 break;
00533             }
00534         }
00535     }
00536 }
00537 
00538 
00539 int
00540 getNextSessID()
00541 {
00542     static int      SessionID = 0;
00543 
00544     return ++SessionID;
00545 }
00546 
00559 int
00560 agent_check_and_process(int block)
00561 {
00562     int             numfds;
00563     fd_set          fdset;
00564     struct timeval  timeout = { LONG_MAX, 0 }, *tvp = &timeout;
00565     int             count;
00566     int             fakeblock = 0;
00567 
00568     numfds = 0;
00569     FD_ZERO(&fdset);
00570     snmp_select_info(&numfds, &fdset, tvp, &fakeblock);
00571     if (block != 0 && fakeblock != 0) {
00572         /*
00573          * There are no alarms registered, and the caller asked for blocking, so
00574          * let select() block forever.  
00575          */
00576 
00577         tvp = NULL;
00578     } else if (block != 0 && fakeblock == 0) {
00579         /*
00580          * The caller asked for blocking, but there is an alarm due sooner than
00581          * LONG_MAX seconds from now, so use the modified timeout returned by
00582          * snmp_select_info as the timeout for select().  
00583          */
00584 
00585     } else if (block == 0) {
00586         /*
00587          * The caller does not want us to block at all.  
00588          */
00589 
00590         tvp->tv_sec = 0;
00591         tvp->tv_usec = 0;
00592     }
00593 
00594     count = select(numfds, &fdset, 0, 0, tvp);
00595 
00596     if (count > 0) {
00597         /*
00598          * packets found, process them 
00599          */
00600         snmp_read(&fdset);
00601     } else
00602         switch (count) {
00603         case 0:
00604             snmp_timeout();
00605             break;
00606         case -1:
00607             if (errno != EINTR) {
00608                 snmp_log_perror("select");
00609             }
00610             return -1;
00611         default:
00612             snmp_log(LOG_ERR, "select returned %d\n", count);
00613             return -1;
00614         }                       /* endif -- count>0 */
00615 
00616     /*
00617      * Run requested alarms.  
00618      */
00619     run_alarms();
00620 
00621     netsnmp_check_outstanding_agent_requests();
00622 
00623     return count;
00624 }
00625 
00626 
00627 /*
00628  * Set up the address cache.  
00629  */
00630 void
00631 netsnmp_addrcache_initialise(void)
00632 {
00633     int             i = 0;
00634 
00635     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
00636         addrCache[i].addr = NULL;
00637         addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
00638     }
00639 }
00640 
00641 /*
00642  * Adds a new entry to the cache of addresses that
00643  * have recently made connections to the agent.
00644  * Returns 0 if the entry already exists (but updates
00645  * the entry with a new timestamp) and 1 if the
00646  * entry did not previously exist.
00647  *
00648  * Implements a simple LRU cache replacement
00649  * policy. Uses a linear search, which should be
00650  * okay, as long as SNMP_ADDRCACHE_SIZE remains
00651  * relatively small.
00652  *
00653  * @retval 0 : updated existing entry
00654  * @retval 1 : added new entry
00655  */
00656 int
00657 netsnmp_addrcache_add(const char *addr)
00658 {
00659     int oldest = -1; /* Index of the oldest cache entry */
00660     int unused = -1; /* Index of the first free cache entry */
00661     int i; /* Looping variable */
00662     int rc = -1;
00663     struct timeval now; /* What time is it now? */
00664     struct timeval aged; /* Oldest allowable cache entry */
00665 
00666     /*
00667      * First get the current and oldest allowable timestamps
00668      */
00669     gettimeofday(&now, (struct timezone*) NULL);
00670     aged.tv_sec = now.tv_sec - SNMP_ADDRCACHE_MAXAGE;
00671     aged.tv_usec = now.tv_usec;
00672 
00673     /*
00674      * Now look for a place to put this thing
00675      */
00676     for(i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
00677         if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) { /* If unused */
00678             /*
00679              * remember this location, in case addr isn't in the cache
00680              */
00681             if (unused < 0)
00682                 unused = i;
00683         }
00684         else { /* If used */
00685             if ((NULL != addr) && (strcmp(addrCache[i].addr, addr) == 0)) {
00686                 /*
00687                  * found a match
00688                  */
00689                 memcpy(&addrCache[i].lastHit, &now, sizeof(struct timeval));
00690                 if (timercmp(&addrCache[i].lastHit, &aged, <))
00691                     rc = 1; /* should have expired, so is new */
00692                 else
00693                     rc = 0; /* not expired, so is existing entry */
00694                 break;
00695             }
00696             else {
00697                 /*
00698                  * Used, but not this address. check if it's stale.
00699                  */
00700                 if (timercmp(&addrCache[i].lastHit, &aged, <)) {
00701                     /*
00702                      * Stale, reuse
00703                      */
00704                     SNMP_FREE(addrCache[i].addr);
00705                     addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
00706                     /*
00707                      * remember this location, in case addr isn't in the cache
00708                      */
00709                     if (unused < 0)
00710                         unused = i;
00711                 }
00712                 else {
00713                     /*
00714                      * Still fresh, but a candidate for LRU replacement
00715                      */
00716                     if (oldest < 0)
00717                         oldest = i;
00718                     else if (timercmp(&addrCache[i].lastHit,
00719                                       &addrCache[oldest].lastHit, <))
00720                         oldest = i;
00721                 } /* fresh */
00722             } /* used, no match */
00723         } /* used */
00724     } /* for loop */
00725 
00726     if ((-1 == rc) && (NULL != addr)) {
00727         /*
00728          * We didn't find the entry in the cache
00729          */
00730         if (unused >= 0) {
00731             /*
00732              * If we have a slot free anyway, use it
00733              */
00734             addrCache[unused].addr = strdup(addr);
00735             addrCache[unused].status = SNMP_ADDRCACHE_USED;
00736             memcpy(&addrCache[unused].lastHit, &now, sizeof(struct timeval));
00737         }
00738         else { /* Otherwise, replace oldest entry */
00739             if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00740                                        NETSNMP_DS_AGENT_VERBOSE))
00741                 snmp_log(LOG_INFO, "Purging address from address cache: %s",
00742                          addrCache[oldest].addr);
00743             
00744             free(addrCache[oldest].addr);
00745             addrCache[oldest].addr = strdup(addr);
00746             memcpy(&addrCache[oldest].lastHit, &now, sizeof(struct timeval));
00747         }
00748         rc = 1;
00749     }
00750     if ((log_addresses && (1 == rc)) ||
00751         netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00752                                NETSNMP_DS_AGENT_VERBOSE)) {
00753         snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n", addr);
00754      }
00755 
00756     return rc;
00757 }
00758 
00759 /*
00760  * Age the entries in the address cache.  
00761  *
00762  * backwards compatability; not used anywhere
00763  */
00764 void
00765 netsnmp_addrcache_age(void)
00766 {
00767     (void)netsnmp_addrcache_add(NULL);
00768 }
00769 
00770 /*******************************************************************-o-******
00771  * netsnmp_agent_check_packet
00772  *
00773  * Parameters:
00774  *      session, transport, transport_data, transport_data_length
00775  *      
00776  * Returns:
00777  *      1       On success.
00778  *      0       On error.
00779  *
00780  * Handler for all incoming messages (a.k.a. packets) for the agent.  If using
00781  * the libwrap utility, log the connection and deny/allow the access. Print
00782  * output when appropriate, and increment the incoming counter.
00783  *
00784  */
00785 
00786 int
00787 netsnmp_agent_check_packet(netsnmp_session * session,
00788                            netsnmp_transport *transport,
00789                            void *transport_data, int transport_data_length)
00790 {
00791     char           *addr_string = NULL;
00792 #ifdef  USE_LIBWRAP
00793     char *tcpudpaddr, *name;
00794 
00795     name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00796                                  NETSNMP_DS_LIB_APPTYPE);
00797 #endif
00798 
00799     /*
00800      * Log the message and/or dump the message.
00801      * Optionally cache the network address of the sender.
00802      */
00803 
00804     if (transport != NULL && transport->f_fmtaddr != NULL) {
00805         /*
00806          * Okay I do know how to format this address for logging.  
00807          */
00808         addr_string = transport->f_fmtaddr(transport, transport_data,
00809                                            transport_data_length);
00810         /*
00811          * Don't forget to free() it.  
00812          */
00813     }
00814 #ifdef  USE_LIBWRAP
00815     /* Catch udp,udp6,tcp,tcp6 transports using "[" */
00816     tcpudpaddr = strstr(addr_string, "[");
00817     if ( tcpudpaddr != 0 ) {
00818         char sbuf[64];
00819         char *xp;
00820         strncpy(sbuf, tcpudpaddr + 1, sizeof(sbuf));
00821         sbuf[sizeof(sbuf)-1] = '\0';
00822         xp = strstr(sbuf, "]");
00823         if (xp)
00824             *xp = '\0';
00825  
00826         if (hosts_ctl(name, STRING_UNKNOWN, sbuf, STRING_UNKNOWN)) {
00827             snmp_log(allow_severity, "Connection from %s\n", addr_string);
00828         } else {
00829             snmp_log(deny_severity, "Connection from %s REFUSED\n",
00830                      addr_string);
00831             SNMP_FREE(addr_string);
00832             return 0;
00833         }
00834     } else {
00835         /*
00836          * don't log callback connections.
00837          * What about 'Local IPC', 'IPX' and 'AAL5 PVC'?
00838          */
00839         if (0 == strncmp(addr_string, "callback", 8))
00840             ;
00841         else if (hosts_ctl(name, STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
00842             snmp_log(allow_severity, "Connection from <UNKNOWN> (%s)\n", addr_string);
00843             addr_string = strdup("<UNKNOWN>");
00844         } else {
00845             snmp_log(deny_severity, "Connection from <UNKNOWN> (%s) REFUSED\n", addr_string);
00846             return 0;
00847         }
00848     }
00849 #endif                          /*USE_LIBWRAP */
00850 
00851     snmp_increment_statistic(STAT_SNMPINPKTS);
00852 
00853     if (addr_string != NULL) {
00854         netsnmp_addrcache_add(addr_string);
00855         SNMP_FREE(addr_string);
00856     }
00857     return 1;
00858 }
00859 
00860 
00861 int
00862 netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
00863                           int result)
00864 {
00865     if (result == 0) {
00866         if (snmp_get_do_logging() &&
00867             netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
00868                                    NETSNMP_DS_AGENT_VERBOSE)) {
00869             netsnmp_variable_list *var_ptr;
00870 
00871             switch (pdu->command) {
00872             case SNMP_MSG_GET:
00873                 snmp_log(LOG_DEBUG, "  GET message\n");
00874                 break;
00875             case SNMP_MSG_GETNEXT:
00876                 snmp_log(LOG_DEBUG, "  GETNEXT message\n");
00877                 break;
00878             case SNMP_MSG_RESPONSE:
00879                 snmp_log(LOG_DEBUG, "  RESPONSE message\n");
00880                 break;
00881             case SNMP_MSG_SET:
00882                 snmp_log(LOG_DEBUG, "  SET message\n");
00883                 break;
00884             case SNMP_MSG_TRAP:
00885                 snmp_log(LOG_DEBUG, "  TRAP message\n");
00886                 break;
00887             case SNMP_MSG_GETBULK:
00888                 snmp_log(LOG_DEBUG, "  GETBULK message, non-rep=%ld, max_rep=%ld\n",
00889                          pdu->errstat, pdu->errindex);
00890                 break;
00891             case SNMP_MSG_INFORM:
00892                 snmp_log(LOG_DEBUG, "  INFORM message\n");
00893                 break;
00894             case SNMP_MSG_TRAP2:
00895                 snmp_log(LOG_DEBUG, "  TRAP2 message\n");
00896                 break;
00897             case SNMP_MSG_REPORT:
00898                 snmp_log(LOG_DEBUG, "  REPORT message\n");
00899                 break;
00900 
00901             case SNMP_MSG_INTERNAL_SET_RESERVE1:
00902                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE1 message\n");
00903                 break;
00904 
00905             case SNMP_MSG_INTERNAL_SET_RESERVE2:
00906                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE2 message\n");
00907                 break;
00908 
00909             case SNMP_MSG_INTERNAL_SET_ACTION:
00910                 snmp_log(LOG_DEBUG, "  INTERNAL ACTION message\n");
00911                 break;
00912 
00913             case SNMP_MSG_INTERNAL_SET_COMMIT:
00914                 snmp_log(LOG_DEBUG, "  INTERNAL COMMIT message\n");
00915                 break;
00916 
00917             case SNMP_MSG_INTERNAL_SET_FREE:
00918                 snmp_log(LOG_DEBUG, "  INTERNAL FREE message\n");
00919                 break;
00920 
00921             case SNMP_MSG_INTERNAL_SET_UNDO:
00922                 snmp_log(LOG_DEBUG, "  INTERNAL UNDO message\n");
00923                 break;
00924 
00925             default:
00926                 snmp_log(LOG_DEBUG, "  UNKNOWN message, type=%02X\n",
00927                          pdu->command);
00928                 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
00929                 return 0;
00930             }
00931 
00932             for (var_ptr = pdu->variables; var_ptr != NULL;
00933                  var_ptr = var_ptr->next_variable) {
00934                 size_t          c_oidlen = 256, c_outlen = 0;
00935                 u_char         *c_oid = (u_char *) malloc(c_oidlen);
00936 
00937                 if (c_oid) {
00938                     if (!sprint_realloc_objid
00939                         (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
00940                          var_ptr->name_length)) {
00941                         snmp_log(LOG_DEBUG, "    -- %s [TRUNCATED]\n",
00942                                  c_oid);
00943                     } else {
00944                         snmp_log(LOG_DEBUG, "    -- %s\n", c_oid);
00945                     }
00946                     SNMP_FREE(c_oid);
00947                 }
00948             }
00949         }
00950         return 1;
00951     }
00952     return 0;                   /* XXX: does it matter what the return value
00953                                  * is?  Yes: if we return 0, then the PDU is
00954                                  * dumped.  */
00955 }
00956 
00957 
00958 /*
00959  * Global access to the primary session structure for this agent.
00960  * for Index Allocation use initially. 
00961  */
00962 
00963 /*
00964  * I don't understand what this is for at the moment.  AFAICS as long as it
00965  * gets set and points at a session, that's fine.  ???  
00966  */
00967 
00968 netsnmp_session *main_session = NULL;
00969 
00970 
00971 
00972 /*
00973  * Set up an agent session on the given transport.  Return a handle
00974  * which may later be used to de-register this transport.  A return
00975  * value of -1 indicates an error.  
00976  */
00977 
00978 int
00979 netsnmp_register_agent_nsap(netsnmp_transport *t)
00980 {
00981     netsnmp_session *s, *sp = NULL;
00982     agent_nsap     *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
00983     int             handle = 0;
00984     void           *isp = NULL;
00985 
00986     if (t == NULL) {
00987         return -1;
00988     }
00989 
00990     DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
00991 
00992     n = (agent_nsap *) malloc(sizeof(agent_nsap));
00993     if (n == NULL) {
00994         return -1;
00995     }
00996     s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
00997     if (s == NULL) {
00998         SNMP_FREE(n);
00999         return -1;
01000     }
01001     memset(s, 0, sizeof(netsnmp_session));
01002     snmp_sess_init(s);
01003 
01004     /*
01005      * Set up the session appropriately for an agent.  
01006      */
01007 
01008     s->version = SNMP_DEFAULT_VERSION;
01009     s->callback = handle_snmp_packet;
01010     s->authenticator = NULL;
01011     s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 
01012                                   NETSNMP_DS_AGENT_FLAGS);
01013     s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
01014 
01015     sp = snmp_add(s, t, netsnmp_agent_check_packet,
01016                   netsnmp_agent_check_parse);
01017     if (sp == NULL) {
01018         SNMP_FREE(s);
01019         SNMP_FREE(n);
01020         return -1;
01021     }
01022 
01023     isp = snmp_sess_pointer(sp);
01024     if (isp == NULL) {          /*  over-cautious  */
01025         SNMP_FREE(s);
01026         SNMP_FREE(n);
01027         return -1;
01028     }
01029 
01030     n->s = isp;
01031     n->t = t;
01032 
01033     if (main_session == NULL) {
01034         main_session = snmp_sess_session(isp);
01035     }
01036 
01037     for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
01038          a = a->next) {
01039         handle = a->handle;
01040         prevNext = &(a->next);
01041     }
01042 
01043     if (handle < INT_MAX) {
01044         n->handle = handle + 1;
01045         n->next = a;
01046         *prevNext = n;
01047         SNMP_FREE(s);
01048         return n->handle;
01049     } else {
01050         SNMP_FREE(s);
01051         SNMP_FREE(n);
01052         return -1;
01053     }
01054 }
01055 
01056 void
01057 netsnmp_deregister_agent_nsap(int handle)
01058 {
01059     agent_nsap     *a = NULL, **prevNext = &agent_nsap_list;
01060     int             main_session_deregistered = 0;
01061 
01062     DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
01063 
01064     for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
01065         prevNext = &(a->next);
01066     }
01067 
01068     if (a != NULL && a->handle == handle) {
01069         *prevNext = a->next;
01070         if (main_session == snmp_sess_session(a->s)) {
01071             main_session_deregistered = 1;
01072         }
01073         snmp_close(snmp_sess_session(a->s));
01074         /*
01075          * The above free()s the transport and session pointers.  
01076          */
01077         SNMP_FREE(a);
01078     }
01079 
01080     /*
01081      * If we've deregistered the session that main_session used to point to,
01082      * then make it point to another one, or in the last resort, make it equal
01083      * to NULL.  Basically this shouldn't ever happen in normal operation
01084      * because main_session starts off pointing at the first session added by
01085      * init_master_agent(), which then discards the handle.  
01086      */
01087 
01088     if (main_session_deregistered) {
01089         if (agent_nsap_list != NULL) {
01090             DEBUGMSGTL(("snmp_agent",
01091                         "WARNING: main_session ptr changed from %p to %p\n",
01092                         main_session, snmp_sess_session(agent_nsap_list->s)));
01093             main_session = snmp_sess_session(agent_nsap_list->s);
01094         } else {
01095             DEBUGMSGTL(("snmp_agent",
01096                         "WARNING: main_session ptr changed from %p to NULL\n",
01097                         main_session));
01098             main_session = NULL;
01099         }
01100     }
01101 }
01102 
01103 
01104 
01105 /*
01106  * 
01107  * This function has been modified to use the experimental
01108  * netsnmp_register_agent_nsap interface.  The major responsibility of this
01109  * function now is to interpret a string specified to the agent (via -p on the
01110  * command line, or from a configuration file) as a list of agent NSAPs on
01111  * which to listen for SNMP packets.  Typically, when you add a new transport
01112  * domain "foo", you add code here such that if the "foo" code is compiled
01113  * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
01114  * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
01115  * transport descriptor.  netsnmp_register_agent_nsap is then called with that
01116  * transport descriptor and sets up a listening agent session on it.
01117  * 
01118  * Everything then works much as normal: the agent runs in an infinite loop
01119  * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
01120  * is readable on any of the given transports.  This routine then traverses
01121  * the library 'Sessions' list to identify the relevant session and eventually
01122  * invokes '_sess_read'.  This then processes the incoming packet, calling the
01123  * pre_parse, parse, post_parse and callback routines in turn.
01124  * 
01125  * JBPN 20001117
01126  */
01127 
01128 int
01129 init_master_agent(void)
01130 {
01131     netsnmp_transport *transport;
01132     char           *cptr;
01133     char            buf[SPRINT_MAX_LEN];
01134     char           *st;
01135 
01136     /* default to a default cache size */
01137     netsnmp_set_lookup_cache_size(-1);
01138 
01139     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
01140                                NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
01141         DEBUGMSGTL(("snmp_agent",
01142                     "init_master_agent; not master agent\n"));
01143 
01144         netsnmp_assert("agent role !master && !sub_agent");
01145         
01146         return 0;               /*  No error if ! MASTER_AGENT  */
01147     }
01148 #ifdef USING_AGENTX_MASTER_MODULE
01149     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
01150                                NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
01151         real_init_master();
01152 #endif
01153 #ifdef USING_SMUX_MODULE
01154     if(should_init("smux"))
01155     real_init_smux();
01156 #endif
01157 
01158     /*
01159      * Have specific agent ports been specified?  
01160      */
01161     cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
01162                                  NETSNMP_DS_AGENT_PORTS);
01163 
01164     if (cptr) {
01165         snprintf(buf, sizeof(buf), "%s", cptr);
01166         buf[ sizeof(buf)-1 ] = 0;
01167     } else {
01168         /*
01169          * No, so just specify the default port.  
01170          */
01171         if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_FLAGS) & SNMP_FLAGS_STREAM_SOCKET) {
01172             sprintf(buf, "tcp:%d", SNMP_PORT);
01173         } else {
01174             sprintf(buf, "udp:%d", SNMP_PORT);
01175         }
01176     }
01177 
01178     DEBUGMSGTL(("snmp_agent", "final port spec: %s\n", buf));
01179     cptr = strtok_r(buf, ",", &st);
01180     while (cptr) {
01181         /*
01182          * Specification format: 
01183          * 
01184          * NONE:                      (a pseudo-transport)
01185          * UDP:[address:]port        (also default if no transport is specified)
01186          * TCP:[address:]port         (if supported)
01187          * Unix:pathname              (if supported)
01188          * AAL5PVC:itf.vpi.vci        (if supported)
01189          * IPX:[network]:node[/port] (if supported)
01190          * 
01191          */
01192 
01193         DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
01194                     cptr));
01195 
01196         if (!cptr || !(*cptr)) {
01197             snmp_log(LOG_ERR, "improper port specification\n");
01198             return 1;
01199         }
01200 
01201         if (strncasecmp(cptr, "none", 4) == 0) {
01202             DEBUGMSGTL(("snmp_agent",
01203                         "init_master_agent; pseudo-transport \"none\" requested\n"));
01204             return 0;
01205         }
01206         transport = netsnmp_tdomain_transport(cptr, 1, "udp");
01207 
01208         if (transport == NULL) {
01209             snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n",
01210                      cptr);
01211             return 1;
01212         }
01213 
01214         if (netsnmp_register_agent_nsap(transport) == 0) {
01215             snmp_log(LOG_ERR,
01216                      "Error registering specified transport \"%s\" as an agent NSAP\n",
01217                      cptr);
01218             return 1;
01219         } else {
01220             DEBUGMSGTL(("snmp_agent",
01221                         "init_master_agent; \"%s\" registered as an agent NSAP\n",
01222                         cptr));
01223         }
01224 
01225         /*
01226          * Next transport please...  
01227          */
01228         cptr = strtok_r(NULL, ",", &st);
01229     }
01230 
01231     return 0;
01232 }
01233 
01234 void
01235 clear_nsap_list(void)
01236 {
01237     DEBUGMSGTL(("clear_nsap_list", "clear the nsap list\n"));
01238 
01239     while (agent_nsap_list != NULL)
01240         netsnmp_deregister_agent_nsap(agent_nsap_list->handle);
01241 }
01242 
01243 void
01244 shutdown_master_agent(void)
01245 {
01246     clear_nsap_list();
01247 }
01248 
01249 
01250 netsnmp_agent_session *
01251 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
01252 {
01253     netsnmp_agent_session *asp = (netsnmp_agent_session *)
01254         calloc(1, sizeof(netsnmp_agent_session));
01255 
01256     if (asp == NULL) {
01257         return NULL;
01258     }
01259 
01260     DEBUGMSGTL(("snmp_agent","agent_sesion %08p created\n", asp));
01261     asp->session = session;
01262     asp->pdu = snmp_clone_pdu(pdu);
01263     asp->orig_pdu = snmp_clone_pdu(pdu);
01264     asp->rw = READ;
01265     asp->exact = TRUE;
01266     asp->next = NULL;
01267     asp->mode = RESERVE1;
01268     asp->status = SNMP_ERR_NOERROR;
01269     asp->index = 0;
01270     asp->oldmode = 0;
01271     asp->treecache_num = -1;
01272     asp->treecache_len = 0;
01273     asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
01274     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p created\n",
01275                 asp, asp->reqinfo));
01276 
01277     return asp;
01278 }
01279 
01280 void
01281 free_agent_snmp_session(netsnmp_agent_session *asp)
01282 {
01283     if (!asp)
01284         return;
01285 
01286     DEBUGMSGTL(("snmp_agent","agent_session %08p released\n", asp));
01287 
01288     netsnmp_remove_from_delegated(asp);
01289     
01290     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p freed\n",
01291                 asp, asp->reqinfo));
01292     if (asp->orig_pdu)
01293         snmp_free_pdu(asp->orig_pdu);
01294     if (asp->pdu)
01295         snmp_free_pdu(asp->pdu);
01296     if (asp->reqinfo)
01297         netsnmp_free_agent_request_info(asp->reqinfo);
01298     if (asp->treecache) {
01299         SNMP_FREE(asp->treecache);
01300     }
01301     if (asp->bulkcache) {
01302         SNMP_FREE(asp->bulkcache);
01303     }
01304     if (asp->requests) {
01305         int             i;
01306         for (i = 0; i < asp->vbcount; i++) {
01307             netsnmp_free_request_data_sets(&asp->requests[i]);
01308         }
01309         SNMP_FREE(asp->requests);
01310     }
01311     if (asp->cache_store) {
01312         netsnmp_free_cachemap(asp->cache_store);
01313         asp->cache_store = NULL;
01314     }
01315     SNMP_FREE(asp);
01316 }
01317 
01318 int
01319 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
01320 {
01321     int             i;
01322     netsnmp_request_info *request;
01323 
01324     if (NULL == asp->treecache)
01325         return 0;
01326     
01327     for (i = 0; i <= asp->treecache_num; i++) {
01328         for (request = asp->treecache[i].requests_begin; request;
01329              request = request->next) {
01330             if (request->delegated)
01331                 return 1;
01332         }
01333     }
01334     return 0;
01335 }
01336 
01337 int
01338 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
01339 {
01340     netsnmp_agent_session *asptmp;
01341     for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
01342         if (asptmp == asp)
01343             return 1;
01344     }
01345     return 0;
01346 }
01347 
01348 int
01349 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
01350 {
01351     if (netsnmp_check_for_delegated(asp)) {
01352         if (!netsnmp_check_delegated_chain_for(asp)) {
01353             /*
01354              * add to delegated request chain 
01355              */
01356             asp->next = agent_delegated_list;
01357             agent_delegated_list = asp;
01358             DEBUGMSGTL(("snmp_agent", "delegate session == %08p\n", asp));
01359         }
01360         return 1;
01361     }
01362     return 0;
01363 }
01364 
01365 int
01366 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
01367 {
01368     netsnmp_agent_session *curr, *prev = NULL;
01369     
01370     for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
01371         /*
01372          * is this us?
01373          */
01374         if (curr != asp)
01375             continue;
01376         
01377         /*
01378          * remove from queue 
01379          */
01380         if (prev != NULL)
01381             prev->next = asp->next;
01382         else
01383             agent_delegated_list = asp->next;
01384 
01385         DEBUGMSGTL(("snmp_agent", "remove delegated session == %08p\n", asp));
01386 
01387         return 1;
01388     }
01389 
01390     return 0;
01391 }
01392 
01393 /*
01394  * netsnmp_remove_delegated_requests_for_session
01395  *
01396  * called when a session is being closed. Check all delegated requests to
01397  * see if the are waiting on this session, and if set, set the status for
01398  * that request to GENERR.
01399  */
01400 int
01401 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
01402 {
01403     netsnmp_agent_session *asp;
01404     int count = 0;
01405     
01406     for (asp = agent_delegated_list; asp; asp = asp->next) {
01407         /*
01408          * check each request
01409          */
01410         netsnmp_request_info *request;
01411         for(request = asp->requests; request; request = request->next) {
01412             /*
01413              * check session
01414              */
01415             netsnmp_assert(NULL!=request->subtree);
01416             if(request->subtree->session != sess)
01417                 continue;
01418 
01419             /*
01420              * matched! mark request as done
01421              */
01422             netsnmp_request_set_error(request, SNMP_ERR_GENERR);
01423             ++count;
01424         }
01425     }
01426 
01427     /*
01428      * if we found any, that request may be finished now
01429      */
01430     if(count) {
01431         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
01432                     "%08p\n", count, sess));
01433         netsnmp_check_outstanding_agent_requests();
01434     }
01435     
01436     return count;
01437 }
01438 
01439 int
01440 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
01441 {
01442     netsnmp_agent_session *asptmp;
01443     for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
01444         if (asptmp == asp)
01445             return 1;
01446     }
01447     return 0;
01448 }
01449 
01450 int
01451 netsnmp_add_queued(netsnmp_agent_session *asp)
01452 {
01453     netsnmp_agent_session *asp_tmp;
01454 
01455     /*
01456      * first item?
01457      */
01458     if (NULL == netsnmp_agent_queued_list) {
01459         netsnmp_agent_queued_list = asp;
01460         return 1;
01461     }
01462 
01463 
01464     /*
01465      * add to end of queued request chain 
01466      */
01467     asp_tmp = netsnmp_agent_queued_list;
01468     for (; asp_tmp; asp_tmp = asp_tmp->next) {
01469         /*
01470          * already in queue?
01471          */
01472         if (asp_tmp == asp)
01473             break;
01474 
01475         /*
01476          * end of queue?
01477          */
01478         if (NULL == asp_tmp->next)
01479             asp_tmp->next = asp;
01480     }
01481     return 1;
01482 }
01483 
01484 
01485 int
01486 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
01487 {
01488     netsnmp_variable_list *var_ptr;
01489     int             i;
01490 
01491     /*
01492      * if this request was a set, clear the global now that we are
01493      * done.
01494      */
01495     if (asp == netsnmp_processing_set) {
01496         DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %08p\n",
01497                     asp));
01498         netsnmp_processing_set = NULL;
01499     }
01500 
01501     if (asp->pdu) {
01502         /*
01503          * If we've got an error status, then this needs to be
01504          *  passed back up to the higher levels....
01505          */
01506         if ( status != 0  && asp->status == 0 )
01507             asp->status = status;
01508 
01509         switch (asp->pdu->command) {
01510             case SNMP_MSG_INTERNAL_SET_BEGIN:
01511             case SNMP_MSG_INTERNAL_SET_RESERVE1:
01512             case SNMP_MSG_INTERNAL_SET_RESERVE2:
01513             case SNMP_MSG_INTERNAL_SET_ACTION:
01514                 /*
01515                  * some stuff needs to be saved in special subagent cases 
01516                  */
01517                 save_set_cache(asp);
01518                 break;
01519 
01520             case SNMP_MSG_GETBULK:
01521                 /*
01522                  * for a GETBULK response we need to rearrange the varbinds 
01523                  */
01524                 _reorder_getbulk(asp);
01525                 break;
01526         }
01527 
01528         /*
01529          * May need to "dumb down" a SET error status for a
01530          * v1 query.  See RFC2576 - section 4.3
01531          */
01532 #ifndef DISABLE_SNMPV1
01533         if ((asp->pdu->command == SNMP_MSG_SET) &&
01534             (asp->pdu->version == SNMP_VERSION_1)) {
01535             switch (asp->status) {
01536                 case SNMP_ERR_WRONGVALUE:
01537                 case SNMP_ERR_WRONGENCODING:
01538                 case SNMP_ERR_WRONGTYPE:
01539                 case SNMP_ERR_WRONGLENGTH:
01540                 case SNMP_ERR_INCONSISTENTVALUE:
01541                     status = SNMP_ERR_BADVALUE;
01542                     asp->status = SNMP_ERR_BADVALUE;
01543                     break;
01544                 case SNMP_ERR_NOACCESS:
01545                 case SNMP_ERR_NOTWRITABLE:
01546                 case SNMP_ERR_NOCREATION:
01547                 case SNMP_ERR_INCONSISTENTNAME:
01548                 case SNMP_ERR_AUTHORIZATIONERROR:
01549                     status = SNMP_ERR_NOSUCHNAME;
01550                     asp->status = SNMP_ERR_NOSUCHNAME;
01551                     break;
01552                 case SNMP_ERR_RESOURCEUNAVAILABLE:
01553                 case SNMP_ERR_COMMITFAILED:
01554                 case SNMP_ERR_UNDOFAILED:
01555                     status = SNMP_ERR_GENERR;
01556                     asp->status = SNMP_ERR_GENERR;
01557                     break;
01558             }
01559         }
01560         /*
01561          * Similarly we may need to "dumb down" v2 exception
01562          *  types to throw an error for a v1 query.
01563          *  See RFC2576 - section 4.1.2.3
01564          */
01565         if ((asp->pdu->command != SNMP_MSG_SET) &&
01566             (asp->pdu->version == SNMP_VERSION_1)) {
01567             for (var_ptr = asp->pdu->variables, i = 1;
01568                  var_ptr != NULL; var_ptr = var_ptr->next_variable, i++) {
01569                 switch (var_ptr->type) {
01570                     case SNMP_NOSUCHOBJECT:
01571                     case SNMP_NOSUCHINSTANCE:
01572                     case SNMP_ENDOFMIBVIEW:
01573                     case ASN_COUNTER64:
01574                         status = SNMP_ERR_NOSUCHNAME;
01575                         asp->status = SNMP_ERR_NOSUCHNAME;
01576                         asp->index = i;
01577                         break;
01578                 }
01579             }
01580         }
01581 #endif /* snmpv1 support */
01582     } 
01584     /*
01585      * Update the snmp error-count statistics
01586      *   XXX - should we include the V2 errors in this or not?
01587      */
01588 #define INCLUDE_V2ERRORS_IN_V1STATS
01589 
01590     switch (status) {
01591 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01592     case SNMP_ERR_WRONGVALUE:
01593     case SNMP_ERR_WRONGENCODING:
01594     case SNMP_ERR_WRONGTYPE:
01595     case SNMP_ERR_WRONGLENGTH:
01596     case SNMP_ERR_INCONSISTENTVALUE:
01597 #endif
01598     case SNMP_ERR_BADVALUE:
01599         snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
01600         break;
01601 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01602     case SNMP_ERR_NOACCESS:
01603     case SNMP_ERR_NOTWRITABLE:
01604     case SNMP_ERR_NOCREATION:
01605     case SNMP_ERR_INCONSISTENTNAME:
01606     case SNMP_ERR_AUTHORIZATIONERROR:
01607 #endif
01608     case SNMP_ERR_NOSUCHNAME:
01609         snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
01610         break;
01611 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
01612     case SNMP_ERR_RESOURCEUNAVAILABLE:
01613     case SNMP_ERR_COMMITFAILED:
01614     case SNMP_ERR_UNDOFAILED:
01615 #endif
01616     case SNMP_ERR_GENERR:
01617         snmp_increment_statistic(STAT_SNMPOUTGENERRS);
01618         break;
01619 
01620     case SNMP_ERR_TOOBIG:
01621         snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
01622         break;
01623     }
01624 
01625     if ((status == SNMP_ERR_NOERROR) && (asp->pdu)) {
01626         snmp_increment_statistic_by((asp->pdu->command == SNMP_MSG_SET ?
01627                                      STAT_SNMPINTOTALSETVARS :
01628                                      STAT_SNMPINTOTALREQVARS),
01629                                     count_varbinds(asp->pdu->variables));
01630     } else {
01631         /*
01632          * Use a copy of the original request
01633          *   to report failures.
01634          */
01635         snmp_free_pdu(asp->pdu);
01636         asp->pdu = asp->orig_pdu;
01637         asp->orig_pdu = NULL;
01638     }
01639     if (asp->pdu) {
01640         asp->pdu->command = SNMP_MSG_RESPONSE;
01641         asp->pdu->errstat = asp->status;
01642         asp->pdu->errindex = asp->index;
01643         if (!snmp_send(asp->session, asp->pdu)) {
01644             netsnmp_variable_list *var_ptr;
01645             snmp_perror("send response");
01646             for (var_ptr = asp->pdu->variables; var_ptr != NULL;
01647                      var_ptr = var_ptr->next_variable) {
01648                 size_t  c_oidlen = 256, c_outlen = 0;
01649                 u_char *c_oid = (u_char *) malloc(c_oidlen);
01650 
01651                 if (c_oid) {
01652                     if (!sprint_realloc_objid (&c_oid, &c_oidlen, &c_outlen, 1,
01653                                                var_ptr->name,
01654                                                var_ptr->name_length)) {
01655                         snmp_log(LOG_ERR, "    -- %s [TRUNCATED]\n", c_oid);
01656                     } else {
01657                         snmp_log(LOG_ERR, "    -- %s\n", c_oid);
01658                     }
01659                     SNMP_FREE(c_oid);
01660                 }
01661             }
01662             snmp_free_pdu(asp->pdu);
01663             asp->pdu = NULL;
01664         }
01665         snmp_increment_statistic(STAT_SNMPOUTPKTS);
01666         snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
01667         asp->pdu = NULL; /* yyy-rks: redundant, no? */
01668         netsnmp_remove_and_free_agent_snmp_session(asp);
01669     }
01670     return 1;
01671 }
01672 
01673 void
01674 dump_sess_list(void)
01675 {
01676     netsnmp_agent_session *a;
01677 
01678     DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
01679     for (a = agent_session_list; a != NULL; a = a->next) {
01680         DEBUGMSG(("snmp_agent", "%08p[session %08p] -> ", a, a->session));
01681     }
01682     DEBUGMSG(("snmp_agent", "[NIL]\n"));
01683 }
01684 
01685 void
01686 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
01687 {
01688     netsnmp_agent_session *a, **prevNext = &agent_session_list;
01689 
01690     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", asp));
01691 
01692     for (a = agent_session_list; a != NULL; a = *prevNext) {
01693         if (a == asp) {
01694             *prevNext = a->next;
01695             a->next = NULL;
01696             free_agent_snmp_session(a);
01697             asp = NULL;
01698             break;
01699         } else {
01700             prevNext = &(a->next);
01701         }
01702     }
01703 
01704     if (a == NULL && asp != NULL) {
01705         /*
01706          * We coulnd't find it on the list, so free it anyway.  
01707          */
01708         free_agent_snmp_session(asp);
01709     }
01710 }
01711 
01712 void
01713 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
01714                                            void (*free_request)
01715                                            (netsnmp_request_list *))
01716 {
01717     netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
01718 
01719     DEBUGMSGTL(("snmp_agent", "REMOVE session == %08p\n", sess));
01720 
01721     for (a = agent_session_list; a != NULL; a = next) {
01722         if (a->session == sess) {
01723             *prevNext = a->next;
01724             next = a->next;
01725             free_agent_snmp_session(a);
01726         } else {
01727             prevNext = &(a->next);
01728             next = a->next;
01729         }
01730     }
01731 }
01732 
01734 int
01735 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
01736                    netsnmp_pdu *pdu, void *magic)
01737 {
01738     netsnmp_agent_session *asp;
01739     int             status, access_ret, rc;
01740 
01741     /*
01742      * We only support receiving here.  
01743      */
01744     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
01745         return 1;
01746     }
01747 
01748     /*
01749      * RESPONSE messages won't get this far, but TRAP-like messages
01750      * might.  
01751      */
01752     if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
01753         pdu->command == SNMP_MSG_TRAP2) {
01754         DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
01755                     pdu->command));
01756         pdu->command = SNMP_MSG_TRAP2;
01757         snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
01758         return 1;
01759     }
01760 
01761     /*
01762      * send snmpv3 authfail trap.
01763      */
01764     if (pdu->version  == SNMP_VERSION_3 && 
01765         session->s_snmp_errno == SNMPERR_USM_AUTHENTICATIONFAILURE) {
01766            send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
01767            return 1;
01768     } 
01769         
01770     if (magic == NULL) {
01771         asp = init_agent_snmp_session(session, pdu);
01772         status = SNMP_ERR_NOERROR;
01773     } else {
01774         asp = (netsnmp_agent_session *) magic;
01775         status = asp->status;
01776     }
01777 
01778     if ((access_ret = check_access(asp->pdu)) != 0) {
01779         if (access_ret == VACM_NOSUCHCONTEXT) {
01780             /*
01781              * rfc3413 section 3.2, step 5 says that we increment the
01782              * counter but don't return a response of any kind 
01783              */
01784 
01785             /*
01786              * we currently don't support unavailable contexts, as
01787              * there is no reason to that I currently know of 
01788              */
01789             snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
01790 
01791             /*
01792              * drop the request 
01793              */
01794             netsnmp_remove_and_free_agent_snmp_session(asp);
01795             return 0;
01796         } else {
01797             /*
01798              * access control setup is incorrect 
01799              */
01800             send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
01801 #if !defined(DISABLE_SNMPV1) || !defined(DISABLE_SNMPV2C)
01802 #if defined(DISABLE_SNMPV1)
01803             if (asp->pdu->version != SNMP_VERSION_2c) {
01804 #else
01805 #if defined(DISABLE_SNMPV2C)
01806             if (asp->pdu->version != SNMP_VERSION_1) {
01807 #else
01808             if (asp->pdu->version != SNMP_VERSION_1
01809                 && asp->pdu->version != SNMP_VERSION_2c) {
01810 #endif
01811 #endif
01812                 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
01813                 asp->pdu->command = SNMP_MSG_RESPONSE;
01814                 snmp_increment_statistic(STAT_SNMPOUTPKTS);
01815                 if (!snmp_send(asp->session, asp->pdu))
01816                     snmp_free_pdu(asp->pdu);
01817                 asp->pdu = NULL;
01818                 netsnmp_remove_and_free_agent_snmp_session(asp);
01819                 return 1;
01820             } else {
01821 #endif /* support for community based SNMP */
01822                 /*
01823                  * drop the request 
01824                  */
01825                 netsnmp_remove_and_free_agent_snmp_session(asp);
01826                 return 0;
01827 #if !defined(DISABLE_SNMPV1) || !defined(DISABLE_SNMPV2C)
01828             }
01829 #endif /* support for community based SNMP */
01830         }
01831     }
01832 
01833     rc = netsnmp_handle_request(asp, status);
01834 
01835     /*
01836      * done 
01837      */
01838     DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %08p\n",
01839                 asp));
01840     return rc;
01841 }
01842 
01843 netsnmp_request_info *
01844 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
01845                              netsnmp_variable_list * varbind_ptr,
01846                              netsnmp_subtree *tp)
01847 {
01848     netsnmp_request_info *request = NULL;
01849     int             cacheid;
01850 
01851     DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
01852     DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
01853                  varbind_ptr->name_length));
01854     DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
01855 
01856     if (tp &&
01857         (asp->pdu->command == SNMP_MSG_GETNEXT ||
01858          asp->pdu->command == SNMP_MSG_GETBULK)) {
01859         int result;
01860         int prefix_len;
01861 
01862         prefix_len = netsnmp_oid_find_prefix(tp->start_a,
01863                                              tp->start_len,
01864                                              tp->end_a, tp->end_len);
01865         result =
01866             netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
01867 
01868         while (result == VACM_NOTINVIEW) {
01869             /* the entire subtree is not in view. Skip it. */
01877             tp = tp->next;
01878             if (tp) {
01879                 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
01880                                                      tp->start_len,
01881                                                      tp->end_a,
01882                                                      tp->end_len);
01883                 result =
01884                     netsnmp_acm_check_subtree(asp->pdu,
01885                                               tp->start_a, prefix_len);
01886             }
01887             else
01888                 break;
01889         }
01890     }
01891     if (tp == NULL) {
01892         /*
01893          * no appropriate registration found 
01894          */
01895         /*
01896          * make up the response ourselves 
01897          */
01898         switch (asp->pdu->command) {
01899         case SNMP_MSG_GETNEXT:
01900         case SNMP_MSG_GETBULK:
01901             varbind_ptr->type = SNMP_ENDOFMIBVIEW;
01902             break;
01903 
01904         case SNMP_MSG_SET:
01905         case SNMP_MSG_GET:
01906             varbind_ptr->type = SNMP_NOSUCHOBJECT;
01907             break;
01908 
01909         default:
01910             return NULL;        /* shouldn't get here */
01911         }
01912     } else {
01913         DEBUGMSGTL(("snmp_agent", "tp->start "));
01914         DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
01915         DEBUGMSG(("snmp_agent", ", tp->end "));
01916         DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
01917         DEBUGMSG(("snmp_agent", ", \n"));
01918 
01919         /*
01920          * malloc the request structure 
01921          */
01922         request = &(asp->requests[vbcount - 1]);
01923         request->index = vbcount;
01924         request->delegated = 0;
01925         request->processed = 0;
01926         request->status = 0;
01927         request->subtree = tp;
01928         request->agent_req_info = asp->reqinfo;
01929         if (request->parent_data) {
01930             netsnmp_free_request_data_sets(request);
01931         }
01932         DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
01933                     asp, asp->reqinfo));
01934 
01935         /*
01936          * for non-SET modes, set the type to NULL 
01937          */
01938         if (!MODE_IS_SET(asp->pdu->command)) {
01939         DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
01940                     asp, asp->reqinfo));
01941             if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
01942                 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
01943                             request->index));
01944                 request->inclusive = 1;
01945             }
01946             varbind_ptr->type = ASN_NULL;
01947         }
01948 
01949         /*
01950          * place them in a cache 
01951          */
01952         if (tp->global_cacheid) {
01953             /*
01954              * we need to merge all marked subtrees together 
01955              */
01956             if (asp->cache_store && -1 !=
01957                 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
01958                                                     tp->global_cacheid))) {
01959             } else {
01960                 cacheid = ++(asp->treecache_num);
01961                 netsnmp_get_or_add_local_cachid(&asp->cache_store,
01962                                                 tp->global_cacheid,
01963                                                 cacheid);
01964                 goto mallocslot;        /* XXX: ick */
01965             }
01966         } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
01967                    asp->treecache[tp->cacheid].subtree == tp) {
01968             /*
01969              * we have already added a request to this tree
01970              * pointer before 
01971              */
01972             cacheid = tp->cacheid;
01973         } else {
01974             cacheid = ++(asp->treecache_num);
01975           mallocslot:
01976             /*
01977              * new slot needed 
01978              */
01979             if (asp->treecache_num >= asp->treecache_len) {
01980                 /*
01981                  * exapand cache array 
01982                  */
01983                 /*
01984                  * WWW: non-linear expansion needed (with cap) 
01985                  */
01986 #define CACHE_GROW_SIZE 16
01987                 asp->treecache_len =
01988                     (asp->treecache_len + CACHE_GROW_SIZE);
01989                 asp->treecache =
01990                     realloc(asp->treecache,
01991                             sizeof(netsnmp_tree_cache) *
01992                             asp->treecache_len);
01993                 if (asp->treecache == NULL)
01994                     return NULL;
01995                 memset(&(asp->treecache[cacheid]), 0x00,
01996                        sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
01997             }
01998             asp->treecache[cacheid].subtree = tp;
01999             asp->treecache[cacheid].requests_begin = request;
02000             tp->cacheid = cacheid;
02001         }
02002 
02003         /*
02004          * if this is a search type, get the ending range oid as well 
02005          */
02006         if (asp->pdu->command == SNMP_MSG_GETNEXT ||
02007             asp->pdu->command == SNMP_MSG_GETBULK) {
02008             request->range_end = tp->end_a;
02009             request->range_end_len = tp->end_len;
02010         } else {
02011             request->range_end = NULL;
02012             request->range_end_len = 0;
02013         }
02014 
02015         /*
02016          * link into chain 
02017          */
02018         if (asp->treecache[cacheid].requests_end)
02019             asp->treecache[cacheid].requests_end->next = request;
02020         request->