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