1 /*
2  * snmp_agent.c
3  *
4  * Simple Network Management Protocol (RFC 1067).
5  */
6 /* Portions of this file are subject to the following copyrights.  See
7  * the Net-SNMP's COPYING file for more details and other copyrights
8  * that may apply:
9  */
10 /***********************************************************
11 	Copyright 1988, 1989 by Carnegie Mellon University
12 
13                       All Rights Reserved
14 
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of CMU not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22 
23 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 SOFTWARE.
30 ******************************************************************/
31 /*
32  * Portions of this file are copyrighted by:
33  * Copyright � 2003 Sun Microsystems, Inc. All rights
34  * reserved.  Use is subject to license terms specified in the
35  * COPYING file distributed with the Net-SNMP package.
36  *
37  * Portions of this file are copyrighted by:
38  * Copyright (c) 2016 VMware, Inc. All rights reserved.
39  * Use is subject to license terms specified in the COPYING file
40  * distributed with the Net-SNMP package.
41  */
42 /** @defgroup snmp_agent net-snmp agent related processing
43  *  @ingroup agent
44  *
45  * @{
46  */
47 #include <net-snmp/net-snmp-config.h>
48 #include <net-snmp/net-snmp-features.h>
49 
50 #include <sys/types.h>
51 #ifdef HAVE_LIMITS_H
52 #include <limits.h>
53 #endif
54 #ifdef HAVE_STDLIB_H
55 #include <stdlib.h>
56 #endif
57 #if HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #if HAVE_STRING_H
61 #include <string.h>
62 #endif
63 #if TIME_WITH_SYS_TIME
64 # include <sys/time.h>
65 # include <time.h>
66 #else
67 # if HAVE_SYS_TIME_H
68 #  include <sys/time.h>
69 # else
70 #  include <time.h>
71 # endif
72 #endif
73 #if HAVE_SYS_SELECT_H
74 #include <sys/select.h>
75 #endif
76 #if HAVE_NETINET_IN_H
77 #include <netinet/in.h>
78 #endif
79 #include <errno.h>
80 
81 #define SNMP_NEED_REQUEST_LIST
82 #include <net-snmp/net-snmp-includes.h>
83 #include <net-snmp/agent/net-snmp-agent-includes.h>
84 #include <net-snmp/library/large_fd_set.h>
85 #include <net-snmp/library/snmp_assert.h>
86 #include "agent_global_vars.h"
87 
88 #if HAVE_SYSLOG_H
89 #include <syslog.h>
90 #endif
91 
92 #ifdef NETSNMP_USE_LIBWRAP
93 #include <tcpd.h>
94 int             allow_severity = LOG_INFO;
95 int             deny_severity = LOG_WARNING;
96 #endif
97 
98 #include "snmpd.h"
99 #include <net-snmp/agent/mib_module_config.h>
100 #include <net-snmp/agent/mib_modules.h>
101 
102 #ifdef USING_AGENTX_PROTOCOL_MODULE
103 #include "agentx/protocol.h"
104 #endif
105 
106 #ifdef USING_AGENTX_MASTER_MODULE
107 #include "agentx/master.h"
108 #endif
109 
110 #ifdef USING_SMUX_MODULE
111 #include "smux/smux.h"
112 #endif
113 
114 netsnmp_feature_child_of(snmp_agent, libnetsnmpagent);
115 netsnmp_feature_child_of(agent_debugging_utilities, libnetsnmpagent);
116 
117 netsnmp_feature_child_of(allocate_globalcacheid, snmp_agent);
118 netsnmp_feature_child_of(free_agent_snmp_session_by_session, snmp_agent);
119 netsnmp_feature_child_of(check_all_requests_error, snmp_agent);
120 netsnmp_feature_child_of(check_requests_error, snmp_agent);
121 netsnmp_feature_child_of(request_set_error_idx, snmp_agent);
122 netsnmp_feature_child_of(set_agent_uptime, snmp_agent);
123 netsnmp_feature_child_of(agent_check_and_process, snmp_agent);
124 
125 netsnmp_feature_child_of(dump_sess_list, agent_debugging_utilities);
126 
127 netsnmp_feature_child_of(agent_remove_list_data, netsnmp_unused);
128 netsnmp_feature_child_of(set_all_requests_error, netsnmp_unused);
129 netsnmp_feature_child_of(addrcache_age, netsnmp_unused);
130 netsnmp_feature_child_of(delete_subtree_cache, netsnmp_unused);
131 
132 #ifndef NETSNMP_NO_PDU_STATS
133 
134 static netsnmp_container *_pdu_stats = NULL;
135 static int _pdu_stats_max = 0;
136 static u_long _pdu_stats_threshold = 0;
137 static u_long _pdu_stats_current_lowest = 0;
138 
139 netsnmp_container *
netsnmp_get_pdu_stats(void)140 netsnmp_get_pdu_stats(void)
141 {
142     return _pdu_stats;
143 }
144 
_pdu_stats_compare(const netsnmp_pdu_stats * lhs,const netsnmp_pdu_stats * rhs)145 int _pdu_stats_compare(const netsnmp_pdu_stats * lhs,
146                        const netsnmp_pdu_stats * rhs)
147 {
148     if (NULL == lhs || NULL == rhs) {
149         snmp_log(LOG_WARNING,
150                  "WARNING: results undefined for compares with NULL\n");
151         return 0;
152     }
153 
154     /** we want list sorted in reverse order, so invert tests */
155     if (lhs->processing_time > rhs->processing_time)
156         return -1;
157     else if (lhs->processing_time < rhs->processing_time)
158         return 1;
159 
160     if (lhs->timestamp > rhs->timestamp)
161         return -1;
162     else if (lhs->timestamp < rhs->timestamp)
163         return 1;
164 
165     return 0;
166 }
167 
168 
169 static void
_pdu_stats_init(void)170 _pdu_stats_init(void) {
171     static int done = 0;
172     if ((NULL != _pdu_stats) || (++done != 1))
173         return;
174 
175     _pdu_stats = netsnmp_container_find("netsnmp_pdustats:binary_array");
176     if (NULL == _pdu_stats) {
177         done = 0;
178         return;
179     }
180 
181     _pdu_stats->compare = (netsnmp_container_compare*)_pdu_stats_compare;
182     _pdu_stats->get_subset = NULL; /** subsets not supported */
183 
184     _pdu_stats_max = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
185                                         NETSNMP_DS_AGENT_PDU_STATS_MAX);
186     _pdu_stats_threshold =
187         netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
188                            NETSNMP_DS_AGENT_PDU_STATS_THRESHOLD);
189     if (_pdu_stats_threshold < 100)
190         _pdu_stats_threshold = 100;
191     DEBUGMSGTL(("stats:pdu", "max: %d, threshold %ld ms\n", _pdu_stats_max,
192                 _pdu_stats_threshold));
193 }
194 
195 
196 static void
_pdu_stats_shutdown(void)197 _pdu_stats_shutdown(void)
198 {
199     netsnmp_pdu_stats *entry;
200     int x = 0;
201 
202     if (NULL == _pdu_stats)
203         return;
204 
205     for( ; x < CONTAINER_SIZE(_pdu_stats); ++x) {
206         CONTAINER_GET_AT(_pdu_stats, x, (void**)&entry);
207         if (NULL == entry)
208             continue;
209         snmp_free_pdu(entry->pdu);
210         free(entry);
211     }
212     CONTAINER_FREE(_pdu_stats);
213     _pdu_stats = NULL;
214 }
215 
216 
217 static void
_dump_pdu_stats(void)218 _dump_pdu_stats(void)
219 {
220     int x = 0;
221     struct tm *tm;
222     char timestr[40];
223     netsnmp_pdu_stats *entry;
224 
225     for( ; x < CONTAINER_SIZE(_pdu_stats); ++x) {
226         netsnmp_pdu    *response;
227         netsnmp_variable_list *vars;
228         CONTAINER_GET_AT(_pdu_stats, x, (void**)&entry);
229         if (NULL == entry) {
230             DEBUGMSGT_NC(("9:stats:pdu", "[%d] ERROR\n", x));
231             continue;
232         }
233         tm = localtime(&entry->timestamp);
234         if (NULL == tm)
235             sprintf(timestr, "UNKNOWN");
236         else if (strftime(timestr, sizeof(timestr), "%m/%d/%Y %H:%M:%S",
237                           tm) == 0)
238             sprintf(timestr, "UNKNOWN");
239 
240         DEBUGMSGT_NC(("9:stats:pdu", "[%d] %ld ms, %s\n",
241                       x, entry->processing_time, timestr));
242         response = entry->pdu;
243         if (response->errstat == SNMP_ERR_NOERROR) {
244             for (vars = response->variables; vars;
245                  vars = vars->next_variable) {
246                 DEBUGMSGT_NC(("9:stats:pdu", "    vb "));
247                 DEBUGMSGVAR(("9:stats:pdu", vars));
248                 DEBUGMSG_NC(("9:stats:pdu", "\n"));
249             }
250         } else {
251             DEBUGMSGT_NC(("9:stats:pdu", "Error in packet: Reason: %s\n",
252                           snmp_errstring(response->errstat)));
253             if (response->errindex != 0) {
254                 int count;
255                 DEBUGMSGT_NC(("9:stats:pdu", "Failed object: "));
256                 for (count = 1, vars = response->variables;
257                      vars && count != response->errindex;
258                      vars = vars->next_variable, count++)
259                     /*EMPTY*/;
260                 if (vars) {
261                     DEBUGMSGOID(("9:stats:pdu", vars->name,
262                                  vars->name_length));
263                 }
264                 DEBUGMSG_NC(("9:stats:pdu", "\n"));
265             }
266         }
267     }
268 }
269 #endif /* NETSNMP_NO_PDU_STATS */
270 
271 
272 NETSNMP_INLINE void
netsnmp_agent_add_list_data(netsnmp_agent_request_info * ari,netsnmp_data_list * node)273 netsnmp_agent_add_list_data(netsnmp_agent_request_info *ari,
274                             netsnmp_data_list *node)
275 {
276     if (ari) {
277 	if (ari->agent_data) {
278             netsnmp_add_list_data(&ari->agent_data, node);
279         } else {
280             ari->agent_data = node;
281 	}
282     }
283 }
284 
285 #ifndef NETSNMP_FEATURE_REMOVE_AGENT_REMOVE_LIST_DATA
286 NETSNMP_INLINE int
netsnmp_agent_remove_list_data(netsnmp_agent_request_info * ari,const char * name)287 netsnmp_agent_remove_list_data(netsnmp_agent_request_info *ari,
288                                const char * name)
289 {
290     if ((NULL == ari) || (NULL == ari->agent_data))
291         return 1;
292 
293     return netsnmp_remove_list_node(&ari->agent_data, name);
294 }
295 #endif /* NETSNMP_FEATURE_REMOVE_AGENT_REMOVE_LIST_DATA */
296 
297 NETSNMP_INLINE void    *
netsnmp_agent_get_list_data(netsnmp_agent_request_info * ari,const char * name)298 netsnmp_agent_get_list_data(netsnmp_agent_request_info *ari,
299                             const char *name)
300 {
301     if (ari) {
302         return netsnmp_get_list_data(ari->agent_data, name);
303     }
304     return NULL;
305 }
306 
307 NETSNMP_INLINE void
netsnmp_free_agent_data_set(netsnmp_agent_request_info * ari)308 netsnmp_free_agent_data_set(netsnmp_agent_request_info *ari)
309 {
310     if (ari) {
311         netsnmp_free_list_data(ari->agent_data);
312     }
313 }
314 
315 NETSNMP_INLINE void
netsnmp_free_agent_data_sets(netsnmp_agent_request_info * ari)316 netsnmp_free_agent_data_sets(netsnmp_agent_request_info *ari)
317 {
318     if (ari) {
319         netsnmp_free_all_list_data(ari->agent_data);
320     }
321 }
322 
323 NETSNMP_INLINE void
netsnmp_free_agent_request_info(netsnmp_agent_request_info * ari)324 netsnmp_free_agent_request_info(netsnmp_agent_request_info *ari)
325 {
326     if (ari) {
327         if (ari->agent_data) {
328             netsnmp_free_all_list_data(ari->agent_data);
329 	}
330         SNMP_FREE(ari);
331     }
332 }
333 
334 const oid version_sysoid[] = { NETSNMP_SYSTEM_MIB };
335 const int version_sysoid_len = OID_LENGTH(version_sysoid);
336 
337 #define SNMP_ADDRCACHE_SIZE 10
338 #define SNMP_ADDRCACHE_MAXAGE 300 /* in seconds */
339 
340 enum {
341     SNMP_ADDRCACHE_UNUSED = 0,
342     SNMP_ADDRCACHE_USED = 1
343 };
344 
345 struct addrCache {
346     char           *addr;
347     int            status;
348     struct timeval lastHitM;
349 };
350 
351 static struct addrCache addrCache[SNMP_ADDRCACHE_SIZE];
352 int             log_addresses = 0;
353 
354 
355 
356 typedef struct _agent_nsap {
357     int             handle;
358     netsnmp_transport *t;
359     void           *s;          /*  Opaque internal session pointer.  */
360     struct _agent_nsap *next;
361 } agent_nsap;
362 
363 static agent_nsap *agent_nsap_list = NULL;
364 static netsnmp_agent_session *agent_session_list = NULL;
365 netsnmp_agent_session *netsnmp_processing_set = NULL;
366 netsnmp_agent_session *agent_delegated_list = NULL;
367 netsnmp_agent_session *netsnmp_agent_queued_list = NULL;
368 
369 
370 int             handle_pdu(netsnmp_agent_session *asp);
371 int             netsnmp_handle_request(netsnmp_agent_session *asp,
372                                        int status);
373 int             check_delayed_request(netsnmp_agent_session *asp);
374 int             handle_getnext_loop(netsnmp_agent_session *asp);
375 int             handle_set_loop(netsnmp_agent_session *asp);
376 
377 int             netsnmp_remove_from_delegated(netsnmp_agent_session *asp);
378 
379 
380 int      netsnmp_running = 1;
381 
382 #ifndef NETSNMP_FEATURE_REMOVE_ALLOCATE_GLOBALCACHEID
383 static int      current_globalid = 0;
384 
385 int
netsnmp_allocate_globalcacheid(void)386 netsnmp_allocate_globalcacheid(void)
387 {
388     return ++current_globalid;
389 }
390 #endif /* NETSNMP_FEATURE_REMOVE_ALLOCATE_GLOBALCACHEID */
391 
392 int
netsnmp_get_local_cachid(netsnmp_cachemap * cache_store,int globalid)393 netsnmp_get_local_cachid(netsnmp_cachemap *cache_store, int globalid)
394 {
395     while (cache_store != NULL) {
396         if (cache_store->globalid == globalid)
397             return cache_store->cacheid;
398         cache_store = cache_store->next;
399     }
400     return -1;
401 }
402 
403 netsnmp_cachemap *
netsnmp_get_or_add_local_cachid(netsnmp_cachemap ** cache_store,int globalid,int localid)404 netsnmp_get_or_add_local_cachid(netsnmp_cachemap **cache_store,
405                                 int globalid, int localid)
406 {
407     netsnmp_cachemap *tmpp;
408 
409     tmpp = SNMP_MALLOC_TYPEDEF(netsnmp_cachemap);
410     if (tmpp != NULL) {
411         if (*cache_store) {
412             tmpp->next = *cache_store;
413             *cache_store = tmpp;
414         } else {
415             *cache_store = tmpp;
416         }
417 
418         tmpp->globalid = globalid;
419         tmpp->cacheid = localid;
420     }
421     return tmpp;
422 }
423 
424 void
netsnmp_free_cachemap(netsnmp_cachemap * cache_store)425 netsnmp_free_cachemap(netsnmp_cachemap *cache_store)
426 {
427     netsnmp_cachemap *tmpp;
428     while (cache_store) {
429         tmpp = cache_store;
430         cache_store = cache_store->next;
431         SNMP_FREE(tmpp);
432     }
433 }
434 
435 
436 typedef struct agent_set_cache_s {
437     /*
438      * match on these 2
439      */
440     int             transID;
441     netsnmp_session *sess;
442 
443     /*
444      * store this info
445      */
446     netsnmp_tree_cache *treecache;
447     int             treecache_len;
448     int             treecache_num;
449 
450     int             vbcount;
451     netsnmp_request_info *requests;
452     netsnmp_variable_list *saved_vars;
453     netsnmp_data_list *agent_data;
454 
455     /*
456      * list
457      */
458     struct agent_set_cache_s *next;
459 } agent_set_cache;
460 
461 static agent_set_cache *Sets = NULL;
462 
463 agent_set_cache *
save_set_cache(netsnmp_agent_session * asp)464 save_set_cache(netsnmp_agent_session *asp)
465 {
466     agent_set_cache *ptr;
467 
468     if (!asp || !asp->reqinfo || !asp->pdu)
469         return NULL;
470 
471     ptr = SNMP_MALLOC_TYPEDEF(agent_set_cache);
472     if (ptr == NULL)
473         return NULL;
474 
475     /*
476      * Save the important information
477      */
478     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p saved in cache (mode %d)\n",
479                 asp, asp->reqinfo, asp->pdu->command));
480     ptr->transID = asp->pdu->transid;
481     ptr->sess = asp->session;
482     ptr->treecache = asp->treecache;
483     ptr->treecache_len = asp->treecache_len;
484     ptr->treecache_num = asp->treecache_num;
485     ptr->agent_data = asp->reqinfo->agent_data;
486     ptr->requests = asp->requests;
487     ptr->saved_vars = asp->pdu->variables; /* requests contains pointers to variables */
488     ptr->vbcount = asp->vbcount;
489 
490     /*
491      * make the agent forget about what we've saved
492      */
493     asp->treecache = NULL;
494     asp->reqinfo->agent_data = NULL;
495     asp->pdu->variables = NULL;
496     asp->requests = NULL;
497 
498     ptr->next = Sets;
499     Sets = ptr;
500 
501     return ptr;
502 }
503 
504 int
get_set_cache(netsnmp_agent_session * asp)505 get_set_cache(netsnmp_agent_session *asp)
506 {
507     agent_set_cache *ptr, *prev = NULL;
508 
509     for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
510         if (ptr->sess == asp->session && ptr->transID == asp->pdu->transid) {
511             /*
512              * remove this item from list
513              */
514             if (prev)
515                 prev->next = ptr->next;
516             else
517                 Sets = ptr->next;
518 
519             /*
520              * found it.  Get the needed data
521              */
522             asp->treecache = ptr->treecache;
523             asp->treecache_len = ptr->treecache_len;
524             asp->treecache_num = ptr->treecache_num;
525 
526             /*
527              * Free previously allocated requests before overwriting by
528              * cached ones, otherwise memory leaks!
529              */
530 	    if (asp->requests) {
531                 /*
532                  * I don't think this case should ever happen. Please email
533                  * the net-snmp-coders@lists.sourceforge.net if you have
534                  * a test case that hits this condition. -- rstory
535                  */
536 		int i;
537                 netsnmp_assert(NULL == asp->requests); /* see note above */
538 		for (i = 0; i < asp->vbcount; i++) {
539 		    netsnmp_free_request_data_sets(&asp->requests[i]);
540 		}
541 		free(asp->requests);
542 	    }
543 	    /*
544 	     * If we replace asp->requests with the info from the set cache,
545 	     * we should replace asp->pdu->variables also with the cached
546 	     * info, as asp->requests contains pointers to them.  And we
547 	     * should also free the current asp->pdu->variables list...
548 	     */
549 	    if (ptr->saved_vars) {
550 		if (asp->pdu->variables)
551 		    snmp_free_varbind(asp->pdu->variables);
552 		asp->pdu->variables = ptr->saved_vars;
553                 asp->vbcount = ptr->vbcount;
554 	    } else {
555                 /*
556                  * when would we not have saved variables? someone
557                  * let me know if they hit this condition. -- rstory
558                  */
559                 netsnmp_assert(NULL != ptr->saved_vars);
560             }
561             asp->requests = ptr->requests;
562 
563             netsnmp_assert(NULL != asp->reqinfo);
564             asp->reqinfo->asp = asp;
565             asp->reqinfo->agent_data = ptr->agent_data;
566 
567             /*
568              * update request reqinfo, if it's out of date.
569              * yyy-rks: investigate when/why sometimes they match,
570              * sometimes they don't.
571              */
572             if(asp->requests->agent_req_info != asp->reqinfo) {
573                 /*
574                  * - one don't match case: agentx subagents. prev asp & reqinfo
575                  *   freed, request reqinfo ptrs not cleared.
576                  */
577                 netsnmp_request_info *tmp = asp->requests;
578                 DEBUGMSGTL(("verbose:asp",
579                             "  reqinfo %p doesn't match cached reqinfo %p\n",
580                             asp->reqinfo, asp->requests->agent_req_info));
581                 for(; tmp; tmp = tmp->next)
582                     tmp->agent_req_info = asp->reqinfo;
583             } else {
584                 /*
585                  * - match case: ?
586                  */
587                 DEBUGMSGTL(("verbose:asp",
588                             "  reqinfo %p matches cached reqinfo %p\n",
589                             asp->reqinfo, asp->requests->agent_req_info));
590             }
591 
592             SNMP_FREE(ptr);
593             return SNMP_ERR_NOERROR;
594         }
595         prev = ptr;
596     }
597     return SNMP_ERR_GENERR;
598 }
599 
600 /* Bulkcache holds the values for the *repeating* varbinds (only),
601  *   but ordered "by column" - i.e. the repetitions for each
602  *   repeating varbind follow on immediately from one another,
603  *   rather than being interleaved, as required by the protocol.
604  *
605  * So we need to rearrange the varbind list so it's ordered "by row".
606  *
607  * In the following code chunk:
608  *     n            = # non-repeating varbinds
609  *     r            = # repeating varbinds
610  *     asp->vbcount = # varbinds in the incoming PDU
611  *         (So asp->vbcount = n+r)
612  *
613  *     repeats = Desired # of repetitions (of 'r' varbinds)
614  */
615 NETSNMP_STATIC_INLINE void
_reorder_getbulk(netsnmp_agent_session * asp)616 _reorder_getbulk(netsnmp_agent_session *asp)
617 {
618     int             i, n = 0, r = 0;
619     int             repeats;
620     int             j, k;
621     int             all_eoMib;
622     netsnmp_variable_list *prev = NULL, *curr;
623 
624     if (NULL == asp || NULL == asp->pdu || asp->vbcount == 0)
625         return;
626 
627     if (asp->pdu->errstat < asp->vbcount) {
628         n = asp->pdu->errstat;
629     } else {
630         n = asp->vbcount;
631     }
632     if ((r = asp->vbcount - n) < 0) {
633         r = 0;
634     }
635 
636     DEBUGMSGTL(("snmp_agent:bulk", "reorder n=%d, r=%d\n", n, r));
637 
638     /* we do nothing if there is nothing repeated */
639     if (r == 0)
640         return;
641 
642     repeats = asp->pdu->errindex;
643     /* Fix endOfMibView entries. */
644     for (i = 0; i < r; i++) {
645         prev = NULL;
646         for (j = 0; j < repeats; j++) {
647 	    curr = asp->bulkcache[i * repeats + j];
648             /*
649              *  If we don't have a valid name for a given repetition
650              *   (and probably for all the ones that follow as well),
651              *   extend the previous result to indicate 'endOfMibView'.
652              *  Or if the repetition already has type endOfMibView make
653              *   sure it has the correct objid (i.e. that of the previous
654              *   entry or that of the original request).
655              */
656             if (curr->name_length == 0 || curr->type == SNMP_ENDOFMIBVIEW) {
657 		if (prev == NULL) {
658 		    /* Use objid from original pdu. */
659 		    prev = asp->orig_pdu->variables;
660 		    for (k = i; prev && k > 0; k--)
661 			prev = prev->next_variable;
662 		}
663 		if (prev) {
664 		    snmp_set_var_objid(curr, prev->name, prev->name_length);
665 		    snmp_set_var_typed_value(curr, SNMP_ENDOFMIBVIEW, NULL, 0);
666 		}
667             }
668             prev = curr;
669         }
670     }
671 
672     /*
673      * For each of the original repeating varbinds (except the last),
674      *  go through the block of results for that varbind,
675      *  and link each instance to the corresponding instance
676      *  in the next block.
677      */
678     for (i = 0; i < r - 1; i++) {
679         for (j = 0; j < repeats; j++) {
680             asp->bulkcache[i * repeats + j]->next_variable =
681                 asp->bulkcache[(i + 1) * repeats + j];
682         }
683     }
684 
685     /*
686      * For the last of the original repeating varbinds,
687      *  go through that block of results, and link each
688      *  instance to the *next* instance in the *first* block.
689      *
690      * The very last instance of this block is left untouched
691      *  since it (correctly) points to the end of the list.
692      */
693     for (j = 0; j < repeats - 1; j++) {
694 	asp->bulkcache[(r - 1) * repeats + j]->next_variable =
695 	    asp->bulkcache[j + 1];
696     }
697 
698     /*
699      * If we've got a full row of endOfMibViews, then we
700      *  can truncate the result varbind list after that.
701      *
702      * Look for endOfMibView exception values in the list of
703      *  repetitions for the first varbind, and check the
704      *  corresponding instances for the other varbinds
705      *  (following the next_variable links).
706      *
707      * If they're all endOfMibView too, then we can terminate
708      *  the linked list there, and free any redundant varbinds.
709      */
710     all_eoMib = 0;
711     for (i = 0; i < repeats; i++) {
712         if (asp->bulkcache[i]->type == SNMP_ENDOFMIBVIEW) {
713             all_eoMib = 1;
714             for (j = 1, prev=asp->bulkcache[i];
715                  j < r;
716                  j++, prev=prev->next_variable) {
717                 if (prev->type != SNMP_ENDOFMIBVIEW) {
718                     all_eoMib = 0;
719                     break;	/* Found a real value */
720                 }
721             }
722             if (all_eoMib) {
723                 /*
724                  * This is indeed a full endOfMibView row.
725                  * Terminate the list here & free the rest.
726                  */
727                 snmp_free_varbind( prev->next_variable );
728                 prev->next_variable = NULL;
729                 break;
730             }
731         }
732     }
733 }
734 
735 
736 /* EndOfMibView replies to a GETNEXT request should according to RFC3416
737  *  have the object ID set to that of the request. Our tree search
738  *  algorithm will sometimes break that requirement. This function will
739  *  fix that.
740  */
741 NETSNMP_STATIC_INLINE void
_fix_endofmibview(netsnmp_agent_session * asp)742 _fix_endofmibview(netsnmp_agent_session *asp)
743 {
744     netsnmp_variable_list *vb, *ovb;
745 
746     if (asp->vbcount == 0)  /* Nothing to do! */
747         return;
748 
749     for (vb = asp->pdu->variables, ovb = asp->orig_pdu->variables;
750 	 vb && ovb; vb = vb->next_variable, ovb = ovb->next_variable) {
751 	if (vb->type == SNMP_ENDOFMIBVIEW)
752 	    snmp_set_var_objid(vb, ovb->name, ovb->name_length);
753     }
754 }
755 
756 #ifndef NETSNMP_FEATURE_REMOVE_AGENT_CHECK_AND_PROCESS
757 /**
758  * This function checks for packets arriving on the SNMP port and
759  * processes them(snmp_read) if some are found, using the select(). If block
760  * is non zero, the function call blocks until a packet arrives
761  *
762  * @param block used to control blocking in the select() function, 1 = block
763  *        forever, and 0 = don't block
764  *
765  * @return  Returns a positive integer if packets were processed, and -1 if an
766  * error was found.
767  *
768  */
769 int
agent_check_and_process(int block)770 agent_check_and_process(int block)
771 {
772     int                  numfds;
773     netsnmp_large_fd_set readfds;
774     netsnmp_large_fd_set writefds;
775     netsnmp_large_fd_set exceptfds;
776     struct timeval       timeout = { LONG_MAX, 0 }, *tvp = &timeout;
777     int                  count;
778     int                  fakeblock = 0;
779 
780     numfds = 0;
781     netsnmp_large_fd_set_init(&readfds, FD_SETSIZE);
782     netsnmp_large_fd_set_init(&writefds, FD_SETSIZE);
783     netsnmp_large_fd_set_init(&exceptfds, FD_SETSIZE);
784     NETSNMP_LARGE_FD_ZERO(&readfds);
785     NETSNMP_LARGE_FD_ZERO(&writefds);
786     NETSNMP_LARGE_FD_ZERO(&exceptfds);
787     snmp_select_info2(&numfds, &readfds, tvp, &fakeblock);
788     if (block != 0 && fakeblock != 0) {
789         /*
790          * There are no alarms registered, and the caller asked for blocking, so
791          * let select() block forever.
792          */
793 
794         tvp = NULL;
795     } else if (block != 0 && fakeblock == 0) {
796         /*
797          * The caller asked for blocking, but there is an alarm due sooner than
798          * LONG_MAX seconds from now, so use the modified timeout returned by
799          * snmp_select_info as the timeout for select().
800          */
801 
802     } else if (block == 0) {
803         /*
804          * The caller does not want us to block at all.
805          */
806 
807         timerclear(tvp);
808     }
809 
810 #ifndef NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER
811     netsnmp_external_event_info2(&numfds, &readfds, &writefds, &exceptfds);
812 #endif /* NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER */
813 
814     count = netsnmp_large_fd_set_select(numfds, &readfds, &writefds, &exceptfds, tvp);
815 
816     if (count > 0) {
817         /*
818          * packets found, process them
819          */
820 #ifndef NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER
821         netsnmp_dispatch_external_events2(&count, &readfds, &writefds, &exceptfds);
822 #endif /* NETSNMP_FEATURE_REMOVE_FD_EVENT_MANAGER */
823 
824         snmp_read2(&readfds);
825     } else
826         switch (count) {
827         case 0:
828             snmp_timeout();
829             break;
830         case -1:
831             if (errno != EINTR) {
832                 snmp_log_perror("select");
833             }
834             count = -1;
835             goto exit;
836         default:
837             snmp_log(LOG_ERR, "select returned %d\n", count);
838             count = -1;
839             goto exit;
840         }                       /* endif -- count>0 */
841 
842     /*
843      * see if persistent store needs to be saved
844      */
845     snmp_store_if_needed();
846 
847     /*
848      * Run requested alarms.
849      */
850     run_alarms();
851 
852     netsnmp_check_outstanding_agent_requests();
853 
854  exit:
855     netsnmp_large_fd_set_cleanup(&readfds);
856     netsnmp_large_fd_set_cleanup(&writefds);
857     netsnmp_large_fd_set_cleanup(&exceptfds);
858     return count;
859 }
860 #endif /* NETSNMP_FEATURE_REMOVE_AGENT_CHECK_AND_PROCESS */
861 
862 /*
863  * Set up the address cache.
864  */
865 void
netsnmp_addrcache_initialise(void)866 netsnmp_addrcache_initialise(void)
867 {
868     int             i = 0;
869 
870     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
871         addrCache[i].addr = NULL;
872         addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
873     }
874 }
875 
netsnmp_addrcache_destroy(void)876 void netsnmp_addrcache_destroy(void)
877 {
878     int             i = 0;
879 
880     for (i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
881         if (addrCache[i].status == SNMP_ADDRCACHE_USED) {
882             free(addrCache[i].addr);
883             addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
884         }
885     }
886 }
887 
888 /*
889  * Adds a new entry to the cache of addresses that
890  * have recently made connections to the agent.
891  * Returns 0 if the entry already exists (but updates
892  * the entry with a new timestamp) and 1 if the
893  * entry did not previously exist.
894  *
895  * Implements a simple LRU cache replacement
896  * policy. Uses a linear search, which should be
897  * okay, as long as SNMP_ADDRCACHE_SIZE remains
898  * relatively small.
899  *
900  * @retval 0 : updated existing entry
901  * @retval 1 : added new entry
902  */
903 int
netsnmp_addrcache_add(const char * addr)904 netsnmp_addrcache_add(const char *addr)
905 {
906     int oldest = -1; /* Index of the oldest cache entry */
907     int unused = -1; /* Index of the first free cache entry */
908     int i; /* Looping variable */
909     int rc = -1;
910     struct timeval now; /* What time is it now? */
911     struct timeval aged; /* Oldest allowable cache entry */
912 
913     /*
914      * First get the current and oldest allowable timestamps
915      */
916     netsnmp_get_monotonic_clock(&now);
917     aged.tv_sec = now.tv_sec - SNMP_ADDRCACHE_MAXAGE;
918     aged.tv_usec = now.tv_usec;
919 
920     /*
921      * Now look for a place to put this thing
922      */
923     for(i = 0; i < SNMP_ADDRCACHE_SIZE; i++) {
924         if (addrCache[i].status == SNMP_ADDRCACHE_UNUSED) { /* If unused */
925             /*
926              * remember this location, in case addr isn't in the cache
927              */
928             if (unused < 0)
929                 unused = i;
930         }
931         else { /* If used */
932             if ((NULL != addr) && (strcmp(addrCache[i].addr, addr) == 0)) {
933                 /*
934                  * found a match
935                  */
936                 addrCache[i].lastHitM = now;
937                 if (timercmp(&addrCache[i].lastHitM, &aged, <))
938 		    rc = 1; /* should have expired, so is new */
939 		else
940 		    rc = 0; /* not expired, so is existing entry */
941                 break;
942             }
943             else {
944                 /*
945                  * Used, but not this address. check if it's stale.
946                  */
947                 if (timercmp(&addrCache[i].lastHitM, &aged, <)) {
948                     /*
949                      * Stale, reuse
950                      */
951                     SNMP_FREE(addrCache[i].addr);
952                     addrCache[i].status = SNMP_ADDRCACHE_UNUSED;
953                     /*
954                      * remember this location, in case addr isn't in the cache
955                      */
956 		    if (unused < 0)
957                         unused = i;
958                 }
959 	        else {
960                     /*
961                      * Still fresh, but a candidate for LRU replacement
962                      */
963                     if (oldest < 0)
964                         oldest = i;
965                     else if (timercmp(&addrCache[i].lastHitM,
966                                       &addrCache[oldest].lastHitM, <))
967                         oldest = i;
968                 } /* fresh */
969             } /* used, no match */
970         } /* used */
971     } /* for loop */
972 
973     if ((-1 == rc) && (NULL != addr)) {
974         /*
975          * We didn't find the entry in the cache
976          */
977         if (unused >= 0) {
978             /*
979              * If we have a slot free anyway, use it
980              */
981             addrCache[unused].addr = strdup(addr);
982             addrCache[unused].status = SNMP_ADDRCACHE_USED;
983             addrCache[unused].lastHitM = now;
984         }
985         else { /* Otherwise, replace oldest entry */
986             if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
987                                        NETSNMP_DS_AGENT_VERBOSE))
988                 snmp_log(LOG_INFO, "Purging address from address cache: %s",
989                          addrCache[oldest].addr);
990 
991             free(addrCache[oldest].addr);
992             addrCache[oldest].addr = strdup(addr);
993             addrCache[oldest].lastHitM = now;
994         }
995         rc = 1;
996     }
997     if ((log_addresses && (1 == rc)) ||
998         netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
999                                NETSNMP_DS_AGENT_VERBOSE)) {
1000         snmp_log(LOG_INFO, "Received SNMP packet(s) from %s\n", addr);
1001      }
1002 
1003     return rc;
1004 }
1005 
1006 /*
1007  * Age the entries in the address cache.
1008  *
1009  * backwards compatability; not used anywhere
1010  */
1011 #ifndef NETSNMP_FEATURE_REMOVE_ADDRCACHE_AGE
1012 void
netsnmp_addrcache_age(void)1013 netsnmp_addrcache_age(void)
1014 {
1015     (void)netsnmp_addrcache_add(NULL);
1016 }
1017 #endif /* NETSNMP_FEATURE_REMOVE_ADDRCACHE_AGE */
1018 
1019 /*******************************************************************-o-******
1020  * netsnmp_agent_check_packet
1021  *
1022  * Parameters:
1023  *	session, transport, transport_data, transport_data_length
1024  *
1025  * Returns:
1026  *	1	On success.
1027  *	0	On error.
1028  *
1029  * Handler for all incoming messages (a.k.a. packets) for the agent.  If using
1030  * the libwrap utility, log the connection and deny/allow the access. Print
1031  * output when appropriate, and increment the incoming counter.
1032  *
1033  */
1034 
1035 int
netsnmp_agent_check_packet(netsnmp_session * session,netsnmp_transport * transport,void * transport_data,int transport_data_length)1036 netsnmp_agent_check_packet(netsnmp_session * session,
1037                            netsnmp_transport *transport,
1038                            void *transport_data, int transport_data_length)
1039 {
1040     char           *addr_string = NULL;
1041 #ifdef  NETSNMP_USE_LIBWRAP
1042     char *tcpudpaddr = NULL, *name;
1043     short not_log_connection;
1044 
1045     name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1046                                  NETSNMP_DS_LIB_APPTYPE);
1047 
1048     /* not_log_connection will be 1 if we should skip the messages */
1049     not_log_connection = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1050                                                 NETSNMP_DS_AGENT_DONT_LOG_TCPWRAPPERS_CONNECTS);
1051 
1052     /*
1053      * handle the error case
1054      * default to logging the messages
1055      */
1056     if (not_log_connection == SNMPERR_GENERR) not_log_connection = 0;
1057 #endif
1058 
1059     /*
1060      * Log the message and/or dump the message.
1061      * Optionally cache the network address of the sender.
1062      */
1063 
1064     if (transport != NULL && transport->f_fmtaddr != NULL) {
1065         /*
1066          * Okay I do know how to format this address for logging.
1067          */
1068         addr_string = transport->f_fmtaddr(transport, transport_data,
1069                                            transport_data_length);
1070         /*
1071          * Don't forget to free() it.
1072          */
1073     }
1074 #ifdef  NETSNMP_USE_LIBWRAP
1075     /* Catch udp,udp6,tcp,tcp6 transports using "[" */
1076     if (addr_string)
1077         tcpudpaddr = strstr(addr_string, "[");
1078     if ( tcpudpaddr != 0 ) {
1079         char sbuf[64];
1080         char *xp;
1081 
1082         strlcpy(sbuf, tcpudpaddr + 1, sizeof(sbuf));
1083         xp = strstr(sbuf, "]");
1084         if (xp)
1085             *xp = '\0';
1086 
1087         if (hosts_ctl(name, STRING_UNKNOWN, sbuf, STRING_UNKNOWN)) {
1088             if (!not_log_connection) {
1089                 snmp_log(allow_severity, "Connection from %s\n", addr_string);
1090             }
1091         } else {
1092             snmp_log(deny_severity, "Connection from %s REFUSED\n",
1093                      addr_string);
1094             SNMP_FREE(addr_string);
1095             return 0;
1096         }
1097     } else {
1098         /*
1099          * don't log callback connections.
1100          * What about 'Local IPC', 'IPX' and 'AAL5 PVC'?
1101          */
1102         if (0 == strncmp(addr_string, "callback", 8))
1103             ;
1104         else if (hosts_ctl(name, STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
1105             if (!not_log_connection) {
1106                 snmp_log(allow_severity, "Connection from <UNKNOWN> (%s)\n", addr_string);
1107             };
1108             SNMP_FREE(addr_string);
1109             addr_string = strdup("<UNKNOWN>");
1110         } else {
1111             snmp_log(deny_severity, "Connection from <UNKNOWN> (%s) REFUSED\n", addr_string);
1112             SNMP_FREE(addr_string);
1113             return 0;
1114         }
1115     }
1116 #endif                          /*NETSNMP_USE_LIBWRAP */
1117 
1118     snmp_increment_statistic(STAT_SNMPINPKTS);
1119 
1120     if (addr_string != NULL) {
1121         netsnmp_addrcache_add(addr_string);
1122         SNMP_FREE(addr_string);
1123     }
1124     return 1;
1125 }
1126 
1127 
1128 int
netsnmp_agent_check_parse(netsnmp_session * session,netsnmp_pdu * pdu,int result)1129 netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
1130                           int result)
1131 {
1132     if (result == 0) {
1133         if (snmp_get_do_logging() &&
1134 	    netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1135 				   NETSNMP_DS_AGENT_VERBOSE)) {
1136             netsnmp_variable_list *var_ptr;
1137 
1138             switch (pdu->command) {
1139             case SNMP_MSG_GET:
1140                 snmp_log(LOG_DEBUG, "  GET message\n");
1141                 break;
1142             case SNMP_MSG_GETNEXT:
1143                 snmp_log(LOG_DEBUG, "  GETNEXT message\n");
1144                 break;
1145             case SNMP_MSG_RESPONSE:
1146                 snmp_log(LOG_DEBUG, "  RESPONSE message\n");
1147                 break;
1148 #ifndef NETSNMP_NO_WRITE_SUPPORT
1149             case SNMP_MSG_SET:
1150                 snmp_log(LOG_DEBUG, "  SET message\n");
1151                 break;
1152 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1153             case SNMP_MSG_TRAP:
1154                 snmp_log(LOG_DEBUG, "  TRAP message\n");
1155                 break;
1156             case SNMP_MSG_GETBULK:
1157                 snmp_log(LOG_DEBUG, "  GETBULK message, non-rep=%ld, max_rep=%ld\n",
1158                          pdu->errstat, pdu->errindex);
1159                 break;
1160             case SNMP_MSG_INFORM:
1161                 snmp_log(LOG_DEBUG, "  INFORM message\n");
1162                 break;
1163             case SNMP_MSG_TRAP2:
1164                 snmp_log(LOG_DEBUG, "  TRAP2 message\n");
1165                 break;
1166             case SNMP_MSG_REPORT:
1167                 snmp_log(LOG_DEBUG, "  REPORT message\n");
1168                 break;
1169 
1170 #ifndef NETSNMP_NO_WRITE_SUPPORT
1171             case SNMP_MSG_INTERNAL_SET_RESERVE1:
1172                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE1 message\n");
1173                 break;
1174 
1175             case SNMP_MSG_INTERNAL_SET_RESERVE2:
1176                 snmp_log(LOG_DEBUG, "  INTERNAL RESERVE2 message\n");
1177                 break;
1178 
1179             case SNMP_MSG_INTERNAL_SET_ACTION:
1180                 snmp_log(LOG_DEBUG, "  INTERNAL ACTION message\n");
1181                 break;
1182 
1183             case SNMP_MSG_INTERNAL_SET_COMMIT:
1184                 snmp_log(LOG_DEBUG, "  INTERNAL COMMIT message\n");
1185                 break;
1186 
1187             case SNMP_MSG_INTERNAL_SET_FREE:
1188                 snmp_log(LOG_DEBUG, "  INTERNAL FREE message\n");
1189                 break;
1190 
1191             case SNMP_MSG_INTERNAL_SET_UNDO:
1192                 snmp_log(LOG_DEBUG, "  INTERNAL UNDO message\n");
1193                 break;
1194 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1195 
1196             default:
1197                 snmp_log(LOG_DEBUG, "  UNKNOWN message, type=%02X\n",
1198                          pdu->command);
1199                 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
1200                 return 0;
1201             }
1202 
1203             for (var_ptr = pdu->variables; var_ptr != NULL;
1204                  var_ptr = var_ptr->next_variable) {
1205                 size_t          c_oidlen = 256, c_outlen = 0;
1206                 u_char         *c_oid = (u_char *) malloc(c_oidlen);
1207 
1208                 if (c_oid) {
1209                     if (!sprint_realloc_objid
1210                         (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
1211                          var_ptr->name_length)) {
1212                         snmp_log(LOG_DEBUG, "    -- %s [TRUNCATED]\n",
1213                                  c_oid);
1214                     } else {
1215                         snmp_log(LOG_DEBUG, "    -- %s\n", c_oid);
1216                     }
1217                     SNMP_FREE(c_oid);
1218                 }
1219             }
1220         }
1221         return 1;
1222     }
1223     return 0;                   /* XXX: does it matter what the return value
1224                                  * is?  Yes: if we return 0, then the PDU is
1225                                  * dumped.  */
1226 }
1227 
1228 
1229 /*
1230  * Global access to the primary session structure for this agent.
1231  * for Index Allocation use initially.
1232  */
1233 
1234 /*
1235  * I don't understand what this is for at the moment.  AFAICS as long as it
1236  * gets set and points at a session, that's fine.  ???
1237  */
1238 
1239 netsnmp_session *main_session = NULL;
1240 
1241 
1242 
1243 /*
1244  * Set up an agent session on the given transport.  Return a handle
1245  * which may later be used to de-register this transport.  A return
1246  * value of -1 indicates an error.
1247  */
1248 
1249 int
netsnmp_register_agent_nsap(netsnmp_transport * t)1250 netsnmp_register_agent_nsap(netsnmp_transport *t)
1251 {
1252     netsnmp_session *s, *sp = NULL;
1253     agent_nsap     *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
1254     int             handle = 0;
1255     void           *isp = NULL;
1256 
1257     if (t == NULL) {
1258         return -1;
1259     }
1260 
1261     DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
1262 
1263     n = (agent_nsap *) malloc(sizeof(agent_nsap));
1264     if (n == NULL) {
1265         return -1;
1266     }
1267     s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
1268     if (s == NULL) {
1269         SNMP_FREE(n);
1270         return -1;
1271     }
1272     snmp_sess_init(s);
1273 
1274     /*
1275      * Set up the session appropriately for an agent.
1276      */
1277 
1278     s->version = SNMP_DEFAULT_VERSION;
1279     s->callback = handle_snmp_packet;
1280     s->authenticator = NULL;
1281     s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
1282 				  NETSNMP_DS_AGENT_FLAGS);
1283     s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
1284 
1285     /* Optional supplimental transport configuration information and
1286        final call to actually open the transport */
1287     if (netsnmp_sess_config_transport(s->transport_configuration, t)
1288         != SNMPERR_SUCCESS) {
1289         SNMP_FREE(s);
1290         SNMP_FREE(n);
1291         return -1;
1292     }
1293 
1294 
1295     if (t->f_open)
1296         t = t->f_open(t);
1297 
1298     if (NULL == t) {
1299         SNMP_FREE(s);
1300         SNMP_FREE(n);
1301         return -1;
1302     }
1303 
1304     t->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
1305 
1306     sp = snmp_add(s, t, netsnmp_agent_check_packet,
1307                   netsnmp_agent_check_parse);
1308     if (sp == NULL) {
1309         SNMP_FREE(s);
1310         SNMP_FREE(n);
1311         return -1;
1312     }
1313 
1314     isp = snmp_sess_pointer(sp);
1315     if (isp == NULL) {          /*  over-cautious  */
1316         SNMP_FREE(s);
1317         SNMP_FREE(n);
1318         return -1;
1319     }
1320 
1321     n->s = isp;
1322     n->t = t;
1323 
1324     if (main_session == NULL) {
1325         main_session = snmp_sess_session(isp);
1326     }
1327 
1328     for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
1329          a = a->next) {
1330         handle = a->handle;
1331         prevNext = &(a->next);
1332     }
1333 
1334     if (handle < INT_MAX) {
1335         n->handle = handle + 1;
1336         n->next = a;
1337         *prevNext = n;
1338         SNMP_FREE(s);
1339         DEBUGMSGTL(("netsnmp_register_agent_nsap", "handle %d\n", n->handle));
1340         return n->handle;
1341     } else {
1342         SNMP_FREE(s);
1343         SNMP_FREE(n);
1344         return -1;
1345     }
1346 }
1347 
1348 void
netsnmp_deregister_agent_nsap(int handle)1349 netsnmp_deregister_agent_nsap(int handle)
1350 {
1351     agent_nsap     *a = NULL, **prevNext = &agent_nsap_list;
1352     int             main_session_deregistered = 0;
1353 
1354     DEBUGMSGTL(("netsnmp_deregister_agent_nsap", "handle %d\n", handle));
1355 
1356     for (a = agent_nsap_list; a != NULL && a->handle < handle; a = a->next) {
1357         prevNext = &(a->next);
1358     }
1359 
1360     if (a != NULL && a->handle == handle) {
1361         *prevNext = a->next;
1362 	if (snmp_sess_session_lookup(a->s)) {
1363             if (main_session == snmp_sess_session(a->s)) {
1364                 main_session_deregistered = 1;
1365             }
1366             snmp_close(snmp_sess_session(a->s));
1367             /*
1368              * The above free()s the transport and session pointers.
1369              */
1370         }
1371         SNMP_FREE(a);
1372     }
1373 
1374     /*
1375      * If we've deregistered the session that main_session used to point to,
1376      * then make it point to another one, or in the last resort, make it equal
1377      * to NULL.  Basically this shouldn't ever happen in normal operation
1378      * because main_session starts off pointing at the first session added by
1379      * init_master_agent(), which then discards the handle.
1380      */
1381 
1382     if (main_session_deregistered) {
1383         if (agent_nsap_list != NULL) {
1384             DEBUGMSGTL(("snmp_agent",
1385 			"WARNING: main_session ptr changed from %p to %p\n",
1386                         main_session, snmp_sess_session(agent_nsap_list->s)));
1387             main_session = snmp_sess_session(agent_nsap_list->s);
1388         } else {
1389             DEBUGMSGTL(("snmp_agent",
1390 			"WARNING: main_session ptr changed from %p to NULL\n",
1391                         main_session));
1392             main_session = NULL;
1393         }
1394     }
1395 }
1396 
1397 int
netsnmp_agent_listen_on(const char * port)1398 netsnmp_agent_listen_on(const char *port)
1399 {
1400     netsnmp_transport *transport;
1401     int                handle;
1402 
1403     if (NULL == port)
1404         return -1;
1405 
1406     transport = netsnmp_transport_open_server("snmp", port);
1407     if (transport == NULL) {
1408         snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n", port);
1409         return -1;
1410     }
1411 
1412     handle = netsnmp_register_agent_nsap(transport);
1413     if (handle < 0) {
1414         snmp_log(LOG_ERR, "Error registering specified transport \"%s\" as an "
1415                  "agent NSAP\n", port);
1416         return -1;
1417     } else {
1418         DEBUGMSGTL(("snmp_agent",
1419                     "init_master_agent; \"%s\" registered as an agent NSAP\n",
1420                     port));
1421     }
1422 
1423     return handle;
1424 }
1425 
1426 /*
1427  *
1428  * This function has been modified to use the experimental
1429  * netsnmp_register_agent_nsap interface.  The major responsibility of this
1430  * function now is to interpret a string specified to the agent (via -p on the
1431  * command line, or from a configuration file) as a list of agent NSAPs on
1432  * which to listen for SNMP packets.  Typically, when you add a new transport
1433  * domain "foo", you add code here such that if the "foo" code is compiled
1434  * into the agent (SNMP_TRANSPORT_FOO_DOMAIN is defined), then a token of the
1435  * form "foo:bletch-3a0054ef%wob&wob" gets turned into the appropriate
1436  * transport descriptor.  netsnmp_register_agent_nsap is then called with that
1437  * transport descriptor and sets up a listening agent session on it.
1438  *
1439  * Everything then works much as normal: the agent runs in an infinite loop
1440  * (in the snmpd.c/receive()routine), which calls snmp_read() when a request
1441  * is readable on any of the given transports.  This routine then traverses
1442  * the library 'Sessions' list to identify the relevant session and eventually
1443  * invokes '_sess_read'.  This then processes the incoming packet, calling the
1444  * pre_parse, parse, post_parse and callback routines in turn.
1445  *
1446  * JBPN 20001117
1447  */
1448 
1449 int
init_master_agent(void)1450 init_master_agent(void)
1451 {
1452     char           *cptr;
1453     char           *buf = NULL;
1454     char           *st;
1455 
1456     /* default to a default cache size */
1457     netsnmp_set_lookup_cache_size(-1);
1458 
1459     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1460 			       NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
1461         DEBUGMSGTL(("snmp_agent",
1462                     "init_master_agent; not master agent\n"));
1463 
1464         netsnmp_assert("agent role !master && !sub_agent");
1465 
1466         return 0;               /*  No error if ! MASTER_AGENT  */
1467     }
1468 
1469 #ifndef NETSNMP_NO_LISTEN_SUPPORT
1470     /*
1471      * Have specific agent ports been specified?
1472      */
1473     cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
1474 				 NETSNMP_DS_AGENT_PORTS);
1475 
1476     if (cptr) {
1477         buf = strdup(cptr);
1478         if (!buf) {
1479             snmp_log(LOG_ERR,
1480                      "Error processing transport \"%s\"\n", cptr);
1481             return 1;
1482         }
1483     } else {
1484         /*
1485          * No, so just specify the default port.
1486          */
1487         buf = strdup("");
1488     }
1489 
1490     DEBUGMSGTL(("snmp_agent", "final port spec: \"%s\"\n", buf));
1491     st = buf;
1492     do {
1493         /*
1494          * Specification format:
1495          *
1496          * NONE:                      (a pseudo-transport)
1497          * UDP:[address:]port        (also default if no transport is specified)
1498          * TCP:[address:]port         (if supported)
1499          * Unix:pathname              (if supported)
1500          * AAL5PVC:itf.vpi.vci        (if supported)
1501          * IPX:[network]:node[/port] (if supported)
1502          *
1503          */
1504 
1505 	cptr = st;
1506 	st = strchr(st, ',');
1507 	if (st)
1508 	    *st++ = '\0';
1509 
1510         DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
1511                     cptr));
1512 
1513         if (strncasecmp(cptr, "none", 4) == 0) {
1514             DEBUGMSGTL(("snmp_agent",
1515                         "init_master_agent; pseudo-transport \"none\" "
1516 			"requested\n"));
1517             break;
1518         }
1519         if (-1 == netsnmp_agent_listen_on(cptr)) {
1520             SNMP_FREE(buf);
1521             return 1;
1522         }
1523     } while(st && *st != '\0');
1524     SNMP_FREE(buf);
1525 #endif /* NETSNMP_NO_LISTEN_SUPPORT */
1526 
1527 #ifdef USING_AGENTX_MASTER_MODULE
1528     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
1529 			       NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
1530         real_init_master();
1531 #endif
1532 #ifdef USING_SMUX_MODULE
1533     if(should_init("smux"))
1534     real_init_smux();
1535 #endif
1536 
1537 #ifndef NETSNMP_NO_PDU_STATS
1538     _pdu_stats_init();
1539 #endif /* NETSNMP_NO_PDU_STATS */
1540 
1541     return 0;
1542 }
1543 
1544 void
clear_nsap_list(void)1545 clear_nsap_list(void)
1546 {
1547     DEBUGMSGTL(("clear_nsap_list", "clear the nsap list\n"));
1548 
1549     while (agent_nsap_list != NULL)
1550 	netsnmp_deregister_agent_nsap(agent_nsap_list->handle);
1551 }
1552 
1553 void
shutdown_master_agent(void)1554 shutdown_master_agent(void)
1555 {
1556     clear_nsap_list();
1557 
1558 #ifndef NETSNMP_NO_PDU_STATS
1559     _pdu_stats_shutdown();
1560 #endif /* NETSNMP_NO_PDU_STATS */
1561 }
1562 
1563 
1564 netsnmp_agent_session *
init_agent_snmp_session(netsnmp_session * session,netsnmp_pdu * pdu)1565 init_agent_snmp_session(netsnmp_session * session, netsnmp_pdu *pdu)
1566 {
1567     netsnmp_agent_session *asp = (netsnmp_agent_session *)
1568         calloc(1, sizeof(netsnmp_agent_session));
1569 
1570     if (asp == NULL) {
1571         return NULL;
1572     }
1573 
1574     DEBUGMSGTL(("snmp_agent","agent_sesion %8p created\n", asp));
1575     asp->session = session;
1576     asp->pdu = snmp_clone_pdu(pdu);
1577     if (!asp->pdu)
1578         goto err;
1579     asp->orig_pdu = snmp_clone_pdu(pdu);
1580     if (!asp->orig_pdu)
1581         goto err;
1582     asp->rw = READ;
1583     asp->exact = TRUE;
1584     asp->next = NULL;
1585     asp->mode = RESERVE1;
1586     asp->status = SNMP_ERR_NOERROR;
1587     asp->index = 0;
1588     asp->oldmode = 0;
1589     asp->treecache_num = -1;
1590     asp->treecache_len = 0;
1591     asp->reqinfo = SNMP_MALLOC_TYPEDEF(netsnmp_agent_request_info);
1592     asp->flags = SNMP_AGENT_FLAGS_NONE;
1593     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p created\n",
1594                 asp, asp->reqinfo));
1595 
1596     return asp;
1597 
1598 err:
1599     snmp_free_pdu(asp->orig_pdu);
1600     snmp_free_pdu(asp->pdu);
1601     free(asp);
1602     return NULL;
1603 }
1604 
1605 void
free_agent_snmp_session(netsnmp_agent_session * asp)1606 free_agent_snmp_session(netsnmp_agent_session *asp)
1607 {
1608     if (!asp)
1609         return;
1610 
1611     DEBUGMSGTL(("snmp_agent","agent_session %8p released\n", asp));
1612 
1613     netsnmp_remove_from_delegated(asp);
1614 
1615     DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p freed\n",
1616                 asp, asp->reqinfo));
1617 
1618     if (asp->orig_pdu)
1619         snmp_free_pdu(asp->orig_pdu);
1620     if (asp->pdu)
1621         snmp_free_pdu(asp->pdu);
1622     if (asp->reqinfo)
1623         netsnmp_free_agent_request_info(asp->reqinfo);
1624     SNMP_FREE(asp->treecache);
1625     SNMP_FREE(asp->bulkcache);
1626     if (asp->requests) {
1627         int             i;
1628         for (i = 0; i < asp->vbcount; i++) {
1629             netsnmp_free_request_data_sets(&asp->requests[i]);
1630         }
1631         SNMP_FREE(asp->requests);
1632     }
1633     if (asp->cache_store) {
1634         netsnmp_free_cachemap(asp->cache_store);
1635         asp->cache_store = NULL;
1636     }
1637     SNMP_FREE(asp);
1638 }
1639 
1640 int
netsnmp_check_for_delegated(netsnmp_agent_session * asp)1641 netsnmp_check_for_delegated(netsnmp_agent_session *asp)
1642 {
1643     int             i;
1644     netsnmp_request_info *request;
1645 
1646     if (NULL == asp->treecache)
1647         return 0;
1648 
1649     if (asp->flags & SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS)
1650         return 0;
1651 
1652     for (i = 0; i <= asp->treecache_num; i++) {
1653         for (request = asp->treecache[i].requests_begin; request;
1654              request = request->next) {
1655             if (request->delegated)
1656                 return 1;
1657         }
1658     }
1659     return 0;
1660 }
1661 
1662 int
netsnmp_check_delegated_chain_for(netsnmp_agent_session * asp)1663 netsnmp_check_delegated_chain_for(netsnmp_agent_session *asp)
1664 {
1665     netsnmp_agent_session *asptmp;
1666     for (asptmp = agent_delegated_list; asptmp; asptmp = asptmp->next) {
1667         if (asptmp == asp)
1668             return 1;
1669     }
1670     return 0;
1671 }
1672 
1673 int
netsnmp_check_for_delegated_and_add(netsnmp_agent_session * asp)1674 netsnmp_check_for_delegated_and_add(netsnmp_agent_session *asp)
1675 {
1676     if (netsnmp_check_for_delegated(asp)) {
1677         if (!netsnmp_check_delegated_chain_for(asp)) {
1678             /*
1679              * add to delegated request chain
1680              */
1681             asp->next = agent_delegated_list;
1682             agent_delegated_list = asp;
1683             DEBUGMSGTL(("snmp_agent", "delegate session == %8p\n", asp));
1684         }
1685         return 1;
1686     }
1687     return 0;
1688 }
1689 
1690 int
netsnmp_remove_from_delegated(netsnmp_agent_session * asp)1691 netsnmp_remove_from_delegated(netsnmp_agent_session *asp)
1692 {
1693     netsnmp_agent_session *curr, *prev = NULL;
1694 
1695     for (curr = agent_delegated_list; curr; prev = curr, curr = curr->next) {
1696         /*
1697          * is this us?
1698          */
1699         if (curr != asp)
1700             continue;
1701 
1702         /*
1703          * remove from queue
1704          */
1705         if (prev != NULL)
1706             prev->next = asp->next;
1707         else
1708             agent_delegated_list = asp->next;
1709 
1710         DEBUGMSGTL(("snmp_agent", "remove delegated session == %8p\n", asp));
1711 
1712         return 1;
1713     }
1714 
1715     return 0;
1716 }
1717 
1718 /*
1719  * netsnmp_remove_delegated_requests_for_session
1720  *
1721  * called when a session is being closed. Check all delegated requests to
1722  * see if the are waiting on this session, and if set, set the status for
1723  * that request to GENERR.
1724  */
1725 int
netsnmp_remove_delegated_requests_for_session(netsnmp_session * sess)1726 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
1727 {
1728     netsnmp_agent_session *asp;
1729     int total_count = 0;
1730 
1731     for (asp = agent_delegated_list; asp; asp = asp->next) {
1732         /*
1733          * check each request
1734          */
1735         int i;
1736         int count = 0;
1737         netsnmp_request_info *request;
1738         for (i = 0; i <= asp->treecache_num; i++) {
1739             for (request = asp->treecache[i].requests_begin; request;
1740                  request = request->next) {
1741                 /*
1742                  * check session
1743                  */
1744                 netsnmp_assert(NULL!=request->subtree);
1745                 if(request->subtree->session != sess)
1746                     continue;
1747 
1748                 /*
1749                  * matched! mark request as done
1750                  */
1751                 netsnmp_request_set_error(request, SNMP_ERR_GENERR);
1752                 ++count;
1753             }
1754         }
1755         if (count) {
1756             asp->flags |= SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS;
1757             total_count += count;
1758         }
1759     }
1760 
1761     /*
1762      * if we found any, that request may be finished now
1763      */
1764     if(total_count) {
1765         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
1766                     "%8p\n", total_count, sess));
1767         netsnmp_check_delegated_requests();
1768     }
1769 
1770     return total_count;
1771 }
1772 
1773 #if 0
1774 int
1775 netsnmp_check_queued_chain_for(netsnmp_agent_session *asp)
1776 {
1777     netsnmp_agent_session *asptmp;
1778     for (asptmp = netsnmp_agent_queued_list; asptmp; asptmp = asptmp->next) {
1779         if (asptmp == asp)
1780             return 1;
1781     }
1782     return 0;
1783 }
1784 #endif
1785 
1786 int
netsnmp_add_queued(netsnmp_agent_session * asp)1787 netsnmp_add_queued(netsnmp_agent_session *asp)
1788 {
1789     netsnmp_agent_session *asp_tmp;
1790 
1791     /*
1792      * first item?
1793      */
1794     if (NULL == netsnmp_agent_queued_list) {
1795         netsnmp_agent_queued_list = asp;
1796         return 1;
1797     }
1798 
1799 
1800     /*
1801      * add to end of queued request chain
1802      */
1803     asp_tmp = netsnmp_agent_queued_list;
1804     for (; asp_tmp; asp_tmp = asp_tmp->next) {
1805         /*
1806          * already in queue?
1807          */
1808         if (asp_tmp == asp)
1809             break;
1810 
1811         /*
1812          * end of queue?
1813          */
1814         if (NULL == asp_tmp->next)
1815             asp_tmp->next = asp;
1816     }
1817     return 1;
1818 }
1819 
1820 #ifndef NETSNMP_NO_PDU_STATS
1821 /*
1822  * netsnmp_pdu_stats_process: record time for pdu processing
1823  */
1824 int
netsnmp_pdu_stats_process(netsnmp_agent_session * asp)1825 netsnmp_pdu_stats_process(netsnmp_agent_session *asp)
1826 {
1827     netsnmp_pdu_stats *new_entry, *old = NULL;
1828     struct timeval tv_end;
1829     marker_t start, end = &tv_end;
1830     u_long msec;
1831 
1832     if (NULL == asp) {
1833         DEBUGMSGTL(("stats:pdu", "netsnmp_pdu_stats_process bad params\n"));
1834         return -1;
1835     }
1836 
1837     /** get start/end time */
1838     netsnmp_set_monotonic_marker(&end);
1839     start = (marker_t)netsnmp_agent_get_list_data(asp->reqinfo,
1840                                                   "netsnmp_pdu_stats");
1841     if (NULL == start) {
1842         DEBUGMSGTL(("stats:pdu:stop", "start time not found!\n"));
1843         return -1;
1844     }
1845 
1846     msec = uatime_diff(start, end);
1847     DEBUGMSGTL(("stats:pdu:stop", "pdu processing took %ld msec\n", msec));
1848 
1849     /** bail if below threshold or less than current low time */
1850     if (msec <= _pdu_stats_threshold || msec < _pdu_stats_current_lowest) {
1851         DEBUGMSGTL(("9:stats:pdu",
1852                     "time below thresholds (%ld/%ld); ignoring\n",
1853                     _pdu_stats_threshold, _pdu_stats_current_lowest));
1854         return 0;
1855     }
1856 
1857     /** insert in list. if list goes over max size, truncate last entry. */
1858     new_entry = SNMP_MALLOC_TYPEDEF(netsnmp_pdu_stats);
1859     if (NULL == new_entry) {
1860         snmp_log(LOG_ERR, "malloc failed for pdu stats entry\n");
1861         return -1;
1862     }
1863     new_entry->processing_time = msec;
1864     time(&new_entry->timestamp);
1865     new_entry->pdu = snmp_clone_pdu(asp->pdu);
1866 
1867     CONTAINER_INSERT(_pdu_stats, new_entry);
1868 
1869     if (CONTAINER_SIZE(_pdu_stats) > _pdu_stats_max) {
1870         DEBUGMSGTL(("9:stats:pdu", "dropping old/low stat\n"));
1871         CONTAINER_REMOVE_AT(_pdu_stats, _pdu_stats_max, (void**)&old);
1872         if (old) {
1873             snmp_free_pdu(old->pdu);
1874             free(old);
1875         }
1876     }
1877 
1878     if (CONTAINER_SIZE(_pdu_stats) < _pdu_stats_max)
1879         _pdu_stats_current_lowest = 0; /* take anything over threshold */
1880     else {
1881         CONTAINER_GET_AT(_pdu_stats, _pdu_stats_max - 1, (void**)&old);
1882         if (old)
1883             _pdu_stats_current_lowest = old->processing_time;
1884     }
1885 
1886     DEBUGIF("9:stats:pdu") {
1887         _dump_pdu_stats();
1888     }
1889 
1890     return 1;
1891 
1892 }
1893 #endif /* NETSNMP_NO_PDU_STATS */
1894 
1895 int
netsnmp_wrap_up_request(netsnmp_agent_session * asp,int status)1896 netsnmp_wrap_up_request(netsnmp_agent_session *asp, int status)
1897 {
1898 #ifndef NETSNMP_NO_PDU_STATS
1899     if (_pdu_stats_max > 0)
1900         netsnmp_pdu_stats_process(asp);
1901 #endif /* NETSNMP_NO_PDU_STATS */
1902 
1903     /*
1904      * if this request was a set, clear the global now that we are
1905      * done.
1906      */
1907     if (asp == netsnmp_processing_set) {
1908         DEBUGMSGTL(("snmp_agent", "SET request complete, asp = %8p\n",
1909                     asp));
1910         netsnmp_processing_set = NULL;
1911     }
1912 
1913     if (asp->pdu) {
1914         /*
1915          * If we've got an error status, then this needs to be
1916          *  passed back up to the higher levels....
1917          */
1918         if ( status != 0  && asp->status == 0 )
1919             asp->status = status;
1920 
1921         switch (asp->pdu->command) {
1922 #ifndef NETSNMP_NO_WRITE_SUPPORT
1923             case SNMP_MSG_INTERNAL_SET_BEGIN:
1924             case SNMP_MSG_INTERNAL_SET_RESERVE1:
1925             case SNMP_MSG_INTERNAL_SET_RESERVE2:
1926             case SNMP_MSG_INTERNAL_SET_ACTION:
1927                 /*
1928                  * some stuff needs to be saved in special subagent cases
1929                  */
1930                 save_set_cache(asp);
1931                 break;
1932 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1933 
1934             case SNMP_MSG_GETNEXT:
1935                 _fix_endofmibview(asp);
1936                 break;
1937 
1938             case SNMP_MSG_GETBULK:
1939                 /*
1940                  * for a GETBULK response we need to rearrange the varbinds
1941                  */
1942                 _reorder_getbulk(asp);
1943                 break;
1944         }
1945 
1946         /*
1947          * May need to "dumb down" a SET error status for a
1948          * v1 query.  See RFC2576 - section 4.3
1949          */
1950 #ifndef NETSNMP_DISABLE_SNMPV1
1951 #ifndef NETSNMP_NO_WRITE_SUPPORT
1952         if ((asp->pdu->command == SNMP_MSG_SET) &&
1953             (asp->pdu->version == SNMP_VERSION_1)) {
1954             switch (asp->status) {
1955                 case SNMP_ERR_WRONGVALUE:
1956                 case SNMP_ERR_WRONGENCODING:
1957                 case SNMP_ERR_WRONGTYPE:
1958                 case SNMP_ERR_WRONGLENGTH:
1959                 case SNMP_ERR_INCONSISTENTVALUE:
1960                     status = SNMP_ERR_BADVALUE;
1961                     asp->status = SNMP_ERR_BADVALUE;
1962                     break;
1963                 case SNMP_ERR_NOACCESS:
1964                 case SNMP_ERR_NOTWRITABLE:
1965                 case SNMP_ERR_NOCREATION:
1966                 case SNMP_ERR_INCONSISTENTNAME:
1967                 case SNMP_ERR_AUTHORIZATIONERROR:
1968                     status = SNMP_ERR_NOSUCHNAME;
1969                     asp->status = SNMP_ERR_NOSUCHNAME;
1970                     break;
1971                 case SNMP_ERR_RESOURCEUNAVAILABLE:
1972                 case SNMP_ERR_COMMITFAILED:
1973                 case SNMP_ERR_UNDOFAILED:
1974                     status = SNMP_ERR_GENERR;
1975                     asp->status = SNMP_ERR_GENERR;
1976                     break;
1977             }
1978         }
1979 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1980         /*
1981          * Similarly we may need to "dumb down" v2 exception
1982          *  types to throw an error for a v1 query.
1983          *  See RFC2576 - section 4.1.2.3
1984          */
1985         if (
1986 #ifndef NETSNMP_NO_WRITE_SUPPORT
1987             (asp->pdu->command != SNMP_MSG_SET) &&
1988 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1989             (asp->pdu->version == SNMP_VERSION_1)) {
1990             netsnmp_variable_list *var_ptr = asp->pdu->variables;
1991             int                    i = 1;
1992 
1993             while (var_ptr != NULL) {
1994                 switch (var_ptr->type) {
1995                     case SNMP_NOSUCHOBJECT:
1996                     case SNMP_NOSUCHINSTANCE:
1997                     case SNMP_ENDOFMIBVIEW:
1998                     case ASN_COUNTER64:
1999                         status = SNMP_ERR_NOSUCHNAME;
2000                         asp->status = SNMP_ERR_NOSUCHNAME;
2001                         asp->index = i;
2002                         break;
2003                 }
2004                 var_ptr = var_ptr->next_variable;
2005                 ++i;
2006             }
2007         }
2008 #endif /* snmpv1 support */
2009 
2010         /** so far so good? try and build packet */
2011         if (status == SNMP_ERR_NOERROR) {
2012             struct session_list *slp = snmp_sess_pointer(asp->session);
2013 
2014             /** build packet to send */
2015             asp->pdu->command = SNMP_MSG_RESPONSE;
2016             asp->pdu->errstat = asp->status;
2017             asp->pdu->errindex = asp->index;
2018             status = _build_initial_pdu_packet(slp, asp->pdu,
2019                                                SNMP_MSG_GETBULK == asp->orig_pdu->command);
2020             if (SNMPERR_SUCCESS != status){
2021                 if (SNMPERR_TOO_LONG == asp->session->s_snmp_errno)
2022                     status = asp->status = SNMP_ERR_TOOBIG;
2023                 else
2024                     status = asp->status = SNMP_ERR_GENERR;
2025             }
2026         }
2027 
2028         if (status == SNMP_ERR_NOERROR)
2029             snmp_increment_statistic_by(
2030 #ifndef NETSNMP_NO_WRITE_SUPPORT
2031                 (asp->pdu->command == SNMP_MSG_SET ?
2032                  STAT_SNMPINTOTALSETVARS : STAT_SNMPINTOTALREQVARS),
2033 #else
2034                 STAT_SNMPINTOTALREQVARS,
2035 #endif
2036                 count_varbinds(asp->pdu->variables));
2037     } /** if asp->pdu */
2038 
2039     /*
2040      * Update the snmp error-count statistics
2041      *   XXX - should we include the V2 errors in this or not?
2042      */
2043 #define INCLUDE_V2ERRORS_IN_V1STATS
2044 
2045     switch (status) {
2046 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
2047     case SNMP_ERR_WRONGVALUE:
2048     case SNMP_ERR_WRONGENCODING:
2049     case SNMP_ERR_WRONGTYPE:
2050     case SNMP_ERR_WRONGLENGTH:
2051     case SNMP_ERR_INCONSISTENTVALUE:
2052 #endif
2053     case SNMP_ERR_BADVALUE:
2054         snmp_increment_statistic(STAT_SNMPOUTBADVALUES);
2055         break;
2056 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
2057     case SNMP_ERR_NOACCESS:
2058     case SNMP_ERR_NOTWRITABLE:
2059     case SNMP_ERR_NOCREATION:
2060     case SNMP_ERR_INCONSISTENTNAME:
2061     case SNMP_ERR_AUTHORIZATIONERROR:
2062 #endif
2063     case SNMP_ERR_NOSUCHNAME:
2064         snmp_increment_statistic(STAT_SNMPOUTNOSUCHNAMES);
2065         break;
2066 #ifdef INCLUDE_V2ERRORS_IN_V1STATS
2067     case SNMP_ERR_RESOURCEUNAVAILABLE:
2068     case SNMP_ERR_COMMITFAILED:
2069     case SNMP_ERR_UNDOFAILED:
2070 #endif
2071     case SNMP_ERR_GENERR:
2072         snmp_increment_statistic(STAT_SNMPOUTGENERRS);
2073         break;
2074 
2075     case SNMP_ERR_TOOBIG:
2076         snmp_increment_statistic(STAT_SNMPOUTTOOBIGS);
2077         break;
2078     }
2079 
2080     if ((status != SNMP_ERR_NOERROR) || (NULL == asp->pdu)) {
2081         /** Use a copy of the original request to report failures. */
2082         snmp_free_pdu(asp->pdu);
2083         asp->pdu = asp->orig_pdu;
2084         asp->orig_pdu = NULL;
2085     }
2086     if (asp->pdu) {
2087         asp->pdu->command = SNMP_MSG_RESPONSE;
2088         asp->pdu->errstat = asp->status;
2089         asp->pdu->errindex = asp->index;
2090         if (!snmp_send(asp->session, asp->pdu) &&
2091              asp->session->s_snmp_errno != SNMPERR_SUCCESS) {
2092             netsnmp_variable_list *var_ptr;
2093             snmp_perror("send response");
2094             for (var_ptr = asp->pdu->variables; var_ptr != NULL;
2095                      var_ptr = var_ptr->next_variable) {
2096                 size_t  c_oidlen = 256, c_outlen = 0;
2097                 u_char *c_oid = (u_char *) malloc(c_oidlen);
2098 
2099                 if (c_oid) {
2100                     if (!sprint_realloc_objid (&c_oid, &c_oidlen, &c_outlen, 1,
2101                                                var_ptr->name,
2102                                                var_ptr->name_length)) {
2103                         snmp_log(LOG_ERR, "    -- %s [TRUNCATED]\n", c_oid);
2104                     } else {
2105                         snmp_log(LOG_ERR, "    -- %s\n", c_oid);
2106                     }
2107                     SNMP_FREE(c_oid);
2108                 }
2109             }
2110             snmp_free_pdu(asp->pdu);
2111         }
2112         snmp_increment_statistic(STAT_SNMPOUTPKTS);
2113         snmp_increment_statistic(STAT_SNMPOUTGETRESPONSES);
2114         asp->pdu = NULL;
2115         netsnmp_remove_and_free_agent_snmp_session(asp);
2116     }
2117     return 1;
2118 }
2119 
2120 #ifndef NETSNMP_FEATURE_REMOVE_DUMP_SESS_LIST
2121 void
dump_sess_list(void)2122 dump_sess_list(void)
2123 {
2124     netsnmp_agent_session *a;
2125 
2126     DEBUGMSGTL(("snmp_agent", "DUMP agent_sess_list -> "));
2127     for (a = agent_session_list; a != NULL; a = a->next) {
2128         DEBUGMSG(("snmp_agent", "%8p[session %8p] -> ", a, a->session));
2129     }
2130     DEBUGMSG(("snmp_agent", "[NIL]\n"));
2131 }
2132 #endif /* NETSNMP_FEATURE_REMOVE_DUMP_SESS_LIST */
2133 
2134 void
netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session * asp)2135 netsnmp_remove_and_free_agent_snmp_session(netsnmp_agent_session *asp)
2136 {
2137     netsnmp_agent_session *a, **prevNext = &agent_session_list;
2138 
2139     DEBUGMSGTL(("snmp_agent", "REMOVE session == %8p\n", asp));
2140 
2141     for (a = agent_session_list; a != NULL; a = *prevNext) {
2142         if (a == asp) {
2143             *prevNext = a->next;
2144             a->next = NULL;
2145             free_agent_snmp_session(a);
2146             asp = NULL;
2147             break;
2148         } else {
2149             prevNext = &(a->next);
2150         }
2151     }
2152 
2153     if (a == NULL && asp != NULL) {
2154         /*
2155          * We coulnd't find it on the list, so free it anyway.
2156          */
2157         free_agent_snmp_session(asp);
2158     }
2159 }
2160 
2161 #ifndef NETSNMP_FEATURE_REMOVE_FREE_AGENT_SNMP_SESSION_BY_SESSION
2162 void
netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,void (* free_request)(netsnmp_request_list *))2163 netsnmp_free_agent_snmp_session_by_session(netsnmp_session * sess,
2164                                            void (*free_request)
2165                                            (netsnmp_request_list *))
2166 {
2167     netsnmp_agent_session *a, *next, **prevNext = &agent_session_list;
2168 
2169     DEBUGMSGTL(("snmp_agent", "REMOVE session == %8p\n", sess));
2170 
2171     for (a = agent_session_list; a != NULL; a = next) {
2172         if (a->session == sess) {
2173             *prevNext = a->next;
2174             next = a->next;
2175             free_agent_snmp_session(a);
2176         } else {
2177             prevNext = &(a->next);
2178             next = a->next;
2179         }
2180     }
2181 }
2182 #endif /* NETSNMP_FEATURE_REMOVE_FREE_AGENT_SNMP_SESSION_BY_SESSION */
2183 
2184 /** handles an incoming SNMP packet into the agent */
2185 int
handle_snmp_packet(int op,netsnmp_session * session,int reqid,netsnmp_pdu * pdu,void * magic)2186 handle_snmp_packet(int op, netsnmp_session * session, int reqid,
2187                    netsnmp_pdu *pdu, void *magic)
2188 {
2189     netsnmp_agent_session *asp;
2190     int             status, access_ret, rc;
2191 
2192     /*
2193      * We only support receiving here.
2194      */
2195     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
2196         return 1;
2197     }
2198 
2199     /*
2200      * RESPONSE messages won't get this far, but TRAP-like messages
2201      * might.
2202      */
2203     if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
2204         pdu->command == SNMP_MSG_TRAP2) {
2205         DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
2206                     pdu->command));
2207         pdu->command = SNMP_MSG_TRAP2;
2208         snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
2209         return 1;
2210     }
2211 
2212     /*
2213      * send snmpv3 authfail trap.
2214      */
2215     if (pdu->version  == SNMP_VERSION_3 &&
2216         session->s_snmp_errno == SNMPERR_USM_AUTHENTICATIONFAILURE) {
2217            send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
2218            return 1;
2219     }
2220 
2221     if (magic == NULL) {
2222         asp = init_agent_snmp_session(session, pdu);
2223         status = SNMP_ERR_NOERROR;
2224     } else {
2225         asp = (netsnmp_agent_session *) magic;
2226         status = asp->status;
2227     }
2228 
2229 #if defined(NETSNMP_DISABLE_SET_SUPPORT) && !defined(NETSNMP_NO_WRITE_SUPPORT)
2230     if (pdu->command == SNMP_MSG_SET) {
2231         /** Silvercreek protocol tests send set with 0 varbinds */
2232         if (NULL == pdu->variables)
2233             return netsnmp_wrap_up_request(asp, SNMP_ERR_NOERROR);
2234         asp->index = 1;
2235         return netsnmp_wrap_up_request(asp, SNMP_ERR_NOTWRITABLE);
2236     }
2237 #endif /* NETSNMP_DISABLE_SET_SUPPORT && !NETSNMP_NO_WRITE_SUPPORT */
2238 
2239     if ((access_ret = check_access(asp->pdu)) != 0) {
2240         if (access_ret == VACM_NOSUCHCONTEXT) {
2241             /*
2242              * rfc3413 section 3.2, step 5 says that we increment the
2243              * counter but don't return a response of any kind
2244              */
2245 
2246             /*
2247              * we currently don't support unavailable contexts, as
2248              * there is no reason to that I currently know of
2249              */
2250             snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);
2251 
2252             /*
2253              * drop the request
2254              */
2255             netsnmp_remove_and_free_agent_snmp_session(asp);
2256             return 0;
2257         } else {
2258             /*
2259              * access control setup is incorrect
2260              */
2261             send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
2262 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
2263 #if defined(NETSNMP_DISABLE_SNMPV1)
2264             if (asp->pdu->version != SNMP_VERSION_2c) {
2265 #else
2266 #if defined(NETSNMP_DISABLE_SNMPV2C)
2267             if (asp->pdu->version != SNMP_VERSION_1) {
2268 #else
2269             if (asp->pdu->version != SNMP_VERSION_1
2270                 && asp->pdu->version != SNMP_VERSION_2c) {
2271 #endif
2272 #endif
2273                 asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
2274                 asp->pdu->command = SNMP_MSG_RESPONSE;
2275                 snmp_increment_statistic(STAT_SNMPOUTPKTS);
2276                 if (!snmp_send(asp->session, asp->pdu))
2277                     snmp_free_pdu(asp->pdu);
2278                 asp->pdu = NULL;
2279                 netsnmp_remove_and_free_agent_snmp_session(asp);
2280                 return 1;
2281             } else {
2282 #endif /* support for community based SNMP */
2283                 /*
2284                  * drop the request
2285                  */
2286                 netsnmp_remove_and_free_agent_snmp_session(asp);
2287                 return 0;
2288 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
2289             }
2290 #endif /* support for community based SNMP */
2291         }
2292     }
2293 
2294     rc = netsnmp_handle_request(asp, status);
2295 
2296     /*
2297      * done
2298      */
2299     DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %8p\n",
2300                 asp));
2301     return rc;
2302 }
2303 
2304 netsnmp_request_info *
2305 netsnmp_add_varbind_to_cache(netsnmp_agent_session *asp, int vbcount,
2306                              netsnmp_variable_list * varbind_ptr,
2307                              netsnmp_subtree *tp)
2308 {
2309     netsnmp_request_info *request = NULL;
2310 
2311     DEBUGMSGTL(("snmp_agent", "add_vb_to_cache(%8p, %d, ", asp, vbcount));
2312     DEBUGMSGOID(("snmp_agent", varbind_ptr->name,
2313                  varbind_ptr->name_length));
2314     DEBUGMSG(("snmp_agent", ", %8p)\n", tp));
2315 
2316     if (tp &&
2317         (asp->pdu->command == SNMP_MSG_GETNEXT ||
2318          asp->pdu->command == SNMP_MSG_GETBULK)) {
2319         int result;
2320         int prefix_len;
2321 
2322         prefix_len = netsnmp_oid_find_prefix(tp->start_a,
2323                                              tp->start_len,
2324                                              tp->end_a, tp->end_len);
2325         if (prefix_len < 1) {
2326             result = VACM_NOTINVIEW; /* ack...  bad bad thing happened */
2327         } else {
2328             result =
2329                 netsnmp_acm_check_subtree(asp->pdu, tp->start_a, prefix_len);
2330         }
2331 
2332         while (result == VACM_NOTINVIEW) {
2333             /* the entire subtree is not in view. Skip it. */
2334             /** @todo make this be more intelligent about ranges.
2335                 Right now we merely take the highest level
2336                 commonality of a registration range and use that.
2337                 At times we might be able to be smarter about
2338                 checking the range itself as opposed to the node
2339                 above where the range exists, but I doubt this will
2340                 come up all that frequently. */
2341             tp = tp->next;
2342             if (tp) {
2343                 prefix_len = netsnmp_oid_find_prefix(tp->start_a,
2344                                                      tp->start_len,
2345                                                      tp->end_a,
2346                                                      tp->end_len);
2347                 if (prefix_len < 1) {
2348                     /* ack...  bad bad thing happened */
2349                     result = VACM_NOTINVIEW;
2350                 } else {
2351                     result =
2352                         netsnmp_acm_check_subtree(asp->pdu,
2353                                                   tp->start_a, prefix_len);
2354                 }
2355             }
2356             else
2357                 break;
2358         }
2359     }
2360     if (tp == NULL) {
2361         /*
2362          * no appropriate registration found
2363          */
2364         /*
2365          * make up the response ourselves
2366          */
2367         switch (asp->pdu->command) {
2368         case SNMP_MSG_GETNEXT:
2369         case SNMP_MSG_GETBULK:
2370             varbind_ptr->type = SNMP_ENDOFMIBVIEW;
2371             break;
2372 
2373 #ifndef NETSNMP_NO_WRITE_SUPPORT
2374         case SNMP_MSG_SET:
2375 #endif /* NETSNMP_NO_WRITE_SUPPORT */
2376         case SNMP_MSG_GET:
2377             varbind_ptr->type = SNMP_NOSUCHOBJECT;
2378             break;
2379 
2380         default:
2381             return NULL;        /* shouldn't get here */
2382         }
2383     } else {
2384         int cacheid;
2385 
2386         DEBUGMSGTL(("snmp_agent", "tp->start "));
2387         DEBUGMSGOID(("snmp_agent", tp->start_a, tp->start_len));
2388         DEBUGMSG(("snmp_agent", ", tp->end "));
2389         DEBUGMSGOID(("snmp_agent", tp->end_a, tp->end_len));
2390         DEBUGMSG(("snmp_agent", ", \n"));
2391 
2392         /*
2393          * malloc the request structure
2394          */
2395         request = &(asp->requests[vbcount - 1]);
2396         request->index = vbcount;
2397         request->delegated = 0;
2398         request->processed = 0;
2399         request->status = 0;
2400         request->subtree = tp;
2401         request->agent_req_info = asp->reqinfo;
2402         if (request->parent_data) {
2403             netsnmp_free_request_data_sets(request);
2404         }
2405         DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
2406                     asp, asp->reqinfo));
2407 
2408         /*
2409          * for non-SET modes, set the type to NULL
2410          */
2411 #ifndef NETSNMP_NO_WRITE_SUPPORT
2412         if (!MODE_IS_SET(asp->pdu->command)) {
2413 #endif /* NETSNMP_NO_WRITE_SUPPORT */
2414             DEBUGMSGTL(("verbose:asp", "asp %p reqinfo %p assigned to request\n",
2415                     asp, asp->reqinfo));
2416             if (varbind_ptr->type == ASN_PRIV_INCL_RANGE) {
2417                 DEBUGMSGTL(("snmp_agent", "varbind %d is inclusive\n",
2418                             request->index));
2419                 request->inclusive = 1;
2420             }
2421             varbind_ptr->type = ASN_NULL;
2422 #ifndef NETSNMP_NO_WRITE_SUPPORT
2423         }
2424 #endif /* NETSNMP_NO_WRITE_SUPPORT */
2425 
2426         /*
2427          * place them in a cache
2428          */
2429         if (tp->global_cacheid) {
2430             /*
2431              * we need to merge all marked subtrees together
2432              */
2433             if (asp->cache_store && -1 !=
2434                 (cacheid = netsnmp_get_local_cachid(asp->cache_store,
2435                                                     tp->global_cacheid))) {
2436             } else {
2437                 cacheid = ++(asp->treecache_num);
2438                 netsnmp_get_or_add_local_cachid(&asp->cache_store,
2439                                                 tp->global_cacheid,
2440                                                 cacheid);
2441                 goto mallocslot;        /* XXX: ick */
2442             }
2443         } else if (tp->cacheid > -1 && tp->cacheid <= asp->treecache_num &&
2444                    asp->treecache[tp->cacheid].subtree == tp) {
2445             /*
2446              * we have already added a request to this tree
2447              * pointer before
2448              */
2449             cacheid = tp->cacheid;
2450         } else {
2451             cacheid = ++(asp->treecache_num);
2452           mallocslot:
2453             /*
2454              * new slot needed
2455              */
2456             if (asp->treecache_num >= asp->treecache_len) {
2457                 /*
2458                  * exapand cache array
2459                  */
2460                 /*
2461                  * WWW: non-linear expansion needed (with cap)
2462                  */
2463 #define CACHE_GROW_SIZE 16
2464                 asp->treecache_len =
2465                     (asp->treecache_len + CACHE_GROW_SIZE);
2466                 asp->treecache =
2467                     (netsnmp_tree_cache *)realloc(asp->treecache,
2468                             sizeof(netsnmp_tree_cache) *
2469                             asp->treecache_len);
2470                 if (asp->treecache == NULL)
2471                     return NULL;
2472                 memset(asp->treecache + cacheid, 0,
2473                        sizeof(netsnmp_tree_cache) * (CACHE_GROW_SIZE));
2474             }
2475             asp->treecache[cacheid].subtree = tp;
2476             asp->treecache[cacheid].requests_begin = request;
2477             tp->cacheid = cacheid;
2478         }
2479 
2480         /*
2481          * if this is a search type, get the ending range oid as well
2482          */
2483         if (asp->pdu->command == SNMP_MSG_GETNEXT ||
2484             asp->pdu->command == SNMP_MSG_GETBULK) {
2485             request->range_end = tp->end_a;
2486             request->range_end_len = tp->end_len;
2487         } else {
2488             request->range_end = NULL;
2489             request->range_end_len = 0;
2490         }
2491 
2492         /*
2493          * link into chain
2494          */
2495         if (asp->treecache[cacheid].requests_end)
2496             asp->treecache[cacheid].requests_end->next = request;
2497         request->next = NULL;
2498         request->prev = asp->treecache[cacheid].requests_end;
2499         asp->treecache[cacheid].requests_end = request;
2500 
2501         /*
2502          * add the given request to the list of requests they need
2503          * to handle results for
2504          */
2505         request->requestvb = request->requestvb_start = varbind_ptr;
2506     }
2507     return request;
2508 }
2509 
2510 /*
2511  * check the ACM(s) for the results on each of the varbinds.
2512  * If ACM disallows it, replace the value with type
2513  *
2514  * Returns number of varbinds with ACM errors
2515  */
2516 int
2517 check_acm(netsnmp_agent_session *asp, u_char type)
2518 {
2519     int             view;
2520     int             i, j, k;
2521     netsnmp_request_info *request;
2522     int             ret = 0;
2523     netsnmp_variable_list *vb, *vb2, *vbc;
2524     int             earliest = 0;
2525 
2526     for (i = 0; i <= asp->treecache_num; i++) {
2527         for (request = asp->treecache[i].requests_begin;
2528              request; request = request->next) {
2529             /*
2530              * for each request, run it through in_a_view()
2531              */
2532             earliest = 0;
2533             for(j = request->repeat, vb = request->requestvb_start;
2534                 vb && j > -1;
2535                 j--, vb = vb->next_variable) {
2536                 if (vb->type != ASN_NULL &&
2537                     vb->type != ASN_PRIV_RETRY) { /* not yet processed */
2538                     view =
2539                         in_a_view(vb->name, &vb->name_length,
2540                                   asp->pdu, vb->type);
2541 
2542                     /*
2543                      * if a ACM error occurs, mark it as type passed in
2544                      */
2545                     if (view != VACM_SUCCESS) {
2546                         ret++;
2547                         if (request->repeat < request->orig_repeat) {
2548                             /* basically this means a GETBULK */
2549                             request->repeat++;
2550                             if (!earliest) {
2551                                 request->requestvb = vb;
2552                                 earliest = 1;
2553                             }
2554 
2555                             /* ugh.  if a whole now exists, we need to
2556                                move the contents up the chain and fill
2557                                in at the end else we won't end up
2558                                lexographically sorted properly */
2559                             if (j > -1 && vb->next_variable &&
2560                                 vb->next_variable->type != ASN_NULL &&
2561                                 vb->next_variable->type != ASN_PRIV_RETRY) {
2562                                 for(k = j, vbc = vb, vb2 = vb->next_variable;
2563                                     k > -2 && vbc && vb2;
2564                                     k--, vbc = vb2, vb2 = vb2->next_variable) {
2565                                     /* clone next into the current */
2566                                     snmp_clone_var(vb2, vbc);
2567                                     vbc->next_variable = vb2;
2568                                 }
2569                             }
2570                         }
2571                         snmp_set_var_typed_value(vb, type, NULL, 0);
2572                         if (ASN_PRIV_RETRY == type)
2573                             request->inclusive = 0;
2574                     }
2575                 }
2576             }
2577         }
2578     }
2579     return ret;
2580 }
2581 
2582 
2583 int
2584 netsnmp_create_subtree_cache(netsnmp_agent_session *asp)
2585 {
2586     netsnmp_subtree *tp;
2587     netsnmp_variable_list *varbind_ptr, *vbsave, *vbptr, **prevNext;
2588     int             view;
2589     int             vbcount = 0;
2590     int             bulkcount = 0, bulkrep = 0;
2591     int             i = 0, n = 0, r = 0;
2592     netsnmp_request_info *request;
2593 
2594     if (NULL == asp || NULL == asp->pdu)
2595         return SNMP_ERR_GENERR;
2596 
2597     if (asp->pdu->msgMaxSize == 0)
2598         asp->pdu->msgMaxSize = netsnmp_max_send_msg_size();
2599     DEBUGMSGTL(("msgMaxSize", "pdu max size %lu\n", asp->pdu->msgMaxSize));
2600 
2601     if (asp->treecache == NULL && asp->treecache_len == 0) {
2602         asp->treecache_len = SNMP_MAX(1 + asp->vbcount / 4, 16);
2603         asp->treecache =
2604             (netsnmp_tree_cache *)calloc(asp->treecache_len, sizeof(netsnmp_tree_cache));
2605         if (asp->treecache == NULL)
2606             return SNMP_ERR_GENERR;
2607     }
2608     asp->treecache_num = -1;
2609 
2610     if (asp->pdu->command == SNMP_MSG_GETBULK) {
2611         /*
2612          * getbulk prep
2613          */
2614         int             count = count_varbinds(asp->pdu->variables);
2615         if (asp->pdu->errstat < 0) {
2616             asp->pdu->errstat = 0;
2617         }
2618         if (asp->pdu->errindex < 0) {
2619             asp->pdu->errindex = 0;
2620         }
2621 
2622         if (asp->pdu->errstat < count) {
2623             n = asp->pdu->errstat;
2624         } else {
2625             n = count;
2626         }
2627         if ((r = count - n) <= 0) {
2628             r = 0;
2629             asp->bulkcache = NULL;
2630         } else {
2631             int           maxbulk =
2632                 netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
2633                                    NETSNMP_DS_AGENT_MAX_GETBULKREPEATS);
2634             int maxresponses =
2635                 netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
2636                                    NETSNMP_DS_AGENT_MAX_GETBULKRESPONSES);
2637             int avgvarbind =
2638                 netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
2639                                    NETSNMP_DS_AGENT_AVG_BULKVARBINDSIZE);
2640 
2641             if (maxresponses == 0)
2642                 maxresponses = 100;   /* more than reasonable default */
2643 
2644             /* ensure that the total number of responses fits in a mallocable
2645              * result vector
2646              */
2647             if (maxresponses < 0 ||
2648                 maxresponses > (int)(INT_MAX / sizeof(struct varbind_list *)))
2649                 maxresponses = (int)(INT_MAX / sizeof(struct varbind_list *));
2650             DEBUGMSGTL(("snmp_agent:bulk", "maxresponse %d\n", maxresponses));
2651 
2652             /* reduce maxresponses by dividing the sessions max size by a
2653              * (very) rough aproximation of the size of an average
2654              * varbind. 15 seems to be a reasonable balance between getting
2655              * enough varbinds to fill the packet vs retrieving varbinds
2656              * that will be discarded to make the response fit the packet size.
2657              */
2658             if (avgvarbind == 0)
2659                 avgvarbind = 15;
2660 
2661             if (maxresponses > (asp->pdu->msgMaxSize / avgvarbind)) {
2662                 maxresponses = asp->pdu->msgMaxSize / avgvarbind;
2663                 DEBUGMSGTL(("snmp_agent:bulk",
2664                             "lowering maxresponse to %d based pdusession msgMaxSize %ld and avgBulkVarbindSize %d\n",
2665                             maxresponses, asp->pdu->msgMaxSize, avgvarbind));
2666             }
2667 
2668              /* ensure that the maximum number of repetitions will fit in the
2669              * result vector
2670              */
2671             if (maxbulk <= 0 || maxbulk > maxresponses / r)
2672                 maxbulk = maxresponses / r;
2673 
2674             /* limit getbulk number of repeats to a configured size */
2675             if (asp->pdu->errindex > maxbulk) {
2676                 asp->pdu->errindex = maxbulk;
2677                 DEBUGMSGTL(("snmp_agent:bulk",
2678                             "lowering requested getbulk repeats to %ld\n",
2679                             asp->pdu->errindex));
2680             }
2681 
2682             asp->bulkcache =
2683                 (netsnmp_variable_list **) malloc(
2684                     (n + asp->pdu->errindex * r) * sizeof(struct varbind_list *));
2685 
2686             if (!asp->bulkcache) {
2687                 DEBUGMSGTL(("snmp_agent:bulk", "Bulkcache malloc failed\n"));
2688                 return SNMP_ERR_GENERR;
2689             }
2690         }
2691         DEBUGMSGTL(("snmp_agent:bulk", "GETBULK N = %d, M = %ld, R = %d\n",
2692                     n, asp->pdu->errindex, r));
2693     }
2694 
2695     /*
2696      * collect varbinds into their registered trees
2697      */
2698     prevNext = &(asp->pdu->variables);
2699     for (varbind_ptr = asp->pdu->variables; varbind_ptr;
2700          varbind_ptr = vbsave) {
2701 
2702         /*
2703          * getbulk mess with this pointer, so save it
2704          */
2705         vbsave = varbind_ptr->next_variable;
2706 
2707         if (asp->pdu->command == SNMP_MSG_GETBULK) {
2708             if (n > 0) {
2709                 n--;
2710             } else {
2711                 /*
2712                  * repeate request varbinds on GETBULK.  These will
2713                  * have to be properly rearranged later though as
2714                  * responses are supposed to actually be interlaced
2715                  * with each other.  This is done with the asp->bulkcache.
2716                  */
2717                 bulkrep = asp->pdu->errindex - 1;
2718                 if (asp->pdu->errindex > 0) {
2719                     vbptr = varbind_ptr;
2720                     asp->bulkcache[bulkcount++] = vbptr;
2721 
2722                     for (i = 1; i < asp->pdu->errindex; i++) {
2723                         vbptr->next_variable =
2724                             SNMP_MALLOC_STRUCT(variable_list);
2725                         /*
2726                          * don't clone the oid as it's got to be
2727                          * overwritten anyway
2728                          */
2729                         if (!vbptr->next_variable) {
2730                             /*
2731                              * XXXWWW: ack!!!
2732                              */
2733                             DEBUGMSGTL(("snmp_agent", "NextVar malloc failed\n"));
2734                         } else {
2735                             vbptr = vbptr->next_variable;
2736                             vbptr->name_length = 0;
2737                             vbptr->type = ASN_NULL;
2738                             asp->bulkcache[bulkcount++] = vbptr;
2739                         }
2740                     }
2741                     vbptr->next_variable = vbsave;
2742                 } else {
2743                     /*
2744                      * 0 repeats requested for this varbind, so take it off
2745                      * the list.
2746                      */
2747                     vbptr = varbind_ptr;
2748                     *prevNext = vbptr->next_variable;
2749                     vbptr->next_variable = NULL;
2750                     snmp_free_varbind(vbptr);
2751                     asp->vbcount--;
2752                     continue;
2753                 }
2754             }
2755         }
2756 
2757         /*
2758          * count the varbinds
2759          */
2760         ++vbcount;
2761 
2762         /*
2763          * find the owning tree
2764          */
2765         tp = netsnmp_subtree_find(varbind_ptr->name, varbind_ptr->name_length,
2766 				  NULL, asp->pdu->contextName);
2767 
2768         /*
2769          * check access control
2770          */
2771         switch (asp->pdu->command) {
2772         case SNMP_MSG_GET:
2773             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
2774                              asp->pdu, varbind_ptr->type);
2775             if (view != VACM_SUCCESS)
2776                 snmp_set_var_typed_value(varbind_ptr, SNMP_NOSUCHOBJECT,
2777                                          NULL, 0);
2778             break;
2779 
2780 #ifndef NETSNMP_NO_WRITE_SUPPORT
2781         case SNMP_MSG_SET:
2782             view = in_a_view(varbind_ptr->name, &varbind_ptr->name_length,
2783                              asp->pdu, varbind_ptr->type);
2784             if (view != VACM_SUCCESS) {
2785                 asp->index = vbcount;
2786                 return SNMP_ERR_NOACCESS;
2787             }
2788             break;
2789 #endif /* NETSNMP_NO_WRITE_SUPPORT */
2790 
2791         case SNMP_MSG_GETNEXT:
2792         case SNMP_MSG_GETBULK:
2793         default:
2794             view = VACM_SUCCESS;
2795             /*
2796              * XXXWWW: check VACM here to see if "tp" is even worthwhile
2797              */
2798         }
2799         if (view == VACM_SUCCESS) {
2800             request = netsnmp_add_varbind_to_cache(asp, vbcount, varbind_ptr,
2801 						   tp);
2802             if (request && asp->pdu->command == SNMP_MSG_GETBULK) {
2803                 request->repeat = request->orig_repeat = bulkrep;
2804             }
2805         }
2806 
2807         prevNext = &(varbind_ptr->next_variable);
2808     }
2809 
2810     return SNMPERR_SUCCESS;
2811 }
2812 
2813 /*
2814  * this function is only applicable in getnext like contexts
2815  */
2816 int
2817 netsnmp_reassign_requests(netsnmp_agent_session *asp)
2818 {
2819     /*
2820      * assume all the requests have been filled or rejected by the
2821      * subtrees, so reassign the rejected ones to the next subtree in
2822      * the chain
2823      */
2824 
2825     int             i;
2826 
2827     /*
2828      * get old info
2829      */
2830     netsnmp_tree_cache *old_treecache = asp->treecache;
2831 
2832     /*
2833      * malloc new space
2834      */
2835     asp->treecache =
2836         (netsnmp_tree_cache *) calloc(asp->treecache_len,
2837                                       sizeof(netsnmp_tree_cache));
2838 
2839     if (asp->treecache == NULL)
2840         return SNMP_ERR_GENERR;
2841 
2842     asp->treecache_num = -1;
2843     if (asp->cache_store) {
2844         netsnmp_free_cachemap(asp->cache_store);
2845         asp->cache_store = NULL;
2846     }
2847 
2848     for (i = 0; i < asp->vbcount; i++) {
2849         if (asp->requests[i].requestvb == NULL) {
2850             /*
2851              * Occurs when there's a mixture of still active
2852              *   and "endOfMibView" repetitions
2853              */
2854             continue;
2855         }
2856         if (asp->requests[i].requestvb->type == ASN_NULL) {
2857             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
2858                                               asp->requests[i].requestvb,
2859                                               asp->requests[i].subtree->next)) {
2860                 SNMP_FREE(old_treecache);
2861             }
2862         } else if (asp->requests[i].requestvb->type == ASN_PRIV_RETRY) {
2863             /*
2864              * re-add the same subtree
2865              */
2866             asp->requests[i].requestvb->type = ASN_NULL;
2867             if (!netsnmp_add_varbind_to_cache(asp, asp->requests[i].index,
2868                                               asp->requests[i].requestvb,
2869                                               asp->requests[i].subtree)) {
2870                 SNMP_FREE(old_treecache);
2871             }
2872         }
2873     }
2874 
2875     SNMP_FREE(old_treecache);
2876     return SNMP_ERR_NOERROR;
2877 }
2878 
2879 void
2880 netsnmp_delete_request_infos(netsnmp_request_info *reqlist)
2881 {
2882     while (reqlist) {
2883         netsnmp_free_request_data_sets(reqlist);
2884         reqlist = reqlist->next;
2885     }
2886 }
2887 
2888 #ifndef NETSNMP_FEATURE_REMOVE_DELETE_SUBTREE_CACHE
2889 void
2890 netsnmp_delete_subtree_cache(netsnmp_agent_session *asp)
2891 {
2892     while (asp->treecache_num >= 0) {
2893         /*
2894          * don't delete subtrees
2895          */
2896         netsnmp_delete_request_infos(asp->treecache[asp->treecache_num].
2897                                      requests_begin);
2898         asp->treecache_num--;
2899     }
2900 }
2901 #endif /* NETSNMP_FEATURE_REMOVE_DELETE_SUBTREE_CACHE */
2902 
2903 #ifndef NETSNMP_FEATURE_REMOVE_CHECK_ALL_REQUESTS_ERROR
2904 /*
2905  * check all requests for errors
2906  *
2907  * @Note:
2908  * This function is a little different from the others in that
2909  * it does not use any linked lists, instead using the original
2910  * asp requests array. This is of particular importance for
2911  * cases where the linked lists are unreliable. One known instance
2912  * of this scenario occurs when the row_merge helper is used, which
2913  * may temporarily disrupts linked lists during its (and its childrens)
2914  * handling of requests.
2915  */
2916 int
2917 netsnmp_check_all_requests_error(netsnmp_agent_session *asp,
2918                                  int look_for_specific)
2919 {
2920     int i;
2921 
2922     /*
2923      * find any errors marked in the requests
2924      */
2925     for( i = 0; i < asp->vbcount; ++i ) {
2926         if ((SNMP_ERR_NOERROR != asp->requests[i].status) &&
2927             (!look_for_specific ||
2928              asp->requests[i].status == look_for_specific))
2929             return asp->requests[i].status;
2930     }
2931 
2932     return SNMP_ERR_NOERROR;
2933 }
2934 #endif /* NETSNMP_FEATURE_REMOVE_CHECK_ALL_REQUESTS_ERROR */
2935 
2936 #ifndef NETSNMP_FEATURE_REMOVE_CHECK_REQUESTS_ERROR
2937 int
2938 netsnmp_check_requests_error(netsnmp_request_info *requests)
2939 {
2940     /*
2941      * find any errors marked in the requests
2942      */
2943     for (;requests;requests = requests->next) {
2944         if (requests->status != SNMP_ERR_NOERROR)
2945             return requests->status;
2946     }
2947     return SNMP_ERR_NOERROR;
2948 }
2949 #endif /* NETSNMP_FEATURE_REMOVE_CHECK_REQUESTS_ERROR */
2950 
2951 int
2952 netsnmp_check_requests_status(netsnmp_agent_session *asp,
2953                               netsnmp_request_info *requests,
2954                               int look_for_specific)
2955 {
2956     /*
2957      * find any errors marked in the requests
2958      */
2959     while (requests) {
2960         if(requests->agent_req_info != asp->reqinfo) {
2961             DEBUGMSGTL(("verbose:asp",
2962                         "**reqinfo %p doesn't match cached reqinfo %p\n",
2963                         asp->reqinfo, requests->agent_req_info));
2964         }
2965         if (requests->status != SNMP_ERR_NOERROR &&
2966             (!look_for_specific || requests->status == look_for_specific)
2967             && (look_for_specific || asp->index == 0
2968                 || requests->index < asp->index)) {
2969             asp->index = requests->index;
2970             asp->status = requests->status;
2971         }
2972         requests = requests->next;
2973     }
2974     return asp->status;
2975 }
2976 
2977 int
2978 netsnmp_check_all_requests_status(netsnmp_agent_session *asp,
2979                                   int look_for_specific)
2980 {
2981     int             i;
2982     for (i = 0; i <= asp->treecache_num; i++) {
2983         netsnmp_check_requests_status(asp,
2984                                       asp->treecache[i].requests_begin,
2985                                       look_for_specific);
2986     }
2987     return asp->status;
2988 }
2989 
2990 int
2991 handle_var_requests(netsnmp_agent_session *asp)
2992 {
2993     int             i, retstatus = SNMP_ERR_NOERROR,
2994         status = SNMP_ERR_NOERROR, final_status = SNMP_ERR_NOERROR;
2995     netsnmp_handler_registration *reginfo;
2996 
2997     asp->reqinfo->asp = asp;
2998     asp->reqinfo->mode = asp->mode;
2999 
3000     /*
3001      * now, have the subtrees in the cache go search for their results
3002      */
3003     for (i = 0; i <= asp->treecache_num; i++) {
3004         /*
3005          * don't call handlers w/null reginfo.
3006          * - when is this? sub agent disconnected while request processing?
3007          * - should this case encompass more of this subroutine?
3008          *   - does check_request_status make send if handlers weren't called?
3009          */
3010         if(NULL != asp->treecache[i].subtree->reginfo) {
3011             reginfo = asp->treecache[i].subtree->reginfo;
3012             status = netsnmp_call_handlers(reginfo, asp->reqinfo,
3013                                            asp->treecache[i].requests_begin);
3014         }
3015         else
3016             status = SNMP_ERR_GENERR;
3017 
3018         /*
3019          * find any errors marked in the requests.  For later parts of
3020          * SET processing, only check for new errors specific to that
3021          * set processing directive (which must superceed the previous
3022          * errors).
3023          */
3024         switch (asp->mode) {
3025 #ifndef NETSNMP_NO_WRITE_SUPPORT
3026         case MODE_SET_COMMIT:
3027             retstatus = netsnmp_check_requests_status(asp,
3028 						      asp->treecache[i].
3029 						      requests_begin,
3030 						      SNMP_ERR_COMMITFAILED);
3031             break;
3032 
3033         case MODE_SET_UNDO:
3034             retstatus = netsnmp_check_requests_status(asp,
3035 						      asp->treecache[i].
3036 						      requests_begin,
3037 						      SNMP_ERR_UNDOFAILED);
3038             break;
3039 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3040 
3041         default:
3042             retstatus = netsnmp_check_requests_status(asp,
3043 						      asp->treecache[i].
3044 						      requests_begin, 0);
3045             break;
3046         }
3047 
3048         /*
3049          * always take lowest varbind if possible
3050          */
3051         if (retstatus != SNMP_ERR_NOERROR) {
3052             status = retstatus;
3053 	}
3054 
3055         /*
3056          * other things we know less about (no index)
3057          */
3058         /*
3059          * WWW: drop support for this?
3060          */
3061         if (final_status == SNMP_ERR_NOERROR && status != SNMP_ERR_NOERROR) {
3062             /*
3063              * we can't break here, since some processing needs to be
3064              * done for all requests anyway (IE, SET handling for UNDO
3065              * needs to be called regardless of previous status
3066              * results.
3067              * WWW:  This should be predictable though and
3068              * breaking should be possible in some cases (eg GET,
3069              * GETNEXT, ...)
3070              */
3071             final_status = status;
3072         }
3073     }
3074 
3075     return final_status;
3076 }
3077 
3078 void
3079 netsnmp_check_delegated_requests(void)
3080 {
3081     netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
3082 
3083     for (asp = agent_delegated_list; asp; asp = next_asp) {
3084         next_asp = asp->next;   /* save in case we clean up asp */
3085         if (!netsnmp_check_for_delegated(asp)) {
3086 
3087             /*
3088              * we're done with this one, remove from queue
3089              */
3090             if (prev_asp != NULL)
3091                 prev_asp->next = asp->next;
3092             else
3093                 agent_delegated_list = asp->next;
3094             asp->next = NULL;
3095 
3096             /*
3097              * check request status
3098              */
3099             netsnmp_check_all_requests_status(asp, 0);
3100 
3101             /*
3102              * continue processing or finish up
3103              */
3104             check_delayed_request(asp);
3105 
3106             /*
3107              * if head was removed, don't drop it if it
3108              * was it re-queued
3109              */
3110             if ((prev_asp == NULL) && (agent_delegated_list == asp)) {
3111                 prev_asp = asp;
3112             }
3113         } else {
3114 
3115             /*
3116              * asp is still on the queue
3117              */
3118             prev_asp = asp;
3119         }
3120     }
3121 }
3122 
3123 /*
3124  * loop through our sessions known delegated sessions and check to see
3125  * if they've completed yet. If there are no more delegated sessions,
3126  * check for and process any queued requests
3127  */
3128 void
3129 netsnmp_check_outstanding_agent_requests(void)
3130 {
3131     netsnmp_agent_session *asp;
3132 
3133     /*
3134      * deal with delegated requests
3135      */
3136     netsnmp_check_delegated_requests();
3137 
3138     /*
3139      * if we are processing a set and there are more delegated
3140      * requests, keep waiting before getting to queued requests.
3141      */
3142     if (netsnmp_processing_set && (NULL != agent_delegated_list))
3143         return;
3144 
3145     while (netsnmp_agent_queued_list) {
3146 
3147         /*
3148          * if we are processing a set, the first item better be
3149          * the set being (or waiting to be) processed.
3150          */
3151         netsnmp_assert((!netsnmp_processing_set) ||
3152                        (netsnmp_processing_set == netsnmp_agent_queued_list));
3153 
3154         /*
3155          * if the top request is a set, don't pop it
3156          * off if there are delegated requests
3157          */
3158 #ifndef NETSNMP_NO_WRITE_SUPPORT
3159         if ((netsnmp_agent_queued_list->pdu->command == SNMP_MSG_SET) &&
3160             (agent_delegated_list)) {
3161 
3162             netsnmp_assert(netsnmp_processing_set == NULL);
3163 
3164             netsnmp_processing_set = netsnmp_agent_queued_list;
3165             DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
3166                         "delegated requests finish, asp = %8p\n",
3167                         agent_delegated_list));
3168             break;
3169         }
3170 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3171 
3172         /*
3173          * pop the first request and process it
3174          */
3175         asp = netsnmp_agent_queued_list;
3176         netsnmp_agent_queued_list = asp->next;
3177         DEBUGMSGTL(("snmp_agent",
3178                     "processing queued request, asp = %8p\n", asp));
3179 
3180         netsnmp_handle_request(asp, asp->status);
3181 
3182         /*
3183          * if we hit a set, stop
3184          */
3185         if (NULL != netsnmp_processing_set)
3186             break;
3187     }
3188 }
3189 
3190 /** Decide if the requested transaction_id is still being processed
3191    within the agent.  This is used to validate whether a delayed cache
3192    (containing possibly freed pointers) is still usable.
3193 
3194    returns SNMPERR_SUCCESS if it's still valid, or SNMPERR_GENERR if not. */
3195 int
3196 netsnmp_check_transaction_id(int transaction_id)
3197 {
3198     netsnmp_agent_session *asp;
3199 
3200     for (asp = agent_delegated_list; asp; asp = asp->next) {
3201         if (asp->pdu->transid == transaction_id)
3202             return SNMPERR_SUCCESS;
3203     }
3204     return SNMPERR_GENERR;
3205 }
3206 
3207 
3208 /*
3209  * check_delayed_request(asp)
3210  *
3211  * Called to rexamine a set of requests and continue processing them
3212  * once all the previous (delayed) requests have been handled one way
3213  * or another.
3214  */
3215 
3216 int
3217 check_delayed_request(netsnmp_agent_session *asp)
3218 {
3219     int             status = SNMP_ERR_NOERROR;
3220 
3221     DEBUGMSGTL(("snmp_agent", "processing delegated request, asp = %8p\n",
3222                 asp));
3223 
3224     switch (asp->mode) {
3225     case SNMP_MSG_GETBULK:
3226     case SNMP_MSG_GETNEXT:
3227         netsnmp_check_all_requests_status(asp, 0);
3228         if (asp->flags & SNMP_AGENT_FLAGS_CANCEL_IN_PROGRESS) {
3229             DEBUGMSGTL(("snmp_agent","canceling next walk for asp %p\n", asp));
3230             break;
3231         }
3232         handle_getnext_loop(asp);
3233         if (netsnmp_check_for_delegated(asp) &&
3234             netsnmp_check_transaction_id(asp->pdu->transid) !=
3235             SNMPERR_SUCCESS) {
3236             /*
3237              * add to delegated request chain
3238              */
3239             if (!netsnmp_check_delegated_chain_for(asp)) {
3240                 asp->next = agent_delegated_list;
3241                 agent_delegated_list = asp;
3242             }
3243         }
3244         break;
3245 
3246 #ifndef NETSNMP_NO_WRITE_SUPPORT
3247     case MODE_SET_COMMIT:
3248         netsnmp_check_all_requests_status(asp, SNMP_ERR_COMMITFAILED);
3249         goto settop;
3250 
3251     case MODE_SET_UNDO:
3252         netsnmp_check_all_requests_status(asp, SNMP_ERR_UNDOFAILED);
3253         goto settop;
3254 
3255     case MODE_SET_BEGIN:
3256     case MODE_SET_RESERVE1:
3257     case MODE_SET_RESERVE2:
3258     case MODE_SET_ACTION:
3259     case MODE_SET_FREE:
3260       settop:
3261         /* If we should do only one pass, this mean we */
3262         /* should not reenter this function */
3263         if ((asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
3264             /* We should have finished the processing after the first */
3265             /* handle_set_loop, so just wrap up */
3266             break;
3267         }
3268         handle_set_loop(asp);
3269         if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
3270 
3271             if (netsnmp_check_for_delegated_and_add(asp)) {
3272                 /*
3273                  * add to delegated request chain
3274                  */
3275                 if (!asp->status)
3276                     asp->status = status;
3277             }
3278 
3279             return SNMP_ERR_NOERROR;
3280         }
3281         break;
3282 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3283 
3284     default:
3285         break;
3286     }
3287 
3288     /*
3289      * if we don't have anything outstanding (delegated), wrap up
3290      */
3291     if (!netsnmp_check_for_delegated(asp))
3292         return netsnmp_wrap_up_request(asp, status);
3293 
3294     return 1;
3295 }
3296 
3297 /** returns 1 if there are valid GETNEXT requests left.  Returns 0 if not. */
3298 int
3299 check_getnext_results(netsnmp_agent_session *asp)
3300 {
3301     /*
3302      * get old info
3303      */
3304     netsnmp_tree_cache *old_treecache = asp->treecache;
3305     int             old_treecache_num = asp->treecache_num;
3306     int             count = 0;
3307     int             i, special = 0;
3308     netsnmp_request_info *request;
3309 
3310     if (asp->mode == SNMP_MSG_GET) {
3311         /*
3312          * Special case for doing INCLUSIVE getNext operations in
3313          * AgentX subagents.
3314          */
3315         DEBUGMSGTL(("snmp_agent",
3316                     "asp->mode == SNMP_MSG_GET in ch_getnext\n"));
3317         asp->mode = asp->oldmode;
3318         special = 1;
3319     }
3320 
3321     for (i = 0; i <= old_treecache_num; i++) {
3322         for (request = old_treecache[i].requests_begin; request;
3323              request = request->next) {
3324 
3325             /*
3326              * If we have just done the special case AgentX GET, then any
3327              * requests which were not INCLUSIVE will now have a wrong
3328              * response, so junk them and retry from the same place (except
3329              * that this time the handler will be called in "inexact"
3330              * mode).
3331              */
3332 
3333             if (special) {
3334                 if (!request->inclusive) {
3335                     DEBUGMSGTL(("snmp_agent",
3336                                 "request %d wasn't inclusive\n",
3337                                 request->index));
3338                     snmp_set_var_typed_value(request->requestvb,
3339                                              ASN_PRIV_RETRY, NULL, 0);
3340                 } else if (request->requestvb->type == ASN_NULL ||
3341                            request->requestvb->type == SNMP_NOSUCHINSTANCE ||
3342                            request->requestvb->type == SNMP_NOSUCHOBJECT) {
3343                     /*
3344                      * it was inclusive, but no results.  Still retry this
3345                      * search.
3346                      */
3347                     snmp_set_var_typed_value(request->requestvb,
3348                                              ASN_PRIV_RETRY, NULL, 0);
3349                 }
3350             }
3351 
3352             /*
3353              * out of range?
3354              */
3355             if (snmp_oid_compare(request->requestvb->name,
3356                                  request->requestvb->name_length,
3357                                  request->range_end,
3358                                  request->range_end_len) >= 0) {
3359                 /*
3360                  * ack, it's beyond the accepted end of range.
3361                  */
3362                 /*
3363                  * fix it by setting the oid to the end of range oid instead
3364                  */
3365                 DEBUGMSGTL(("check_getnext_results",
3366                             "request response %d out of range\n",
3367                             request->index));
3368                 /*
3369                  * I'm not sure why inclusive is set unconditionally here (see
3370                  * comments for revision 1.161), but it causes a problem for
3371                  * GETBULK over an overridden variable. The bulk-to-next
3372                  * handler re-uses the same request for multiple varbinds,
3373                  * and once inclusive was set, it was never cleared. So, a
3374                  * hack. Instead of setting it to 1, set it to 2, so bulk-to
3375                  * next can clear it later. As of the time of this hack, all
3376                  * checks of this var are boolean checks (not == 1), so this
3377                  * should be safe. Cross your fingers.
3378                  */
3379                 request->inclusive = 2;
3380                 /*
3381                  * XXX: should set this to the original OID?
3382                  */
3383                 snmp_set_var_objid(request->requestvb,
3384                                    request->range_end,
3385                                    request->range_end_len);
3386                 snmp_set_var_typed_value(request->requestvb, ASN_NULL,
3387                                          NULL, 0);
3388             }
3389 
3390             /*
3391              * mark any existent requests with illegal results as NULL
3392              */
3393             if (request->requestvb->type == SNMP_ENDOFMIBVIEW) {
3394                 /*
3395                  * illegal response from a subagent.  Change it back to NULL
3396                  *  xxx-rks: err, how do we know this is a subagent?
3397                  */
3398                 request->requestvb->type = ASN_NULL;
3399                 request->inclusive = 1;
3400             }
3401 
3402             if (request->requestvb->type == ASN_NULL ||
3403                 request->requestvb->type == ASN_PRIV_RETRY ||
3404                 (asp->reqinfo->mode == MODE_GETBULK
3405                  && request->repeat > 0))
3406                 count++;
3407         }
3408     }
3409     return count;
3410 }
3411 
3412 /** repeatedly calls getnext handlers looking for an answer till all
3413    requests are satisified.  It's expected that one pass has been made
3414    before entering this function */
3415 int
3416 handle_getnext_loop(netsnmp_agent_session *asp)
3417 {
3418     int             status, rough_size, count = 0, total, val_len;
3419     netsnmp_variable_list *var_ptr, *last_var = NULL;
3420 
3421     if (NULL == asp || NULL == asp->pdu)
3422         return SNMP_ERR_GENERR;
3423 
3424     total = count_varbinds(asp->pdu->variables);
3425 
3426     /*
3427      * loop
3428      */
3429     while (netsnmp_running) {
3430 
3431         /*
3432          * bail for now if anything is delegated.
3433          */
3434         if (netsnmp_check_for_delegated(asp)) {
3435             return SNMP_ERR_NOERROR;
3436         }
3437 
3438         /*
3439          * check vacm against results
3440          */
3441         check_acm(asp, ASN_PRIV_RETRY);
3442 
3443         /*
3444          * need to keep going we're not done yet.
3445          */
3446         if (!check_getnext_results(asp))
3447             /*
3448              * nothing left, quit now
3449              */
3450             break;
3451 
3452         count = rough_size = 0;
3453         DEBUGMSGTL(("results:intermediate",
3454                     "getnext results, before next pass:\n"));
3455         for (var_ptr = asp->pdu->variables; var_ptr;
3456              var_ptr = var_ptr->next_variable) {
3457             if ((var_ptr->type == ASN_NULL && 0 == var_ptr->name_length) ||
3458                 (var_ptr->type == ASN_PRIV_RETRY)) {
3459                 continue;
3460             }
3461             ++count;
3462             DEBUGIF("results:intermediate") {
3463                 DEBUGMSGTL(("results:intermediate", "\t"));
3464                 DEBUGMSGVAR(("results:intermediate", var_ptr));
3465                 DEBUGMSG(("results:intermediate", "\n"));
3466             }
3467             /*
3468              * make a very rough guesstimate of the encoded varbind size by
3469              * adding the name and val lengths. If these rough sizes add up
3470              * to more than the msgMaxSize, stop gathing new varbinds.
3471              *
3472              * [Increasing the accuracy of this estimate would allow us to
3473              * do better at filling packets and collecting fewer varbinds that
3474              * we'll later have to trim. This is left as an exercise for the
3475              * reader.]
3476              */
3477             rough_size += var_ptr->name_length;
3478 #if (SIZEOF_LONG == 8)
3479             /** sizeof(oid) is 8 on 64bit systems :-( Hardcode for 4 */
3480             if (ASN_OBJECT_ID == var_ptr->type)
3481                 val_len = (var_ptr->val_len / 2);
3482             else
3483 #endif
3484                 val_len = var_ptr->val_len;
3485 
3486             DEBUGMSGTL(("results:intermediate", "\t+ %" NETSNMP_PRIz "d %d = %d\n",
3487                         var_ptr->name_length, val_len, rough_size));
3488             if (rough_size > asp->pdu->msgMaxSize) {
3489                 DEBUGMSGTL(("results",
3490                             "estimating packet too big; stop gathering\n"));
3491                 asp->pdu->flags |= UCD_MSG_FLAG_BULK_TOOBIG |
3492                     UCD_MSG_FLAG_FORWARD_ENCODE;
3493                 var_ptr->type = ASN_PRIV_STOP;
3494                 if (NULL != last_var)
3495                     last_var->next_variable = NULL;
3496                 break;
3497             }
3498             last_var = var_ptr;
3499         }
3500         if (rough_size > asp->pdu->msgMaxSize)
3501             break;
3502 
3503         netsnmp_reassign_requests(asp);
3504         status = handle_var_requests(asp);
3505         if (status != SNMP_ERR_NOERROR) {
3506             return status;      /* should never really happen */
3507         }
3508     }
3509     DEBUGMSGTL(("results:summary", "gathered %d/%d varbinds\n", count,
3510                 total));
3511     if (!netsnmp_running) {
3512         return SNMP_ERR_GENERR;
3513     }
3514     return SNMP_ERR_NOERROR;
3515 }
3516 
3517 #ifndef NETSNMP_NO_WRITE_SUPPORT
3518 int
3519 handle_set(netsnmp_agent_session *asp)
3520 {
3521     int             status;
3522     /*
3523      * SETS require 3-4 passes through the var_op_list.
3524      * The first two
3525      * passes verify that all types, lengths, and values are valid
3526      * and may reserve resources and the third does the set and a
3527      * fourth executes any actions.  Then the identical GET RESPONSE
3528      * packet is returned.
3529      * If either of the first two passes returns an error, another
3530      * pass is made so that any reserved resources can be freed.
3531      * If the third pass returns an error, another pass is
3532      * made so that
3533      * any changes can be reversed.
3534      * If the fourth pass (or any of the error handling passes)
3535      * return an error, we'd rather not know about it!
3536      */
3537     if (!(asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY)) {
3538         switch (asp->mode) {
3539         case MODE_SET_BEGIN:
3540             snmp_increment_statistic(STAT_SNMPINSETREQUESTS);
3541             asp->rw = WRITE;    /* WWW: still needed? */
3542             asp->mode = MODE_SET_RESERVE1;
3543             asp->status = SNMP_ERR_NOERROR;
3544             break;
3545 
3546         case MODE_SET_RESERVE1:
3547 
3548             if (asp->status != SNMP_ERR_NOERROR)
3549                 asp->mode = MODE_SET_FREE;
3550             else
3551                 asp->mode = MODE_SET_RESERVE2;
3552             break;
3553 
3554         case MODE_SET_RESERVE2:
3555             if (asp->status != SNMP_ERR_NOERROR)
3556                 asp->mode = MODE_SET_FREE;
3557             else
3558                 asp->mode = MODE_SET_ACTION;
3559             break;
3560 
3561         case MODE_SET_ACTION:
3562             if (asp->status != SNMP_ERR_NOERROR)
3563                 asp->mode = MODE_SET_UNDO;
3564             else
3565                 asp->mode = MODE_SET_COMMIT;
3566             break;
3567 
3568         case MODE_SET_COMMIT:
3569             if (asp->status != SNMP_ERR_NOERROR) {
3570                 asp->mode = FINISHED_FAILURE;
3571             } else {
3572                 asp->mode = FINISHED_SUCCESS;
3573             }
3574             break;
3575 
3576         case MODE_SET_UNDO:
3577             asp->mode = FINISHED_FAILURE;
3578             break;
3579 
3580         case MODE_SET_FREE:
3581             asp->mode = FINISHED_FAILURE;
3582             break;
3583         }
3584     }
3585 
3586     if (asp->mode != FINISHED_SUCCESS && asp->mode != FINISHED_FAILURE) {
3587         DEBUGMSGTL(("agent_set", "doing set mode = %d (%s)\n", asp->mode,
3588                     se_find_label_in_slist("agent_mode", asp->mode)));
3589         status = handle_var_requests(asp);
3590         DEBUGMSGTL(("agent_set", "did set mode = %d, status = %d\n",
3591                     asp->mode, status));
3592         if ((status != SNMP_ERR_NOERROR && asp->status == SNMP_ERR_NOERROR) ||
3593 	    status == SNMP_ERR_COMMITFAILED ||
3594 	    status == SNMP_ERR_UNDOFAILED) {
3595             asp->status = status;
3596         }
3597     }
3598     return asp->status;
3599 }
3600 
3601 int
3602 handle_set_loop(netsnmp_agent_session *asp)
3603 {
3604     while (asp->mode != FINISHED_FAILURE && asp->mode != FINISHED_SUCCESS) {
3605         handle_set(asp);
3606         if (netsnmp_check_for_delegated(asp)) {
3607             return SNMP_ERR_NOERROR;
3608 	}
3609         if (asp->pdu->flags & UCD_MSG_FLAG_ONE_PASS_ONLY) {
3610             return asp->status;
3611 	}
3612     }
3613     return asp->status;
3614 }
3615 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3616 
3617 int
3618 netsnmp_handle_request(netsnmp_agent_session *asp, int status)
3619 {
3620 #ifndef NETSNMP_NO_PDU_STATS
3621     if (_pdu_stats_max > 0) {
3622         /** tag new pdus with a start time */
3623         marker_t start;
3624         start = (marker_t)netsnmp_agent_get_list_data(asp->reqinfo,
3625                                                       "netsnmp_pdu_stats");
3626         if (NULL == start) {
3627             netsnmp_data_list *data_list;
3628             DEBUGMSGTL(("stats:pdu:start", "starting pdu processing\n"));
3629 
3630             netsnmp_set_monotonic_marker(&start); /* will alloc space */
3631             data_list = netsnmp_create_data_list("netsnmp_pdu_stats", start,
3632                                                  free);
3633             if (NULL == data_list) {
3634                 free(start);
3635                 snmp_log(LOG_WARNING, "error creating data list for stats\n");
3636             } else
3637                 netsnmp_agent_add_list_data(asp->reqinfo, data_list);
3638         }
3639     }
3640 #endif /* NETSNMP_NO_PDU_STATS */
3641 
3642     /*
3643      * if this isn't a delegated request trying to finish,
3644      * processing of a set request should not start until all
3645      * delegated requests have completed, and no other new requests
3646      * should be processed until the set request completes.
3647      */
3648     if ((0 == netsnmp_check_delegated_chain_for(asp)) &&
3649         (asp != netsnmp_processing_set)) {
3650         /*
3651          * if we are processing a set and this is not a delegated
3652          * request, queue the request
3653          */
3654         if (netsnmp_processing_set) {
3655             netsnmp_add_queued(asp);
3656             DEBUGMSGTL(("snmp_agent",
3657                         "request queued while processing set, "
3658                         "asp = %8p\n", asp));
3659             return 1;
3660         }
3661 
3662         /*
3663          * check for set request
3664          */
3665 #ifndef NETSNMP_NO_WRITE_SUPPORT
3666         if (asp->pdu->command == SNMP_MSG_SET) {
3667             netsnmp_processing_set = asp;
3668 
3669             /*
3670              * if there are delegated requests, we must wait for them
3671              * to finish.
3672              */
3673             if (agent_delegated_list) {
3674                 DEBUGMSGTL(("snmp_agent", "SET request queued while "
3675                             "delegated requests finish, asp = %8p\n",
3676                             asp));
3677                 netsnmp_add_queued(asp);
3678                 return 1;
3679             }
3680         }
3681 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3682     }
3683 
3684     /*
3685      * process the request
3686      */
3687     status = handle_pdu(asp);
3688 
3689     /*
3690      * print the results in appropriate debugging mode
3691      */
3692     DEBUGIF("results") {
3693         netsnmp_variable_list *var_ptr;
3694         DEBUGMSGTL(("results", "request results (status = %d):\n",
3695                     status));
3696         for (var_ptr = asp->pdu->variables; var_ptr;
3697              var_ptr = var_ptr->next_variable) {
3698             DEBUGMSGTL(("results", "\t"));
3699             DEBUGMSGVAR(("results", var_ptr));
3700             DEBUGMSG(("results", "\n"));
3701         }
3702     }
3703 
3704     /*
3705      * check for uncompleted requests
3706      */
3707     if (netsnmp_check_for_delegated_and_add(asp)) {
3708         /*
3709          * add to delegated request chain
3710          */
3711         asp->status = status;
3712     } else {
3713         /*
3714          * if we don't have anything outstanding (delegated), wrap up
3715          */
3716         return netsnmp_wrap_up_request(asp, status);
3717     }
3718 
3719     return 1;
3720 }
3721 
3722 int
3723 handle_pdu(netsnmp_agent_session *asp)
3724 {
3725     int             status, inclusives = 0;
3726     netsnmp_variable_list *v = NULL;
3727 
3728     /*
3729      * for illegal requests, mark all nodes as ASN_NULL
3730      */
3731     switch (asp->pdu->command) {
3732 
3733 #ifndef NETSNMP_NO_WRITE_SUPPORT
3734     case SNMP_MSG_INTERNAL_SET_RESERVE2:
3735     case SNMP_MSG_INTERNAL_SET_ACTION:
3736     case SNMP_MSG_INTERNAL_SET_COMMIT:
3737     case SNMP_MSG_INTERNAL_SET_FREE:
3738     case SNMP_MSG_INTERNAL_SET_UNDO:
3739         status = get_set_cache(asp);
3740         if (status != SNMP_ERR_NOERROR)
3741             return status;
3742         break;
3743 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3744 
3745     case SNMP_MSG_GET:
3746     case SNMP_MSG_GETNEXT:
3747     case SNMP_MSG_GETBULK:
3748         for (v = asp->pdu->variables; v != NULL; v = v->next_variable) {
3749             if (v->type == ASN_PRIV_INCL_RANGE) {
3750                 /*
3751                  * Leave the type for now (it gets set to
3752                  * ASN_NULL in netsnmp_add_varbind_to_cache,
3753                  * called by netsnmp_create_subtree_cache below).
3754                  * If we set it to ASN_NULL now, we wouldn't be
3755                  * able to distinguish INCLUSIVE search
3756                  * ranges.
3757                  */
3758                 inclusives++;
3759             } else {
3760                 snmp_set_var_typed_value(v, ASN_NULL, NULL, 0);
3761             }
3762         }
3763         /* FALL THROUGH */
3764 
3765     default:
3766 #ifndef NETSNMP_NO_WRITE_SUPPORT
3767     case SNMP_MSG_INTERNAL_SET_BEGIN:
3768     case SNMP_MSG_INTERNAL_SET_RESERVE1:
3769 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3770         asp->vbcount = count_varbinds(asp->pdu->variables);
3771         asp->requests = calloc(asp->vbcount, sizeof(netsnmp_request_info));
3772         /*
3773          * collect varbinds
3774          */
3775         status = netsnmp_create_subtree_cache(asp);
3776         if (status != SNMP_ERR_NOERROR)
3777             return status;
3778     }
3779 
3780     asp->mode = asp->pdu->command;
3781     switch (asp->mode) {
3782     case SNMP_MSG_GET:
3783         /*
3784          * increment the message type counter
3785          */
3786         snmp_increment_statistic(STAT_SNMPINGETREQUESTS);
3787 
3788         /*
3789          * check vacm ahead of time
3790          */
3791         check_acm(asp, SNMP_NOSUCHOBJECT);
3792 
3793         /*
3794          * get the results
3795          */
3796         status = handle_var_requests(asp);
3797 
3798         /*
3799          * Deal with unhandled results -> noSuchInstance (rather
3800          * than noSuchObject -- in that case, the type will
3801          * already have been set to noSuchObject when we realised
3802          * we couldn't find an appropriate tree).
3803          */
3804         if (status == SNMP_ERR_NOERROR)
3805             snmp_replace_var_types(asp->pdu->variables, ASN_NULL,
3806                                    SNMP_NOSUCHINSTANCE);
3807         break;
3808 
3809     case SNMP_MSG_GETNEXT:
3810         snmp_increment_statistic(STAT_SNMPINGETNEXTS);
3811         /* FALL THROUGH */
3812 
3813     case SNMP_MSG_GETBULK:     /* note: there is no getbulk stat */
3814         /*
3815          * loop through our mib tree till we find an
3816          * appropriate response to return to the caller.
3817          */
3818 
3819         if (inclusives) {
3820             /*
3821              * This is a special case for AgentX INCLUSIVE getNext
3822              * requests where a result lexi-equal to the request is okay
3823              * but if such a result does not exist, we still want the
3824              * lexi-next one.  So basically we do a GET first, and if any
3825              * of the INCLUSIVE requests are satisfied, we use that
3826              * value.  Then, unsatisfied INCLUSIVE requests, and
3827              * non-INCLUSIVE requests get done as normal.
3828              */
3829 
3830             DEBUGMSGTL(("snmp_agent", "inclusive range(s) in getNext\n"));
3831             asp->oldmode = asp->mode;
3832             asp->mode = SNMP_MSG_GET;
3833         }
3834 
3835         /*
3836          * first pass
3837          */
3838         status = handle_var_requests(asp);
3839         if (status != SNMP_ERR_NOERROR) {
3840             if (!inclusives)
3841                 return status;  /* should never really happen */
3842             else
3843                 asp->status = SNMP_ERR_NOERROR;
3844         }
3845 
3846         /*
3847          * loop through our mib tree till we find an
3848          * appropriate response to return to the caller.
3849          */
3850 
3851         status = handle_getnext_loop(asp);
3852         break;
3853 
3854 #ifndef NETSNMP_NO_WRITE_SUPPORT
3855     case SNMP_MSG_SET:
3856 #ifdef NETSNMP_DISABLE_SET_SUPPORT
3857         return SNMP_ERR_NOTWRITABLE;
3858 #else
3859         /*
3860          * check access permissions first
3861          */
3862         if (check_acm(asp, SNMP_NOSUCHOBJECT))
3863             return SNMP_ERR_NOTWRITABLE;
3864 
3865         asp->mode = MODE_SET_BEGIN;
3866         status = handle_set_loop(asp);
3867 #endif
3868         break;
3869 
3870     case SNMP_MSG_INTERNAL_SET_BEGIN:
3871     case SNMP_MSG_INTERNAL_SET_RESERVE1:
3872     case SNMP_MSG_INTERNAL_SET_RESERVE2:
3873     case SNMP_MSG_INTERNAL_SET_ACTION:
3874     case SNMP_MSG_INTERNAL_SET_COMMIT:
3875     case SNMP_MSG_INTERNAL_SET_FREE:
3876     case SNMP_MSG_INTERNAL_SET_UNDO:
3877         asp->pdu->flags |= UCD_MSG_FLAG_ONE_PASS_ONLY;
3878         status = handle_set_loop(asp);
3879         /*
3880          * asp related cache is saved in cleanup
3881          */
3882         break;
3883 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3884 
3885     case SNMP_MSG_RESPONSE:
3886         snmp_increment_statistic(STAT_SNMPINGETRESPONSES);
3887         return SNMP_ERR_NOERROR;
3888 
3889     case SNMP_MSG_TRAP:
3890     case SNMP_MSG_TRAP2:
3891         snmp_increment_statistic(STAT_SNMPINTRAPS);
3892         return SNMP_ERR_NOERROR;
3893 
3894     default:
3895         /*
3896          * WWW: are reports counted somewhere ?
3897          */
3898         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
3899         return SNMPERR_GENERR;  /* shouldn't get here */
3900         /*
3901          * WWW
3902          */
3903     }
3904     return status;
3905 }
3906 
3907 /** set error for a request
3908  * \internal external interface: netsnmp_request_set_error
3909  */
3910 NETSNMP_STATIC_INLINE int
3911 _request_set_error(netsnmp_request_info *request, int mode, int error_value)
3912 {
3913     if (!request)
3914         return SNMPERR_NO_VARS;
3915 
3916     request->processed = 1;
3917     request->delegated = REQUEST_IS_NOT_DELEGATED;
3918 
3919     switch (error_value) {
3920     case SNMP_NOSUCHOBJECT:
3921     case SNMP_NOSUCHINSTANCE:
3922     case SNMP_ENDOFMIBVIEW:
3923         /*
3924          * these are exceptions that should be put in the varbind
3925          * in the case of a GET but should be translated for a SET
3926          * into a real error status code and put in the request
3927          */
3928         switch (mode) {
3929         case MODE_GET:
3930         case MODE_GETNEXT:
3931         case MODE_GETBULK:
3932             request->requestvb->type = error_value;
3933             break;
3934 
3935             /*
3936              * These are technically illegal to set by the
3937              * client APIs for these modes.  But accepting
3938              * them here allows the 'sparse_table' helper to
3939              * provide some common table handling processing
3940              *
3941             snmp_log(LOG_ERR, "Illegal error_value %d for mode %d ignored\n",
3942                      error_value, mode);
3943             return SNMPERR_VALUE;
3944              */
3945 
3946 #ifndef NETSNMP_NO_WRITE_SUPPORT
3947         case SNMP_MSG_INTERNAL_SET_RESERVE1:
3948             request->status = SNMP_ERR_NOCREATION;
3949             break;
3950 #endif /* NETSNMP_NO_WRITE_SUPPORT */
3951 
3952         default:
3953             request->status = SNMP_ERR_NOSUCHNAME;      /* WWW: correct? */
3954             break;
3955         }
3956         break;                  /* never get here */
3957 
3958     default:
3959         if (error_value < 0) {
3960             /*
3961              * illegal local error code.  translate to generr
3962              */
3963             /*
3964              * WWW: full translation map?
3965              */
3966             snmp_log(LOG_ERR, "Illegal error_value %d translated to %d\n",
3967                      error_value, SNMP_ERR_GENERR);
3968             request->status = SNMP_ERR_GENERR;
3969         } else {
3970             /*
3971              * WWW: translations and mode checking?
3972              */
3973             request->status = error_value;
3974         }
3975         break;
3976     }
3977     return SNMPERR_SUCCESS;
3978 }
3979 
3980 /** set error for a request
3981  * @param request request which has error
3982  * @param error_value error value for request
3983  */
3984 int
3985 netsnmp_request_set_error(netsnmp_request_info *request, int error_value)
3986 {
3987     if (!request || !request->agent_req_info)
3988         return SNMPERR_NO_VARS;
3989 
3990     return _request_set_error(request, request->agent_req_info->mode,
3991                               error_value);
3992 }
3993 
3994 #ifndef NETSNMP_FEATURE_REMOVE_REQUEST_SET_ERROR_IDX
3995 /** set error for a request within a request list
3996  * @param request head of the request list
3997  * @param error_value error value for request
3998  * @param idx index of the request which has the error
3999  */
4000 int
4001 netsnmp_request_set_error_idx(netsnmp_request_info *request,
4002                               int error_value, int idx)
4003 {
4004     int i;
4005     netsnmp_request_info *req = request;
4006 
4007     if (!request || !request->agent_req_info)
4008         return SNMPERR_NO_VARS;
4009 
4010     /*
4011      * Skip to the indicated varbind
4012      */
4013     for ( i=2; i<idx; i++) {
4014         req = req->next;
4015         if (!req)
4016             return SNMPERR_NO_VARS;
4017     }
4018 
4019     return _request_set_error(req, request->agent_req_info->mode,
4020                               error_value);
4021 }
4022 #endif /* NETSNMP_FEATURE_REMOVE_REQUEST_SET_ERROR_IDX */
4023 
4024 /** set error for all requests
4025  * @param requests request list
4026  * @param error error value for requests
4027  * @return SNMPERR_SUCCESS, or an error code
4028  */
4029 NETSNMP_INLINE int
4030 netsnmp_request_set_error_all( netsnmp_request_info *requests, int error)
4031 {
4032     int mode, rc, result = SNMPERR_SUCCESS;
4033 
4034     if((NULL == requests) || (NULL == requests->agent_req_info))
4035         return SNMPERR_NO_VARS;
4036 
4037     mode = requests->agent_req_info->mode; /* every req has same mode */
4038 
4039     for(; requests ; requests = requests->next) {
4040 
4041         /** paranoid sanity checks */
4042         netsnmp_assert(NULL != requests->agent_req_info);
4043         netsnmp_assert(mode == requests->agent_req_info->mode);
4044 
4045         /*
4046          * set error for this request. Log any errors, save the last
4047          * to return to the user.
4048          */
4049         if((rc = _request_set_error(requests, mode, error))) {
4050             snmp_log(LOG_WARNING,"got %d while setting request error\n", rc);
4051             result = rc;
4052         }
4053     }
4054     return result;
4055 }
4056 
4057 /**
4058  * Return the difference between pm and the agent start time in hundredths of
4059  * a second.
4060  * \deprecated Don't use in new code.
4061  *
4062  * @param[in] pm An absolute time as e.g. reported by gettimeofday().
4063  */
4064 u_long
4065 netsnmp_marker_uptime(marker_t pm)
4066 {
4067     u_long          res;
4068     const_marker_t  start = netsnmp_get_agent_starttime();
4069 
4070     res = uatime_hdiff(start, pm);
4071     return res;
4072 }
4073 
4074 /**
4075  * Return the difference between tv and the agent start time in hundredths of
4076  * a second.
4077  *
4078  * \deprecated Use netsnmp_get_agent_uptime() instead.
4079  *
4080  * @param[in] tv An absolute time as e.g. reported by gettimeofday().
4081  */
4082 u_long
4083 netsnmp_timeval_uptime(struct timeval * tv)
4084 {
4085     return netsnmp_marker_uptime((marker_t) tv);
4086 }
4087 
4088 
4089 struct timeval  starttime;
4090 static struct timeval starttimeM;
4091 
4092 /**
4093  * Return a pointer to the variable in which the Net-SNMP start time has
4094  * been stored.
4095  *
4096  * @note Use netsnmp_get_agent_runtime() instead of this function if you need
4097  *   to know how much time elapsed since netsnmp_set_agent_starttime() has been
4098  *   called.
4099  */
4100 const_marker_t
4101 netsnmp_get_agent_starttime(void)
4102 {
4103     return &starttime;
4104 }
4105 
4106 /**
4107  * Report the time that elapsed since the agent start time in hundredths of a
4108  * second.
4109  *
4110  * @see See also netsnmp_set_agent_starttime().
4111  */
4112 uint64_t
4113 netsnmp_get_agent_runtime(void)
4114 {
4115     struct timeval now, delta;
4116 
4117     netsnmp_get_monotonic_clock(&now);
4118     NETSNMP_TIMERSUB(&now, &starttimeM, &delta);
4119     return (uint64_t)(delta.tv_sec * (uint64_t)100 + delta.tv_usec / 10000);
4120 }
4121 
4122 /**
4123  * Set the time at which Net-SNMP started either to the current time
4124  * (if s == NULL) or to *s (if s is not NULL).
4125  *
4126  * @see See also netsnmp_set_agent_uptime().
4127  */
4128 void
4129 netsnmp_set_agent_starttime(marker_t s)
4130 {
4131     if (s) {
4132         struct timeval nowA, nowM;
4133 
4134         starttime = *(struct timeval*)s;
4135         gettimeofday(&nowA, NULL);
4136         netsnmp_get_monotonic_clock(&nowM);
4137         NETSNMP_TIMERSUB(&starttime, &nowA, &starttimeM);
4138         NETSNMP_TIMERADD(&starttimeM, &nowM, &starttimeM);
4139     } else {
4140         gettimeofday(&starttime, NULL);
4141         netsnmp_get_monotonic_clock(&starttimeM);
4142     }
4143 }
4144 
4145 
4146 /**
4147  * Return the current value of 'sysUpTime'
4148  */
4149 u_long
4150 netsnmp_get_agent_uptime(void)
4151 {
4152     struct timeval now, delta;
4153 
4154     netsnmp_get_monotonic_clock(&now);
4155     NETSNMP_TIMERSUB(&now, &starttimeM, &delta);
4156     return (u_long)(delta.tv_sec * 100UL + delta.tv_usec / 10000);
4157 }
4158 
4159 #ifndef NETSNMP_FEATURE_REMOVE_SET_AGENT_UPTIME
4160 /**
4161  * Set the start time from which 'sysUpTime' is computed.
4162  *
4163  * @param[in] hsec New sysUpTime in hundredths of a second.
4164  *
4165  * @see See also netsnmp_set_agent_starttime().
4166  */
4167 void
4168 netsnmp_set_agent_uptime(u_long hsec)
4169 {
4170     struct timeval  nowA, nowM;
4171     struct timeval  new_uptime;
4172 
4173     gettimeofday(&nowA, NULL);
4174     netsnmp_get_monotonic_clock(&nowM);
4175     new_uptime.tv_sec = hsec / 100;
4176     new_uptime.tv_usec = (uint32_t)(hsec - new_uptime.tv_sec * 100) * 10000L;
4177     NETSNMP_TIMERSUB(&nowA, &new_uptime, &starttime);
4178     NETSNMP_TIMERSUB(&nowM, &new_uptime, &starttimeM);
4179 }
4180 #endif /* NETSNMP_FEATURE_REMOVE_SET_AGENT_UPTIME */
4181 
4182 
4183 /*************************************************************************
4184  *
4185  * deprecated functions
4186  *
4187  */
4188 
4189 /** set error for a request
4190  * \deprecated, use netsnmp_request_set_error instead
4191  * @param reqinfo agent_request_info pointer for request
4192  * @param request request_info pointer
4193  * @param error_value error value for requests
4194  * @return error_value
4195  */
4196 int
4197 netsnmp_set_request_error(netsnmp_agent_request_info *reqinfo,
4198                           netsnmp_request_info *request, int error_value)
4199 {
4200     if (!request || !reqinfo)
4201         return error_value;
4202 
4203     _request_set_error(request, reqinfo->mode, error_value);
4204 
4205     return error_value;
4206 }
4207 
4208 /** set error for a request
4209  * \deprecated, use netsnmp_request_set_error instead
4210  * @param mode Net-SNMP agent processing mode
4211  * @param request request_info pointer
4212  * @param error_value error value for requests
4213  * @return error_value
4214  */
4215 int
4216 netsnmp_set_mode_request_error(int mode, netsnmp_request_info *request,
4217                                int error_value)
4218 {
4219     _request_set_error(request, mode, error_value);
4220 
4221     return error_value;
4222 }
4223 
4224 /** set error for all request
4225  * \deprecated use netsnmp_request_set_error_all
4226  * @param reqinfo agent_request_info pointer for requests
4227  * @param requests request list
4228  * @param error_value error value for requests
4229  * @return error_value
4230  */
4231 #ifndef NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR
4232 int
4233 netsnmp_set_all_requests_error(netsnmp_agent_request_info *reqinfo,
4234                                netsnmp_request_info *requests,
4235                                int error_value)
4236 {
4237     netsnmp_request_set_error_all(requests, error_value);
4238     return error_value;
4239 }
4240 #endif /* NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR */
4241 /** @} */
4242