1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "checks_snmp.h"
21 
22 #ifdef HAVE_NETSNMP
23 
24 #define SNMP_NO_DEBUGGING		/* disabling debugging messages from Net-SNMP library */
25 #include <net-snmp/net-snmp-config.h>
26 #include <net-snmp/net-snmp-includes.h>
27 
28 #include "comms.h"
29 #include "zbxalgo.h"
30 #include "zbxjson.h"
31 
32 /*
33  * SNMP Dynamic Index Cache
34  * ========================
35  *
36  * Description
37  * -----------
38  *
39  * Zabbix caches the whole index table for the particular OID separately based on:
40  *   * IP address;
41  *   * port;
42  *   * community string (SNMPv2c);
43  *   * context, security name (SNMPv3).
44  *
45  * Zabbix revalidates each index before using it to get a value and rebuilds the index cache for the OID if the
46  * index is invalid.
47  *
48  * Example
49  * -------
50  *
51  * OID for getting memory usage of process by PID (index):
52  *   HOST-RESOURCES-MIB::hrSWRunPerfMem:<PID>
53  *
54  * OID for getting PID (index) by process name (value):
55  *   HOST-RESOURCES-MIB::hrSWRunPath:<PID> <NAME>
56  *
57  * SNMP OID as configured in Zabbix to get memory usage of "snmpd" process:
58  *   HOST-RESOURCES-MIB::hrSWRunPerfMem["index","HOST-RESOURCES-MIB::hrSWRunPath","snmpd"]
59  *
60  * 1. Zabbix walks hrSWRunPath table and caches all <PID> and <NAME> pairs of particular SNMP agent/user.
61  * 2. Before each GET request Zabbix revalidates the cached <PID> by getting its <NAME> from hrSWRunPath table.
62  * 3. If the names match then Zabbix uses the cached <PID> in the GET request for the hrSWRunPerfMem.
63  *    Otherwise Zabbix rebuilds the hrSWRunPath cache for the particular agent/user (see 1.).
64  *
65  * Implementation
66  * --------------
67  *
68  * The cache is implemented using hash tables. In ERD:
69  * zbx_snmpidx_main_key_t -------------------------------------------0< zbx_snmpidx_mapping_t
70  * (OID, host, <v2c: community|v3: (context, security name)>)           (index, value)
71  */
72 
73 /******************************************************************************
74  *                                                                            *
75  * This is zbx_snmp_walk() callback function prototype.                       *
76  *                                                                            *
77  * Parameters: arg   - [IN] an user argument passed to zbx_snmp_walk()        *
78  *                          function                                          *
79  *             snmp_oid - [IN] the OID the walk function is looking for       *
80  *             index    - [IN] the index of found OID                         *
81  *             value    - [IN] the OID value                                  *
82  *                                                                            *
83  ******************************************************************************/
84 typedef void (zbx_snmp_walk_cb_func)(void *arg, const char *snmp_oid, const char *index, const char *value);
85 
86 typedef struct
87 {
88 	char		*addr;
89 	unsigned short	port;
90 	char		*oid;
91 	char		*community_context;	/* community (SNMPv1 or v2c) or contextName (SNMPv3) */
92 	char		*security_name;		/* only SNMPv3, empty string in case of other versions */
93 	zbx_hashset_t	*mappings;
94 }
95 zbx_snmpidx_main_key_t;
96 
97 typedef struct
98 {
99 	char		*value;
100 	char		*index;
101 }
102 zbx_snmpidx_mapping_t;
103 
104 static zbx_hashset_t	snmpidx;		/* Dynamic Index Cache */
105 static char		zbx_snmp_init_done;
106 
__snmpidx_main_key_hash(const void * data)107 static zbx_hash_t	__snmpidx_main_key_hash(const void *data)
108 {
109 	const zbx_snmpidx_main_key_t	*main_key = (const zbx_snmpidx_main_key_t *)data;
110 
111 	zbx_hash_t			hash;
112 
113 	hash = ZBX_DEFAULT_STRING_HASH_FUNC(main_key->addr);
114 	hash = ZBX_DEFAULT_STRING_HASH_ALGO(&main_key->port, sizeof(main_key->port), hash);
115 	hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->oid, strlen(main_key->oid), hash);
116 	hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->community_context, strlen(main_key->community_context), hash);
117 	hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->security_name, strlen(main_key->security_name), hash);
118 
119 	return hash;
120 }
121 
__snmpidx_main_key_compare(const void * d1,const void * d2)122 static int	__snmpidx_main_key_compare(const void *d1, const void *d2)
123 {
124 	const zbx_snmpidx_main_key_t	*main_key1 = (const zbx_snmpidx_main_key_t *)d1;
125 	const zbx_snmpidx_main_key_t	*main_key2 = (const zbx_snmpidx_main_key_t *)d2;
126 
127 	int				ret;
128 
129 	if (0 != (ret = strcmp(main_key1->addr, main_key2->addr)))
130 		return ret;
131 
132 	ZBX_RETURN_IF_NOT_EQUAL(main_key1->port, main_key2->port);
133 
134 	if (0 != (ret = strcmp(main_key1->community_context, main_key2->community_context)))
135 		return ret;
136 
137 	if (0 != (ret = strcmp(main_key1->security_name, main_key2->security_name)))
138 		return ret;
139 
140 	return strcmp(main_key1->oid, main_key2->oid);
141 }
142 
__snmpidx_main_key_clean(void * data)143 static void	__snmpidx_main_key_clean(void *data)
144 {
145 	zbx_snmpidx_main_key_t	*main_key = (zbx_snmpidx_main_key_t *)data;
146 
147 	zbx_free(main_key->addr);
148 	zbx_free(main_key->oid);
149 	zbx_free(main_key->community_context);
150 	zbx_free(main_key->security_name);
151 	zbx_hashset_destroy(main_key->mappings);
152 	zbx_free(main_key->mappings);
153 }
154 
__snmpidx_mapping_hash(const void * data)155 static zbx_hash_t	__snmpidx_mapping_hash(const void *data)
156 {
157 	const zbx_snmpidx_mapping_t	*mapping = (const zbx_snmpidx_mapping_t *)data;
158 
159 	return ZBX_DEFAULT_STRING_HASH_FUNC(mapping->value);
160 }
161 
__snmpidx_mapping_compare(const void * d1,const void * d2)162 static int	__snmpidx_mapping_compare(const void *d1, const void *d2)
163 {
164 	const zbx_snmpidx_mapping_t	*mapping1 = (const zbx_snmpidx_mapping_t *)d1;
165 	const zbx_snmpidx_mapping_t	*mapping2 = (const zbx_snmpidx_mapping_t *)d2;
166 
167 	return strcmp(mapping1->value, mapping2->value);
168 }
169 
__snmpidx_mapping_clean(void * data)170 static void	__snmpidx_mapping_clean(void *data)
171 {
172 	zbx_snmpidx_mapping_t	*mapping = (zbx_snmpidx_mapping_t *)data;
173 
174 	zbx_free(mapping->value);
175 	zbx_free(mapping->index);
176 }
177 
get_item_community_context(const DC_ITEM * item)178 static char	*get_item_community_context(const DC_ITEM *item)
179 {
180 	if (ZBX_IF_SNMP_VERSION_1 == item->snmp_version || ZBX_IF_SNMP_VERSION_2 == item->snmp_version)
181 		return item->snmp_community;
182 	else if (ZBX_IF_SNMP_VERSION_3 == item->snmp_version)
183 		return item->snmpv3_contextname;
184 
185 	THIS_SHOULD_NEVER_HAPPEN;
186 	exit(EXIT_FAILURE);
187 }
188 
get_item_security_name(const DC_ITEM * item)189 static char	*get_item_security_name(const DC_ITEM *item)
190 {
191 	if (ZBX_IF_SNMP_VERSION_3 == item->snmp_version)
192 		return item->snmpv3_securityname;
193 
194 	return "";
195 }
196 
197 /******************************************************************************
198  *                                                                            *
199  * Function: cache_get_snmp_index                                             *
200  *                                                                            *
201  * Purpose: retrieve index that matches value from the relevant index cache   *
202  *                                                                            *
203  * Parameters: item      - [IN] configuration of Zabbix item, contains        *
204  *                              IP address, port, community string, context,  *
205  *                              security name                                 *
206  *             snmp_oid  - [IN] OID of the table which contains the indexes   *
207  *             value     - [IN] value for which to look up the index          *
208  *             idx       - [IN/OUT] destination pointer for the               *
209  *                                  heap-(re)allocated index                  *
210  *             idx_alloc - [IN/OUT] size of the (re)allocated index           *
211  *                                                                            *
212  * Return value: FAIL    - dynamic index cache is empty or cache does not     *
213  *                         contain index matching the value                   *
214  *               SUCCEED - idx contains the found index,                      *
215  *                         idx_alloc contains the current size of the         *
216  *                         heap-(re)allocated idx                             *
217  *                                                                            *
218  ******************************************************************************/
cache_get_snmp_index(const DC_ITEM * item,const char * snmp_oid,const char * value,char ** idx,size_t * idx_alloc)219 static int	cache_get_snmp_index(const DC_ITEM *item, const char *snmp_oid, const char *value, char **idx, size_t *idx_alloc)
220 {
221 	int			ret = FAIL;
222 	zbx_snmpidx_main_key_t	*main_key, main_key_local;
223 	zbx_snmpidx_mapping_t	*mapping;
224 	size_t			idx_offset = 0;
225 
226 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s' value:'%s'", __func__, snmp_oid, value);
227 
228 	if (NULL == snmpidx.slots)
229 		goto end;
230 
231 	main_key_local.addr = item->interface.addr;
232 	main_key_local.port = item->interface.port;
233 	main_key_local.oid = (char *)snmp_oid;
234 
235 	main_key_local.community_context = get_item_community_context(item);
236 	main_key_local.security_name = get_item_security_name(item);
237 
238 	if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
239 		goto end;
240 
241 	if (NULL == (mapping = (zbx_snmpidx_mapping_t *)zbx_hashset_search(main_key->mappings, &value)))
242 		goto end;
243 
244 	zbx_strcpy_alloc(idx, idx_alloc, &idx_offset, mapping->index);
245 	ret = SUCCEED;
246 end:
247 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s idx:'%s'", __func__, zbx_result_string(ret),
248 			SUCCEED == ret ? *idx : "");
249 
250 	return ret;
251 }
252 
253 /******************************************************************************
254  *                                                                            *
255  * Function: cache_put_snmp_index                                             *
256  *                                                                            *
257  * Purpose: store the index-value pair in the relevant index cache            *
258  *                                                                            *
259  * Parameters: item      - [IN] configuration of Zabbix item, contains        *
260  *                              IP address, port, community string, context,  *
261  *                              security name                                 *
262  *             snmp_oid  - [IN] OID of the table which contains the indexes   *
263  *             index     - [IN] index part of the index-value pair            *
264  *             value     - [IN] value part of the index-value pair            *
265  *                                                                            *
266  ******************************************************************************/
cache_put_snmp_index(const DC_ITEM * item,const char * snmp_oid,const char * index,const char * value)267 static void	cache_put_snmp_index(const DC_ITEM *item, const char *snmp_oid, const char *index, const char *value)
268 {
269 	zbx_snmpidx_main_key_t	*main_key, main_key_local;
270 	zbx_snmpidx_mapping_t	*mapping, mapping_local;
271 
272 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s' index:'%s' value:'%s'", __func__, snmp_oid, index, value);
273 
274 	if (NULL == snmpidx.slots)
275 	{
276 		zbx_hashset_create_ext(&snmpidx, 100,
277 				__snmpidx_main_key_hash, __snmpidx_main_key_compare, __snmpidx_main_key_clean,
278 				ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC);
279 	}
280 
281 	main_key_local.addr = item->interface.addr;
282 	main_key_local.port = item->interface.port;
283 	main_key_local.oid = (char *)snmp_oid;
284 
285 	main_key_local.community_context = get_item_community_context(item);
286 	main_key_local.security_name = get_item_security_name(item);
287 
288 	if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
289 	{
290 		main_key_local.addr = zbx_strdup(NULL, item->interface.addr);
291 		main_key_local.oid = zbx_strdup(NULL, snmp_oid);
292 
293 		main_key_local.community_context = zbx_strdup(NULL, get_item_community_context(item));
294 		main_key_local.security_name = zbx_strdup(NULL, get_item_security_name(item));
295 
296 		main_key_local.mappings = (zbx_hashset_t *)zbx_malloc(NULL, sizeof(zbx_hashset_t));
297 		zbx_hashset_create_ext(main_key_local.mappings, 100,
298 				__snmpidx_mapping_hash, __snmpidx_mapping_compare, __snmpidx_mapping_clean,
299 				ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC);
300 
301 		main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_insert(&snmpidx, &main_key_local, sizeof(main_key_local));
302 	}
303 
304 	if (NULL == (mapping = (zbx_snmpidx_mapping_t *)zbx_hashset_search(main_key->mappings, &value)))
305 	{
306 		mapping_local.value = zbx_strdup(NULL, value);
307 		mapping_local.index = zbx_strdup(NULL, index);
308 
309 		zbx_hashset_insert(main_key->mappings, &mapping_local, sizeof(mapping_local));
310 	}
311 	else if (0 != strcmp(mapping->index, index))
312 	{
313 		zbx_free(mapping->index);
314 		mapping->index = zbx_strdup(NULL, index);
315 	}
316 
317 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
318 }
319 
320 /******************************************************************************
321  *                                                                            *
322  * Function: cache_del_snmp_index_subtree                                     *
323  *                                                                            *
324  * Purpose: delete index-value mappings from the specified index cache        *
325  *                                                                            *
326  * Parameters: item      - [IN] configuration of Zabbix item, contains        *
327  *                              IP address, port, community string, context,  *
328  *                              security name                                 *
329  *             snmp_oid  - [IN] OID of the table which contains the indexes   *
330  *                                                                            *
331  * Comments: does nothing if the index cache is empty or if it does not       *
332  *           contain the cache for the specified OID                          *
333  *                                                                            *
334  ******************************************************************************/
cache_del_snmp_index_subtree(const DC_ITEM * item,const char * snmp_oid)335 static void	cache_del_snmp_index_subtree(const DC_ITEM *item, const char *snmp_oid)
336 {
337 	zbx_snmpidx_main_key_t	*main_key, main_key_local;
338 
339 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s'", __func__, snmp_oid);
340 
341 	if (NULL == snmpidx.slots)
342 		goto end;
343 
344 	main_key_local.addr = item->interface.addr;
345 	main_key_local.port = item->interface.port;
346 	main_key_local.oid = (char *)snmp_oid;
347 
348 	main_key_local.community_context = get_item_community_context(item);
349 	main_key_local.security_name = get_item_security_name(item);
350 
351 	if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
352 		goto end;
353 
354 	zbx_hashset_clear(main_key->mappings);
355 end:
356 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
357 }
358 
zbx_snmpv3_set_auth_protocol(const DC_ITEM * item,struct snmp_session * session)359 static int	zbx_snmpv3_set_auth_protocol(const DC_ITEM *item, struct snmp_session *session)
360 {
361 	int	ret = SUCCEED;
362 
363 	switch (item->snmpv3_authprotocol)
364 	{
365 		case ITEM_SNMPV3_AUTHPROTOCOL_MD5:
366 			session->securityAuthProto = usmHMACMD5AuthProtocol;
367 			session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
368 			break;
369 		case ITEM_SNMPV3_AUTHPROTOCOL_SHA1:
370 			session->securityAuthProto = usmHMACSHA1AuthProtocol;
371 			session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
372 			break;
373 #ifdef HAVE_NETSNMP_STRONG_AUTH
374 		case ITEM_SNMPV3_AUTHPROTOCOL_SHA224:
375 			session->securityAuthProto = usmHMAC128SHA224AuthProtocol;
376 			session->securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol);
377 			break;
378 		case ITEM_SNMPV3_AUTHPROTOCOL_SHA256:
379 			session->securityAuthProto = usmHMAC192SHA256AuthProtocol;
380 			session->securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol);
381 			break;
382 		case ITEM_SNMPV3_AUTHPROTOCOL_SHA384:
383 			session->securityAuthProto = usmHMAC256SHA384AuthProtocol;
384 			session->securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol);
385 			break;
386 		case ITEM_SNMPV3_AUTHPROTOCOL_SHA512:
387 			session->securityAuthProto = usmHMAC384SHA512AuthProtocol;
388 			session->securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol);
389 			break;
390 #endif
391 		default:
392 			ret = FAIL;
393 	}
394 
395 	return ret;
396 }
397 
zbx_get_snmp_type_error(u_char type)398 static char	*zbx_get_snmp_type_error(u_char type)
399 {
400 	switch (type)
401 	{
402 		case SNMP_NOSUCHOBJECT:
403 			return zbx_strdup(NULL, "No Such Object available on this agent at this OID");
404 		case SNMP_NOSUCHINSTANCE:
405 			return zbx_strdup(NULL, "No Such Instance currently exists at this OID");
406 		case SNMP_ENDOFMIBVIEW:
407 			return zbx_strdup(NULL, "No more variables left in this MIB View"
408 					" (it is past the end of the MIB tree)");
409 		default:
410 			return zbx_dsprintf(NULL, "Value has unknown type 0x%02X", (unsigned int)type);
411 	}
412 }
413 
zbx_get_snmp_response_error(const struct snmp_session * ss,const DC_INTERFACE * interface,int status,const struct snmp_pdu * response,char * error,size_t max_error_len)414 static int	zbx_get_snmp_response_error(const struct snmp_session *ss, const DC_INTERFACE *interface, int status,
415 		const struct snmp_pdu *response, char *error, size_t max_error_len)
416 {
417 	int	ret;
418 
419 	if (STAT_SUCCESS == status)
420 	{
421 		zbx_snprintf(error, max_error_len, "SNMP error: %s", snmp_errstring(response->errstat));
422 		ret = NOTSUPPORTED;
423 	}
424 	else if (STAT_ERROR == status)
425 	{
426 		zbx_snprintf(error, max_error_len, "Cannot connect to \"%s:%hu\": %s.",
427 				interface->addr, interface->port, snmp_api_errstring(ss->s_snmp_errno));
428 
429 		switch (ss->s_snmp_errno)
430 		{
431 			case SNMPERR_UNKNOWN_USER_NAME:
432 			case SNMPERR_UNSUPPORTED_SEC_LEVEL:
433 			case SNMPERR_AUTHENTICATION_FAILURE:
434 				ret = NOTSUPPORTED;
435 				break;
436 			default:
437 				ret = NETWORK_ERROR;
438 		}
439 	}
440 	else if (STAT_TIMEOUT == status)
441 	{
442 		zbx_snprintf(error, max_error_len, "Timeout while connecting to \"%s:%hu\".",
443 				interface->addr, interface->port);
444 		ret = NETWORK_ERROR;
445 	}
446 	else
447 	{
448 		zbx_snprintf(error, max_error_len, "SNMP error: [%d]", status);
449 		ret = NOTSUPPORTED;
450 	}
451 
452 	return ret;
453 }
454 
zbx_snmp_open_session(const DC_ITEM * item,char * error,size_t max_error_len)455 static struct snmp_session	*zbx_snmp_open_session(const DC_ITEM *item, char *error, size_t max_error_len)
456 {
457 	struct snmp_session	session, *ss = NULL;
458 	char			addr[128];
459 #ifdef HAVE_IPV6
460 	int			family;
461 #endif
462 
463 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
464 
465 	snmp_sess_init(&session);
466 
467 	/* Allow using sub-OIDs higher than MAX_INT, like in 'snmpwalk -Ir'. */
468 	/* Disables the validation of varbind values against the MIB definition for the relevant OID. */
469 	if (SNMPERR_SUCCESS != netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE, 1))
470 	{
471 		/* This error is not fatal and should never happen (see netsnmp_ds_set_boolean() implementation). */
472 		/* Only items with sub-OIDs higher than MAX_INT will be unsupported. */
473 		zabbix_log(LOG_LEVEL_WARNING, "cannot set \"DontCheckRange\" option for Net-SNMP");
474 	}
475 
476 	switch (item->snmp_version)
477 	{
478 		case ZBX_IF_SNMP_VERSION_1:
479 			session.version = SNMP_VERSION_1;
480 			break;
481 		case ZBX_IF_SNMP_VERSION_2:
482 			session.version = SNMP_VERSION_2c;
483 			break;
484 		case ZBX_IF_SNMP_VERSION_3:
485 			session.version = SNMP_VERSION_3;
486 			break;
487 		default:
488 			THIS_SHOULD_NEVER_HAPPEN;
489 			break;
490 	}
491 
492 	session.timeout = CONFIG_TIMEOUT * 1000 * 1000;	/* timeout of one attempt in microseconds */
493 							/* (net-snmp default = 1 second) */
494 
495 #ifdef HAVE_IPV6
496 	if (SUCCEED != get_address_family(item->interface.addr, &family, error, max_error_len))
497 		goto end;
498 
499 	if (PF_INET == family)
500 	{
501 		zbx_snprintf(addr, sizeof(addr), "%s:%hu", item->interface.addr, item->interface.port);
502 	}
503 	else
504 	{
505 		if (item->interface.useip)
506 			zbx_snprintf(addr, sizeof(addr), "udp6:[%s]:%hu", item->interface.addr, item->interface.port);
507 		else
508 			zbx_snprintf(addr, sizeof(addr), "udp6:%s:%hu", item->interface.addr, item->interface.port);
509 	}
510 #else
511 	zbx_snprintf(addr, sizeof(addr), "%s:%hu", item->interface.addr, item->interface.port);
512 #endif
513 	session.peername = addr;
514 
515 	if (SNMP_VERSION_1 == session.version || SNMP_VERSION_2c == session.version)
516 	{
517 		session.community = (u_char *)item->snmp_community;
518 		session.community_len = strlen((char *)session.community);
519 		zabbix_log(LOG_LEVEL_DEBUG, "SNMP [%s@%s]", session.community, session.peername);
520 	}
521 	else if (SNMP_VERSION_3 == session.version)
522 	{
523 		/* set the SNMPv3 user name */
524 		session.securityName = item->snmpv3_securityname;
525 		session.securityNameLen = strlen(session.securityName);
526 
527 		/* set the SNMPv3 context if specified */
528 		if ('\0' != *item->snmpv3_contextname)
529 		{
530 			session.contextName = item->snmpv3_contextname;
531 			session.contextNameLen = strlen(session.contextName);
532 		}
533 
534 		/* set the security level to authenticated, but not encrypted */
535 		switch (item->snmpv3_securitylevel)
536 		{
537 			case ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV:
538 				session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
539 				break;
540 			case ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV:
541 				session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
542 
543 				if (FAIL == zbx_snmpv3_set_auth_protocol(item, &session))
544 				{
545 					zbx_snprintf(error, max_error_len, "Unsupported authentication protocol [%d]",
546 							item->snmpv3_authprotocol);
547 					goto end;
548 				}
549 
550 				session.securityAuthKeyLen = USM_AUTH_KU_LEN;
551 
552 				if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
553 						session.securityAuthProtoLen, (u_char *)item->snmpv3_authpassphrase,
554 						strlen(item->snmpv3_authpassphrase), session.securityAuthKey,
555 						&session.securityAuthKeyLen))
556 				{
557 					zbx_strlcpy(error, "Error generating Ku from authentication pass phrase",
558 							max_error_len);
559 					goto end;
560 				}
561 				break;
562 			case ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV:
563 				session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
564 
565 				if (FAIL == zbx_snmpv3_set_auth_protocol(item, &session))
566 				{
567 					zbx_snprintf(error, max_error_len, "Unsupported authentication protocol [%d]",
568 							item->snmpv3_authprotocol);
569 					goto end;
570 				}
571 
572 				session.securityAuthKeyLen = USM_AUTH_KU_LEN;
573 
574 				if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
575 						session.securityAuthProtoLen, (u_char *)item->snmpv3_authpassphrase,
576 						strlen(item->snmpv3_authpassphrase), session.securityAuthKey,
577 						&session.securityAuthKeyLen))
578 				{
579 					zbx_strlcpy(error, "Error generating Ku from authentication pass phrase",
580 							max_error_len);
581 					goto end;
582 				}
583 
584 				switch (item->snmpv3_privprotocol)
585 				{
586 #ifdef HAVE_NETSNMP_SESSION_DES
587 					case ITEM_SNMPV3_PRIVPROTOCOL_DES:
588 						/* set the privacy protocol to DES */
589 						session.securityPrivProto = usmDESPrivProtocol;
590 						session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
591 						break;
592 #endif
593 					case ITEM_SNMPV3_PRIVPROTOCOL_AES128:
594 						/* set the privacy protocol to AES128 */
595 						session.securityPrivProto = usmAESPrivProtocol;
596 						session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
597 						break;
598 #ifdef HAVE_NETSNMP_STRONG_PRIV
599 					case ITEM_SNMPV3_PRIVPROTOCOL_AES192:
600 						/* set the privacy protocol to AES192 */
601 						session.securityPrivProto = usmAES192PrivProtocol;
602 						session.securityPrivProtoLen = OID_LENGTH(usmAES192PrivProtocol);
603 						break;
604 					case ITEM_SNMPV3_PRIVPROTOCOL_AES256:
605 						/* set the privacy protocol to AES256 */
606 						session.securityPrivProto = usmAES256PrivProtocol;
607 						session.securityPrivProtoLen = OID_LENGTH(usmAES256PrivProtocol);
608 						break;
609 					case ITEM_SNMPV3_PRIVPROTOCOL_AES192C:
610 						/* set the privacy protocol to AES192 (Cisco version) */
611 						session.securityPrivProto = usmAES192CiscoPrivProtocol;
612 						session.securityPrivProtoLen = OID_LENGTH(usmAES192CiscoPrivProtocol);
613 						break;
614 					case ITEM_SNMPV3_PRIVPROTOCOL_AES256C:
615 						/* set the privacy protocol to AES256 (Cisco version) */
616 						session.securityPrivProto = usmAES256CiscoPrivProtocol;
617 						session.securityPrivProtoLen = OID_LENGTH(usmAES256CiscoPrivProtocol);
618 						break;
619 #endif
620 					default:
621 						zbx_snprintf(error, max_error_len,
622 								"Unsupported privacy protocol [%d]",
623 								item->snmpv3_privprotocol);
624 						goto end;
625 				}
626 
627 				session.securityPrivKeyLen = USM_PRIV_KU_LEN;
628 
629 				if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
630 						session.securityAuthProtoLen, (u_char *)item->snmpv3_privpassphrase,
631 						strlen(item->snmpv3_privpassphrase), session.securityPrivKey,
632 						&session.securityPrivKeyLen))
633 				{
634 					zbx_strlcpy(error, "Error generating Ku from privacy pass phrase",
635 							max_error_len);
636 					goto end;
637 				}
638 				break;
639 		}
640 
641 		zabbix_log(LOG_LEVEL_DEBUG, "SNMPv3 [%s@%s]", session.securityName, session.peername);
642 	}
643 
644 #ifdef HAVE_NETSNMP_SESSION_LOCALNAME
645 	if (NULL != CONFIG_SOURCE_IP)
646 	{
647 		/* In some cases specifying just local host (without local port) is not enough. We do */
648 		/* not care about the port number though so we let the OS select one by specifying 0. */
649 		/* See marc.info/?l=net-snmp-bugs&m=115624676507760 for details. */
650 
651 		static char	localname[64];
652 
653 		zbx_snprintf(localname, sizeof(localname), "%s:0", CONFIG_SOURCE_IP);
654 		session.localname = localname;
655 	}
656 #endif
657 
658 	SOCK_STARTUP;
659 
660 	if (NULL == (ss = snmp_open(&session)))
661 	{
662 		SOCK_CLEANUP;
663 
664 		zbx_strlcpy(error, "Cannot open SNMP session", max_error_len);
665 	}
666 end:
667 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
668 
669 	return ss;
670 }
671 
zbx_snmp_close_session(struct snmp_session * session)672 static void	zbx_snmp_close_session(struct snmp_session *session)
673 {
674 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
675 
676 	snmp_close(session);
677 	SOCK_CLEANUP;
678 
679 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
680 }
681 
zbx_snmp_get_octet_string(const struct variable_list * var,unsigned char * string_type)682 static char	*zbx_snmp_get_octet_string(const struct variable_list *var, unsigned char *string_type)
683 {
684 	const char	*hint;
685 	char		buffer[MAX_BUFFER_LEN];
686 	char		*strval_dyn = NULL;
687 	struct tree	*subtree;
688 	unsigned char	type;
689 
690 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
691 
692 	/* find the subtree to get display hint */
693 	subtree = get_tree(var->name, var->name_length, get_tree_head());
694 	hint = (NULL != subtree ? subtree->hint : NULL);
695 
696 	/* we will decide if we want the value from var->val or what snprint_value() returned later */
697 	if (-1 == snprint_value(buffer, sizeof(buffer), var->name, var->name_length, var))
698 		goto end;
699 
700 	zabbix_log(LOG_LEVEL_DEBUG, "%s() full value:'%s' hint:'%s'", __func__, buffer, ZBX_NULL2STR(hint));
701 
702 	if (0 == strncmp(buffer, "Hex-STRING: ", 12))
703 	{
704 		strval_dyn = zbx_strdup(strval_dyn, buffer + 12);
705 		type = ZBX_SNMP_STR_HEX;
706 	}
707 	else if (NULL != hint && 0 == strncmp(buffer, "STRING: ", 8))
708 	{
709 		strval_dyn = zbx_strdup(strval_dyn, buffer + 8);
710 		type = ZBX_SNMP_STR_STRING;
711 	}
712 	else if (0 == strncmp(buffer, "OID: ", 5))
713 	{
714 		strval_dyn = zbx_strdup(strval_dyn, buffer + 5);
715 		type = ZBX_SNMP_STR_OID;
716 	}
717 	else if (0 == strncmp(buffer, "BITS: ", 6))
718 	{
719 		strval_dyn = zbx_strdup(strval_dyn, buffer + 6);
720 		type = ZBX_SNMP_STR_BITS;
721 	}
722 	else
723 	{
724 		/* snprint_value() escapes hintless ASCII strings, so */
725 		/* we are copying the raw unescaped value in this case */
726 
727 		strval_dyn = (char *)zbx_malloc(strval_dyn, var->val_len + 1);
728 		memcpy(strval_dyn, var->val.string, var->val_len);
729 		strval_dyn[var->val_len] = '\0';
730 		type = ZBX_SNMP_STR_ASCII;
731 	}
732 
733 	if (NULL != string_type)
734 		*string_type = type;
735 end:
736 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():'%s'", __func__, ZBX_NULL2STR(strval_dyn));
737 
738 	return strval_dyn;
739 }
740 
zbx_snmp_set_result(const struct variable_list * var,AGENT_RESULT * result,unsigned char * string_type)741 static int	zbx_snmp_set_result(const struct variable_list *var, AGENT_RESULT *result, unsigned char *string_type)
742 {
743 	char		*strval_dyn;
744 	int		ret = SUCCEED;
745 
746 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() type:%d", __func__, (int)var->type);
747 
748 	*string_type = ZBX_SNMP_STR_UNDEFINED;
749 
750 	if (ASN_OCTET_STR == var->type || ASN_OBJECT_ID == var->type)
751 	{
752 		if (NULL == (strval_dyn = zbx_snmp_get_octet_string(var, string_type)))
753 		{
754 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot receive string value: out of memory."));
755 			ret = NOTSUPPORTED;
756 		}
757 		else
758 		{
759 			set_result_type(result, ITEM_VALUE_TYPE_TEXT, strval_dyn);
760 			zbx_free(strval_dyn);
761 		}
762 	}
763 #ifdef OPAQUE_SPECIAL_TYPES
764 	else if (ASN_UINTEGER == var->type || ASN_COUNTER == var->type || ASN_OPAQUE_U64 == var->type ||
765 			ASN_TIMETICKS == var->type || ASN_GAUGE == var->type)
766 #else
767 	else if (ASN_UINTEGER == var->type || ASN_COUNTER == var->type ||
768 			ASN_TIMETICKS == var->type || ASN_GAUGE == var->type)
769 #endif
770 	{
771 		SET_UI64_RESULT(result, (unsigned long)*var->val.integer);
772 	}
773 #ifdef OPAQUE_SPECIAL_TYPES
774 	else if (ASN_COUNTER64 == var->type || ASN_OPAQUE_COUNTER64 == var->type)
775 #else
776 	else if (ASN_COUNTER64 == var->type)
777 #endif
778 	{
779 		SET_UI64_RESULT(result, (((zbx_uint64_t)var->val.counter64->high) << 32) +
780 				(zbx_uint64_t)var->val.counter64->low);
781 	}
782 #ifdef OPAQUE_SPECIAL_TYPES
783 	else if (ASN_INTEGER == var->type || ASN_OPAQUE_I64 == var->type)
784 #else
785 	else if (ASN_INTEGER == var->type)
786 #endif
787 	{
788 		char	buffer[21];
789 
790 		zbx_snprintf(buffer, sizeof(buffer), "%ld", *var->val.integer);
791 
792 		set_result_type(result, ITEM_VALUE_TYPE_TEXT, buffer);
793 	}
794 #ifdef OPAQUE_SPECIAL_TYPES
795 	else if (ASN_OPAQUE_FLOAT == var->type)
796 	{
797 		SET_DBL_RESULT(result, *var->val.floatVal);
798 	}
799 	else if (ASN_OPAQUE_DOUBLE == var->type)
800 	{
801 		SET_DBL_RESULT(result, *var->val.doubleVal);
802 	}
803 #endif
804 	else if (ASN_IPADDRESS == var->type)
805 	{
806 		SET_STR_RESULT(result, zbx_dsprintf(NULL, "%u.%u.%u.%u",
807 				(unsigned int)var->val.string[0],
808 				(unsigned int)var->val.string[1],
809 				(unsigned int)var->val.string[2],
810 				(unsigned int)var->val.string[3]));
811 	}
812 	else
813 	{
814 		SET_MSG_RESULT(result, zbx_get_snmp_type_error(var->type));
815 		ret = NOTSUPPORTED;
816 	}
817 
818 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
819 
820 	return ret;
821 }
822 
zbx_snmp_dump_oid(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len)823 static void	zbx_snmp_dump_oid(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len)
824 {
825 	size_t	i, offset = 0;
826 
827 	*buffer = '\0';
828 
829 	for (i = 0; i < objid_len; i++)
830 		offset += zbx_snprintf(buffer + offset, buffer_len - offset, ".%lu", (unsigned long)objid[i]);
831 }
832 
833 #define ZBX_OID_INDEX_STRING	0
834 #define ZBX_OID_INDEX_NUMERIC	1
835 
zbx_snmp_print_oid(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len,int format)836 static int	zbx_snmp_print_oid(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len, int format)
837 {
838 	if (SNMPERR_SUCCESS != netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS,
839 			format))
840 	{
841 		zabbix_log(LOG_LEVEL_WARNING, "cannot set \"dontBreakdownOids\" option to %d for Net-SNMP", format);
842 		return -1;
843 	}
844 
845 	return snprint_objid(buffer, buffer_len, objid, objid_len);
846 }
847 
zbx_snmp_choose_index(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len,size_t root_string_len,size_t root_numeric_len)848 static int	zbx_snmp_choose_index(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len,
849 		size_t root_string_len, size_t root_numeric_len)
850 {
851 	oid	parsed_oid[MAX_OID_LEN];
852 	size_t	parsed_oid_len = MAX_OID_LEN;
853 	char	printed_oid[MAX_STRING_LEN];
854 	char	*printed_oid_escaped;
855 
856 	/**************************************************************************************************************/
857 	/*                                                                                                            */
858 	/* When we are providing a value for {#SNMPINDEX}, we would like to provide a pretty value. This is only a    */
859 	/* concern for OIDs with string indices. For instance, suppose we are walking the following OID:              */
860 	/*                                                                                                            */
861 	/*   SNMP-VIEW-BASED-ACM-MIB::vacmGroupName                                                                   */
862 	/*                                                                                                            */
863 	/* Suppose also that we are currently looking at this OID:                                                    */
864 	/*                                                                                                            */
865 	/*   SNMP-VIEW-BASED-ACM-MIB::vacmGroupName.3."authOnlyUser"                                                  */
866 	/*                                                                                                            */
867 	/* Then, we would like to provide {#SNMPINDEX} with this value:                                               */
868 	/*                                                                                                            */
869 	/*   3."authOnlyUser"                                                                                         */
870 	/*                                                                                                            */
871 	/* An alternative approach would be to provide {#SNMPINDEX} with numeric value. While it is equivalent to the */
872 	/* string representation above, the string representation is more readable and thus more useful to users:     */
873 	/*                                                                                                            */
874 	/*   3.12.97.117.116.104.79.110.108.121.85.115.101.114                                                        */
875 	/*                                                                                                            */
876 	/* Here, 12 is the length of "authOnlyUser" and the rest is the string encoding using ASCII characters.       */
877 	/*                                                                                                            */
878 	/* There are two problems with always providing {#SNMPINDEX} that has an index representation as a string.    */
879 	/*                                                                                                            */
880 	/* The first problem is indices of type InetAddress. The Net-SNMP library has code for pretty-printing IP     */
881 	/* addresses, but no way to parse them back. As an example, consider the following OID:                       */
882 	/*                                                                                                            */
883 	/*   .1.3.6.1.2.1.4.34.1.4.1.4.192.168.3.255                                                                  */
884 	/*                                                                                                            */
885 	/* Its pretty representation is like this:                                                                    */
886 	/*                                                                                                            */
887 	/*   IP-MIB::ipAddressType.ipv4."192.168.3.255"                                                               */
888 	/*                                                                                                            */
889 	/* However, when trying to parse it, it turns into this OID:                                                  */
890 	/*                                                                                                            */
891 	/*   .1.3.6.1.2.1.4.34.1.4.1.13.49.57.50.46.49.54.56.46.51.46.50.53.53                                        */
892 	/*                                                                                                            */
893 	/* Apparently, this is different than the original.                                                           */
894 	/*                                                                                                            */
895 	/* The second problem is indices of type OCTET STRING, which might contain unprintable characters:            */
896 	/*                                                                                                            */
897 	/*   1.3.6.1.2.1.17.4.3.1.1.0.0.240.122.113.21                                                                */
898 	/*                                                                                                            */
899 	/* Its pretty representation is like this (note the single quotes which stand for a fixed-length string):     */
900 	/*                                                                                                            */
901 	/*   BRIDGE-MIB::dot1dTpFdbAddress.'...zq.'                                                                   */
902 	/*                                                                                                            */
903 	/* Here, '...zq.' stands for 0.0.240.122.113.21, where only 'z' (122) and 'q' (113) are printable.            */
904 	/*                                                                                                            */
905 	/* Apparently, this cannot be turned back into the numeric representation.                                    */
906 	/*                                                                                                            */
907 	/* So what we try to do is first print it pretty. If there is no string-looking index, return it as output.   */
908 	/* If there is such an index, we check that it can be parsed and that the result is the same as the original. */
909 	/*                                                                                                            */
910 	/**************************************************************************************************************/
911 
912 	if (-1 == zbx_snmp_print_oid(printed_oid, sizeof(printed_oid), objid, objid_len, ZBX_OID_INDEX_STRING))
913 	{
914 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot print OID with string indices", __func__);
915 		goto numeric;
916 	}
917 
918 	if (NULL == strchr(printed_oid, '"') && NULL == strchr(printed_oid, '\''))
919 	{
920 		zbx_strlcpy(buffer, printed_oid + root_string_len + 1, buffer_len);
921 		return SUCCEED;
922 	}
923 
924 	printed_oid_escaped = zbx_dyn_escape_string(printed_oid, "\\");
925 
926 	if (NULL == snmp_parse_oid(printed_oid_escaped, parsed_oid, &parsed_oid_len))
927 	{
928 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot parse OID '%s'", __func__, printed_oid_escaped);
929 		zbx_free(printed_oid_escaped);
930 		goto numeric;
931 	}
932 	zbx_free(printed_oid_escaped);
933 
934 	if (parsed_oid_len == objid_len && 0 == memcmp(parsed_oid, objid, parsed_oid_len * sizeof(oid)))
935 	{
936 		zbx_strlcpy(buffer, printed_oid + root_string_len + 1, buffer_len);
937 		return SUCCEED;
938 	}
939 numeric:
940 	if (-1 == zbx_snmp_print_oid(printed_oid, sizeof(printed_oid), objid, objid_len, ZBX_OID_INDEX_NUMERIC))
941 	{
942 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot print OID with numeric indices", __func__);
943 		return FAIL;
944 	}
945 
946 	zbx_strlcpy(buffer, printed_oid + root_numeric_len + 1, buffer_len);
947 	return SUCCEED;
948 }
949 
950 /******************************************************************************
951  *                                                                            *
952  * Functions for detecting looping in SNMP OID sequence using hashset         *
953  *                                                                            *
954  * Once there is a possibility of looping we start putting OIDs into hashset. *
955  * We do it until a duplicate OID shows up or ZBX_OIDS_MAX_NUM OIDs have been *
956  * collected.                                                                 *
957  *                                                                            *
958  * The hashset key is array of elements of type 'oid'. Element 0 holds the    *
959  * number of OID components (sub-OIDs), element 1 and so on - OID components  *
960  * themselves.                                                                *
961  *                                                                            *
962  * OIDs may contain up to 128 sub-OIDs, so 1 byte is sufficient to keep the   *
963  * number of them. On the other hand, sub-OIDs are of type 'oid' which can be *
964  * defined in NetSNMP as 'uint8_t' or 'u_long'. Sub-OIDs are compared as      *
965  * numbers, so some platforms may require they to be properly aligned in      *
966  * memory. To ensure proper alignment we keep number of elements in element 0 *
967  * instead of using a separate structure element for it.                      *
968  *                                                                            *
969  ******************************************************************************/
970 
__oids_seen_key_hash(const void * data)971 static zbx_hash_t	__oids_seen_key_hash(const void *data)
972 {
973 	const oid	*key = (const oid *)data;
974 
975 	return ZBX_DEFAULT_HASH_ALGO(key, (key[0] + 1) * sizeof(oid), ZBX_DEFAULT_HASH_SEED);
976 }
977 
__oids_seen_key_compare(const void * d1,const void * d2)978 static int	__oids_seen_key_compare(const void *d1, const void *d2)
979 {
980 	const oid	*k1 = (const oid *)d1;
981 	const oid	*k2 = (const oid *)d2;
982 
983 	if (d1 == d2)
984 		return 0;
985 
986 	return snmp_oid_compare(k1 + 1, k1[0], k2 + 1, k2[0]);
987 }
988 
zbx_detect_loop_init(zbx_hashset_t * hs)989 static void	zbx_detect_loop_init(zbx_hashset_t *hs)
990 {
991 #define ZBX_OIDS_SEEN_INIT_SIZE	500		/* minimum initial number of slots in hashset */
992 
993 	zbx_hashset_create(hs, ZBX_OIDS_SEEN_INIT_SIZE, __oids_seen_key_hash, __oids_seen_key_compare);
994 
995 #undef ZBX_OIDS_SEEN_INIT_SIZE
996 }
997 
zbx_oid_is_new(zbx_hashset_t * hs,size_t root_len,const oid * p_oid,size_t oid_len)998 static int	zbx_oid_is_new(zbx_hashset_t *hs, size_t root_len, const oid *p_oid, size_t oid_len)
999 {
1000 #define ZBX_OIDS_MAX_NUM	1000000		/* max number of OIDs to store for checking duplicates */
1001 
1002 	const oid	*var_oid;		/* points to the first element in the variable part */
1003 	size_t		var_len;		/* number of elements in the variable part */
1004 	oid		oid_k[MAX_OID_LEN + 1];	/* array for constructing a hashset key */
1005 
1006 	/* OIDs share a common initial part. Save space by storing only the variable part. */
1007 
1008 	var_oid = p_oid + root_len;
1009 	var_len = oid_len - root_len;
1010 
1011 	if (ZBX_OIDS_MAX_NUM == hs->num_data)
1012 		return FAIL;
1013 
1014 	oid_k[0] = var_len;
1015 	memcpy(oid_k + 1, var_oid, var_len * sizeof(oid));
1016 
1017 	if (NULL != zbx_hashset_search(hs, oid_k))
1018 		return FAIL;					/* OID already seen */
1019 
1020 	if (NULL != zbx_hashset_insert(hs, oid_k, (var_len + 1) * sizeof(oid)))
1021 		return SUCCEED;					/* new OID */
1022 
1023 	THIS_SHOULD_NEVER_HAPPEN;
1024 	return FAIL;						/* hashset fail */
1025 
1026 #undef ZBX_OIDS_MAX_NUM
1027 }
1028 
1029 /******************************************************************************
1030  *                                                                            *
1031  * Function: zbx_snmp_walk                                                    *
1032  *                                                                            *
1033  * Purpose: retrieve information by walking an OID tree                       *
1034  *                                                                            *
1035  * Parameters: ss            - [IN] SNMP session handle                       *
1036  *             item          - [IN] configuration of Zabbix item              *
1037  *             OID           - [IN] OID of table with values of interest      *
1038  *             error         - [OUT] a buffer to store error message          *
1039  *             max_error_len - [IN] maximum error message length              *
1040  *             max_succeed   - [OUT] value of "max_repetitions" that succeeded*
1041  *             min_fail      - [OUT] value of "max_repetitions" that failed   *
1042  *             max_vars      - [IN] suggested value of "max_repetitions"      *
1043  *             bulk          - [IN] whether GetBulkRequest-PDU should be used *
1044  *             walk_cb_func  - [IN] callback function to process discovered   *
1045  *                                  OIDs and their values                     *
1046  *             walk_cb_arg   - [IN] argument to pass to the callback function *
1047  *                                                                            *
1048  * Return value: NOTSUPPORTED - OID does not exist, any other critical error  *
1049  *               NETWORK_ERROR - recoverable network error                    *
1050  *               CONFIG_ERROR - item configuration error                      *
1051  *               SUCCEED - if function successfully completed                 *
1052  *                                                                            *
1053  * Author: Alexander Vladishev, Aleksandrs Saveljevs                          *
1054  *                                                                            *
1055  ******************************************************************************/
zbx_snmp_walk(struct snmp_session * ss,const DC_ITEM * item,const char * snmp_oid,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int max_vars,int bulk,zbx_snmp_walk_cb_func walk_cb_func,void * walk_cb_arg)1056 static int	zbx_snmp_walk(struct snmp_session *ss, const DC_ITEM *item, const char *snmp_oid, char *error,
1057 		size_t max_error_len, int *max_succeed, int *min_fail, int max_vars, int bulk,
1058 		zbx_snmp_walk_cb_func walk_cb_func, void *walk_cb_arg)
1059 {
1060 	struct snmp_pdu		*pdu, *response;
1061 	oid			anOID[MAX_OID_LEN], rootOID[MAX_OID_LEN];
1062 	size_t			anOID_len = MAX_OID_LEN, rootOID_len = MAX_OID_LEN, root_string_len, root_numeric_len;
1063 	char			oid_index[MAX_STRING_LEN];
1064 	struct variable_list	*var;
1065 	int			status, level, running, num_vars, check_oid_increase = 1, ret = SUCCEED;
1066 	AGENT_RESULT		snmp_result;
1067 	zbx_hashset_t		oids_seen;
1068 
1069 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() type:%d OID:'%s' bulk:%d", __func__, (int)item->type, snmp_oid, bulk);
1070 
1071 	if (ZBX_IF_SNMP_VERSION_1 == item->snmp_version)	/* GetBulkRequest-PDU available since SNMPv2 */
1072 		bulk = SNMP_BULK_DISABLED;
1073 
1074 	/* create OID from string */
1075 	if (NULL == snmp_parse_oid(snmp_oid, rootOID, &rootOID_len))
1076 	{
1077 		zbx_snprintf(error, max_error_len, "snmp_parse_oid(): cannot parse OID \"%s\".", snmp_oid);
1078 		ret = CONFIG_ERROR;
1079 		goto out;
1080 	}
1081 
1082 	if (-1 == zbx_snmp_print_oid(oid_index, sizeof(oid_index), rootOID, rootOID_len, ZBX_OID_INDEX_STRING))
1083 	{
1084 		zbx_snprintf(error, max_error_len, "zbx_snmp_print_oid(): cannot print OID \"%s\" with string indices.",
1085 				snmp_oid);
1086 		ret = CONFIG_ERROR;
1087 		goto out;
1088 	}
1089 
1090 	root_string_len = strlen(oid_index);
1091 
1092 	if (-1 == zbx_snmp_print_oid(oid_index, sizeof(oid_index), rootOID, rootOID_len, ZBX_OID_INDEX_NUMERIC))
1093 	{
1094 		zbx_snprintf(error, max_error_len, "zbx_snmp_print_oid(): cannot print OID \"%s\""
1095 				" with numeric indices.", snmp_oid);
1096 		ret = CONFIG_ERROR;
1097 		goto out;
1098 	}
1099 
1100 	root_numeric_len = strlen(oid_index);
1101 
1102 	/* copy rootOID to anOID */
1103 	memcpy(anOID, rootOID, rootOID_len * sizeof(oid));
1104 	anOID_len = rootOID_len;
1105 
1106 	/* initialize variables */
1107 	level = 0;
1108 	running = 1;
1109 
1110 	while (1 == running)
1111 	{
1112 		/* create PDU */
1113 		if (NULL == (pdu = snmp_pdu_create(SNMP_BULK_ENABLED == bulk ? SNMP_MSG_GETBULK : SNMP_MSG_GETNEXT)))
1114 		{
1115 			zbx_strlcpy(error, "snmp_pdu_create(): cannot create PDU object.", max_error_len);
1116 			ret = CONFIG_ERROR;
1117 			break;
1118 		}
1119 
1120 		if (NULL == snmp_add_null_var(pdu, anOID, anOID_len))	/* add OID as variable to PDU */
1121 		{
1122 			zbx_strlcpy(error, "snmp_add_null_var(): cannot add null variable.", max_error_len);
1123 			ret = CONFIG_ERROR;
1124 			snmp_free_pdu(pdu);
1125 			break;
1126 		}
1127 
1128 		if (SNMP_BULK_ENABLED == bulk)
1129 		{
1130 			pdu->non_repeaters = 0;
1131 			pdu->max_repetitions = max_vars;
1132 		}
1133 
1134 		ss->retries = (0 == bulk || (1 == max_vars && 0 == level) ? 1 : 0);
1135 
1136 		/* communicate with agent */
1137 		status = snmp_synch_response(ss, pdu, &response);
1138 
1139 		zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d s_snmp_errno:%d errstat:%ld"
1140 				" max_vars:%d", __func__, status, ss->s_snmp_errno,
1141 				NULL == response ? (long)-1 : response->errstat, max_vars);
1142 
1143 		if (1 < max_vars &&
1144 			((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status))
1145 		{
1146 			/* The logic of iteratively reducing request size here is the same as in function */
1147 			/* zbx_snmp_get_values(). Please refer to the description there for explanation.  */
1148 
1149 			if (*min_fail > max_vars)
1150 				*min_fail = max_vars;
1151 
1152 			if (0 == level)
1153 			{
1154 				max_vars /= 2;
1155 			}
1156 			else if (1 == level)
1157 			{
1158 				max_vars = 1;
1159 			}
1160 
1161 			level++;
1162 
1163 			goto next;
1164 		}
1165 		else if (STAT_SUCCESS != status || SNMP_ERR_NOERROR != response->errstat)
1166 		{
1167 			ret = zbx_get_snmp_response_error(ss, &item->interface, status, response, error, max_error_len);
1168 			running = 0;
1169 			goto next;
1170 		}
1171 
1172 		/* process response */
1173 		for (num_vars = 0, var = response->variables; NULL != var; num_vars++, var = var->next_variable)
1174 		{
1175 			char		**str_res;
1176 			unsigned char	val_type;
1177 
1178 			/* verify if we are in the same subtree */
1179 			if (SNMP_ENDOFMIBVIEW == var->type || var->name_length < rootOID_len ||
1180 					0 != memcmp(rootOID, var->name, rootOID_len * sizeof(oid)))
1181 			{
1182 				/* reached the end or past this subtree */
1183 				running = 0;
1184 				break;
1185 			}
1186 			else if (SNMP_NOSUCHOBJECT != var->type && SNMP_NOSUCHINSTANCE != var->type)
1187 			{
1188 				/* not an exception value */
1189 
1190 				if (1 == check_oid_increase)	/* typical case */
1191 				{
1192 					int	res;
1193 
1194 					/* normally devices return OIDs in increasing order, */
1195 					/* snmp_oid_compare() will return -1 in this case */
1196 
1197 					if (-1 != (res = snmp_oid_compare(anOID, anOID_len, var->name,
1198 							var->name_length)))
1199 					{
1200 						if (0 == res)	/* got the same OID */
1201 						{
1202 							zbx_strlcpy(error, "OID not changing.", max_error_len);
1203 							ret = NOTSUPPORTED;
1204 							running = 0;
1205 							break;
1206 						}
1207 						else	/* 1 == res */
1208 						{
1209 							/* OID decreased. Disable further checks of increasing */
1210 							/* and set up a protection against endless looping. */
1211 
1212 							check_oid_increase = 0;
1213 							zbx_detect_loop_init(&oids_seen);
1214 						}
1215 					}
1216 				}
1217 
1218 				if (0 == check_oid_increase && FAIL == zbx_oid_is_new(&oids_seen, rootOID_len,
1219 						var->name, var->name_length))
1220 				{
1221 					zbx_strlcpy(error, "OID loop detected or too many OIDs.", max_error_len);
1222 					ret = NOTSUPPORTED;
1223 					running = 0;
1224 					break;
1225 				}
1226 
1227 				if (SUCCEED != zbx_snmp_choose_index(oid_index, sizeof(oid_index), var->name,
1228 						var->name_length, root_string_len, root_numeric_len))
1229 				{
1230 					zbx_snprintf(error, max_error_len, "zbx_snmp_choose_index():"
1231 							" cannot choose appropriate index while walking for"
1232 							" OID \"%s\".", snmp_oid);
1233 					ret = NOTSUPPORTED;
1234 					running = 0;
1235 					break;
1236 				}
1237 
1238 				str_res = NULL;
1239 				init_result(&snmp_result);
1240 
1241 				if (SUCCEED == zbx_snmp_set_result(var, &snmp_result, &val_type))
1242 				{
1243 					if (ISSET_TEXT(&snmp_result) && ZBX_SNMP_STR_HEX == val_type)
1244 						zbx_remove_chars(snmp_result.text, "\r\n");
1245 
1246 					str_res = GET_STR_RESULT(&snmp_result);
1247 				}
1248 
1249 				if (NULL == str_res)
1250 				{
1251 					char	**msg;
1252 
1253 					msg = GET_MSG_RESULT(&snmp_result);
1254 
1255 					zabbix_log(LOG_LEVEL_DEBUG, "cannot get index '%s' string value: %s",
1256 							oid_index, NULL != msg && NULL != *msg ? *msg : "(null)");
1257 				}
1258 				else
1259 					walk_cb_func(walk_cb_arg, snmp_oid, oid_index, snmp_result.str);
1260 
1261 				free_result(&snmp_result);
1262 
1263 				/* go to next variable */
1264 				memcpy((char *)anOID, (char *)var->name, var->name_length * sizeof(oid));
1265 				anOID_len = var->name_length;
1266 			}
1267 			else
1268 			{
1269 				/* an exception value, so stop */
1270 				char	*errmsg;
1271 
1272 				errmsg = zbx_get_snmp_type_error(var->type);
1273 				zbx_strlcpy(error, errmsg, max_error_len);
1274 				zbx_free(errmsg);
1275 				ret = NOTSUPPORTED;
1276 				running = 0;
1277 				break;
1278 			}
1279 		}
1280 
1281 		if (*max_succeed < num_vars)
1282 			*max_succeed = num_vars;
1283 next:
1284 		if (NULL != response)
1285 			snmp_free_pdu(response);
1286 	}
1287 
1288 	if (0 == check_oid_increase)
1289 		zbx_hashset_destroy(&oids_seen);
1290 out:
1291 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1292 
1293 	return ret;
1294 }
1295 
zbx_snmp_get_values(struct snmp_session * ss,const DC_ITEM * items,char oids[][ITEM_SNMP_OID_LEN_MAX],AGENT_RESULT * results,int * errcodes,unsigned char * query_and_ignore_type,int num,int level,char * error,size_t max_error_len,int * max_succeed,int * min_fail,unsigned char poller_type)1296 static int	zbx_snmp_get_values(struct snmp_session *ss, const DC_ITEM *items, char oids[][ITEM_SNMP_OID_LEN_MAX],
1297 		AGENT_RESULT *results, int *errcodes, unsigned char *query_and_ignore_type, int num, int level,
1298 		char *error, size_t max_error_len, int *max_succeed, int *min_fail, unsigned char poller_type)
1299 {
1300 	int			i, j, status, ret = SUCCEED;
1301 	int			mapping[MAX_SNMP_ITEMS], mapping_num = 0;
1302 	oid			parsed_oids[MAX_SNMP_ITEMS][MAX_OID_LEN];
1303 	size_t			parsed_oid_lens[MAX_SNMP_ITEMS];
1304 	struct snmp_pdu		*pdu, *response;
1305 	struct variable_list	*var;
1306 	unsigned char		val_type;
1307 
1308 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() num:%d level:%d", __func__, num, level);
1309 
1310 	if (NULL == (pdu = snmp_pdu_create(SNMP_MSG_GET)))
1311 	{
1312 		zbx_strlcpy(error, "snmp_pdu_create(): cannot create PDU object.", max_error_len);
1313 		ret = CONFIG_ERROR;
1314 		goto out;
1315 	}
1316 
1317 	for (i = 0; i < num; i++)
1318 	{
1319 		if (SUCCEED != errcodes[i])
1320 			continue;
1321 
1322 		if (NULL != query_and_ignore_type && 0 == query_and_ignore_type[i])
1323 			continue;
1324 
1325 		parsed_oid_lens[i] = MAX_OID_LEN;
1326 
1327 		if (NULL == snmp_parse_oid(oids[i], parsed_oids[i], &parsed_oid_lens[i]))
1328 		{
1329 			SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "snmp_parse_oid(): cannot parse OID \"%s\".",
1330 					oids[i]));
1331 			errcodes[i] = CONFIG_ERROR;
1332 			continue;
1333 		}
1334 
1335 		if (NULL == snmp_add_null_var(pdu, parsed_oids[i], parsed_oid_lens[i]))
1336 		{
1337 			SET_MSG_RESULT(&results[i], zbx_strdup(NULL, "snmp_add_null_var(): cannot add null variable."));
1338 			errcodes[i] = CONFIG_ERROR;
1339 			continue;
1340 		}
1341 
1342 		mapping[mapping_num++] = i;
1343 	}
1344 
1345 	if (0 == mapping_num)
1346 	{
1347 		snmp_free_pdu(pdu);
1348 		goto out;
1349 	}
1350 
1351 	ss->retries = (1 == mapping_num && 0 == level && ZBX_POLLER_TYPE_UNREACHABLE != poller_type ? 1 : 0);
1352 retry:
1353 	status = snmp_synch_response(ss, pdu, &response);
1354 
1355 	zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d s_snmp_errno:%d errstat:%ld mapping_num:%d",
1356 			__func__, status, ss->s_snmp_errno, NULL == response ? (long)-1 : response->errstat,
1357 			mapping_num);
1358 
1359 	if (STAT_SUCCESS == status && SNMP_ERR_NOERROR == response->errstat)
1360 	{
1361 		for (i = 0, var = response->variables;; i++, var = var->next_variable)
1362 		{
1363 			/* check that response variable binding matches the request variable binding */
1364 
1365 			if (i == mapping_num)
1366 			{
1367 				if (NULL != var)
1368 				{
1369 					zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1370 							" too many variable bindings", items[0].host.host);
1371 
1372 					if (1 != mapping_num)	/* give device a chance to handle a smaller request */
1373 						goto halve;
1374 
1375 					zbx_strlcpy(error, "Invalid SNMP response: too many variable bindings.",
1376 							max_error_len);
1377 
1378 					ret = NOTSUPPORTED;
1379 				}
1380 
1381 				break;
1382 			}
1383 
1384 			if (NULL == var)
1385 			{
1386 				zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1387 						" too few variable bindings", items[0].host.host);
1388 
1389 				if (1 != mapping_num)	/* give device a chance to handle a smaller request */
1390 					goto halve;
1391 
1392 				zbx_strlcpy(error, "Invalid SNMP response: too few variable bindings.", max_error_len);
1393 
1394 				ret = NOTSUPPORTED;
1395 				break;
1396 			}
1397 
1398 			j = mapping[i];
1399 
1400 			if (parsed_oid_lens[j] != var->name_length ||
1401 					0 != memcmp(parsed_oids[j], var->name, parsed_oid_lens[j] * sizeof(oid)))
1402 			{
1403 				char	sent_oid[ITEM_SNMP_OID_LEN_MAX], received_oid[ITEM_SNMP_OID_LEN_MAX];
1404 
1405 				zbx_snmp_dump_oid(sent_oid, sizeof(sent_oid), parsed_oids[j], parsed_oid_lens[j]);
1406 				zbx_snmp_dump_oid(received_oid, sizeof(received_oid), var->name, var->name_length);
1407 
1408 				if (1 != mapping_num)
1409 				{
1410 					zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1411 							" variable bindings that do not match the request:"
1412 							" sent \"%s\", received \"%s\"",
1413 							items[0].host.host, sent_oid, received_oid);
1414 
1415 					goto halve;	/* give device a chance to handle a smaller request */
1416 				}
1417 				else
1418 				{
1419 					zabbix_log(LOG_LEVEL_DEBUG, "SNMP response from host \"%s\" contains"
1420 							" variable bindings that do not match the request:"
1421 							" sent \"%s\", received \"%s\"",
1422 							items[0].host.host, sent_oid, received_oid);
1423 				}
1424 			}
1425 
1426 			/* process received data */
1427 
1428 			if (NULL != query_and_ignore_type && 1 == query_and_ignore_type[j])
1429 				(void)zbx_snmp_set_result(var, &results[j], &val_type);
1430 			else
1431 				errcodes[j] = zbx_snmp_set_result(var, &results[j], &val_type);
1432 
1433 			if (ISSET_TEXT(&results[j]) && ZBX_SNMP_STR_HEX == val_type)
1434 				zbx_remove_chars(results[j].text, "\r\n");
1435 		}
1436 
1437 		if (SUCCEED == ret)
1438 		{
1439 			if (*max_succeed < mapping_num)
1440 				*max_succeed = mapping_num;
1441 		}
1442 		/* min_fail value is updated when bulk request is halved in the case of failure */
1443 	}
1444 	else if (STAT_SUCCESS == status && SNMP_ERR_NOSUCHNAME == response->errstat && 0 != response->errindex)
1445 	{
1446 		/* If a request PDU contains a bad variable, the specified behavior is different between SNMPv1 and */
1447 		/* later versions. In SNMPv1, the whole PDU is rejected and "response->errindex" is set to indicate */
1448 		/* the bad variable. In SNMPv2 and later, the SNMP agent processes the PDU by filling values for the */
1449 		/* known variables and marking unknown variables individually in the variable binding list. However, */
1450 		/* SNMPv2 allows SNMPv1 behavior, too. So regardless of the SNMP version used, if we get this error, */
1451 		/* then we fix the PDU by removing the bad variable and retry the request. */
1452 
1453 		i = response->errindex - 1;
1454 
1455 		if (0 > i || i >= mapping_num)
1456 		{
1457 			zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1458 					" an out of bounds error index: %ld", items[0].host.host, response->errindex);
1459 
1460 			zbx_strlcpy(error, "Invalid SNMP response: error index out of bounds.", max_error_len);
1461 
1462 			ret = NOTSUPPORTED;
1463 			goto exit;
1464 		}
1465 
1466 		j = mapping[i];
1467 
1468 		zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() errindex:%ld OID:'%s'", __func__,
1469 				response->errindex, oids[j]);
1470 
1471 		if (NULL == query_and_ignore_type || 0 == query_and_ignore_type[j])
1472 		{
1473 			errcodes[j] = zbx_get_snmp_response_error(ss, &items[0].interface, status, response, error,
1474 					max_error_len);
1475 			SET_MSG_RESULT(&results[j], zbx_strdup(NULL, error));
1476 			*error = '\0';
1477 		}
1478 
1479 		if (1 < mapping_num)
1480 		{
1481 			if (NULL != (pdu = snmp_fix_pdu(response, SNMP_MSG_GET)))
1482 			{
1483 				memmove(mapping + i, mapping + i + 1, sizeof(int) * (mapping_num - i - 1));
1484 				mapping_num--;
1485 
1486 				snmp_free_pdu(response);
1487 				goto retry;
1488 			}
1489 			else
1490 			{
1491 				zbx_strlcpy(error, "snmp_fix_pdu(): cannot fix PDU object.", max_error_len);
1492 				ret = NOTSUPPORTED;
1493 			}
1494 		}
1495 	}
1496 	else if (1 < mapping_num &&
1497 			((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status ||
1498 			(STAT_ERROR == status && SNMPERR_TOO_LONG == ss->s_snmp_errno)))
1499 	{
1500 		/* Since we are trying to obtain multiple values from the SNMP agent, the response that it has to  */
1501 		/* generate might be too big. It seems to be required by the SNMP standard that in such cases the  */
1502 		/* error status should be set to "tooBig(1)". However, some devices simply do not respond to such  */
1503 		/* queries and we get a timeout. Moreover, some devices exhibit both behaviors - they either send  */
1504 		/* "tooBig(1)" or do not respond at all. So what we do is halve the number of variables to query - */
1505 		/* it should work in the vast majority of cases, because, since we are now querying "num" values,  */
1506 		/* we know that querying "num/2" values succeeded previously. The case where it can still fail due */
1507 		/* to exceeded maximum response size is if we are now querying values that are unusually large. So */
1508 		/* if querying with half the number of the last values does not work either, we resort to querying */
1509 		/* values one by one, and the next time configuration cache gives us items to query, it will give  */
1510 		/* us less. */
1511 
1512 		/* The explanation above is for the first two conditions. The third condition comes from SNMPv3, */
1513 		/* where the size of the request that we are trying to send exceeds device's "msgMaxSize" limit. */
1514 halve:
1515 		if (*min_fail > mapping_num)
1516 			*min_fail = mapping_num;
1517 
1518 		if (0 == level)
1519 		{
1520 			/* halve the number of items */
1521 
1522 			int	base;
1523 
1524 			ret = zbx_snmp_get_values(ss, items, oids, results, errcodes, query_and_ignore_type, num / 2,
1525 					level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1526 
1527 			if (SUCCEED != ret)
1528 				goto exit;
1529 
1530 			base = num / 2;
1531 
1532 			ret = zbx_snmp_get_values(ss, items + base, oids + base, results + base, errcodes + base,
1533 					NULL == query_and_ignore_type ? NULL : query_and_ignore_type + base, num - base,
1534 					level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1535 		}
1536 		else if (1 == level)
1537 		{
1538 			/* resort to querying items one by one */
1539 
1540 			for (i = 0; i < num; i++)
1541 			{
1542 				if (SUCCEED != errcodes[i])
1543 					continue;
1544 
1545 				ret = zbx_snmp_get_values(ss, items + i, oids + i, results + i, errcodes + i,
1546 						NULL == query_and_ignore_type ? NULL : query_and_ignore_type + i, 1,
1547 						level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1548 
1549 				if (SUCCEED != ret)
1550 					goto exit;
1551 			}
1552 		}
1553 	}
1554 	else
1555 		ret = zbx_get_snmp_response_error(ss, &items[0].interface, status, response, error, max_error_len);
1556 exit:
1557 	if (NULL != response)
1558 		snmp_free_pdu(response);
1559 out:
1560 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1561 
1562 	return ret;
1563 }
1564 
1565 /******************************************************************************
1566  *                                                                            *
1567  * Function: zbx_snmp_translate                                               *
1568  *                                                                            *
1569  * Purpose: translate well-known object identifiers into numeric form         *
1570  *                                                                            *
1571  * Author: Alexei Vladishev                                                   *
1572  *                                                                            *
1573  ******************************************************************************/
zbx_snmp_translate(char * oid_translated,const char * snmp_oid,size_t max_oid_len)1574 static void	zbx_snmp_translate(char *oid_translated, const char *snmp_oid, size_t max_oid_len)
1575 {
1576 	typedef struct
1577 	{
1578 		const size_t	sz;
1579 		const char	*mib;
1580 		const char	*replace;
1581 	}
1582 	zbx_mib_norm_t;
1583 
1584 #define LEN_STR(x)	ZBX_CONST_STRLEN(x), x
1585 	static zbx_mib_norm_t mibs[] =
1586 	{
1587 		/* the most popular items first */
1588 		{LEN_STR("ifDescr"),		".1.3.6.1.2.1.2.2.1.2"},
1589 		{LEN_STR("ifInOctets"),		".1.3.6.1.2.1.2.2.1.10"},
1590 		{LEN_STR("ifOutOctets"),	".1.3.6.1.2.1.2.2.1.16"},
1591 		{LEN_STR("ifAdminStatus"),	".1.3.6.1.2.1.2.2.1.7"},
1592 		{LEN_STR("ifOperStatus"),	".1.3.6.1.2.1.2.2.1.8"},
1593 		{LEN_STR("ifIndex"),		".1.3.6.1.2.1.2.2.1.1"},
1594 		{LEN_STR("ifType"),		".1.3.6.1.2.1.2.2.1.3"},
1595 		{LEN_STR("ifMtu"),		".1.3.6.1.2.1.2.2.1.4"},
1596 		{LEN_STR("ifSpeed"),		".1.3.6.1.2.1.2.2.1.5"},
1597 		{LEN_STR("ifPhysAddress"),	".1.3.6.1.2.1.2.2.1.6"},
1598 		{LEN_STR("ifInUcastPkts"),	".1.3.6.1.2.1.2.2.1.11"},
1599 		{LEN_STR("ifInNUcastPkts"),	".1.3.6.1.2.1.2.2.1.12"},
1600 		{LEN_STR("ifInDiscards"),	".1.3.6.1.2.1.2.2.1.13"},
1601 		{LEN_STR("ifInErrors"),		".1.3.6.1.2.1.2.2.1.14"},
1602 		{LEN_STR("ifInUnknownProtos"),	".1.3.6.1.2.1.2.2.1.15"},
1603 		{LEN_STR("ifOutUcastPkts"),	".1.3.6.1.2.1.2.2.1.17"},
1604 		{LEN_STR("ifOutNUcastPkts"),	".1.3.6.1.2.1.2.2.1.18"},
1605 		{LEN_STR("ifOutDiscards"),	".1.3.6.1.2.1.2.2.1.19"},
1606 		{LEN_STR("ifOutErrors"),	".1.3.6.1.2.1.2.2.1.20"},
1607 		{LEN_STR("ifOutQLen"),		".1.3.6.1.2.1.2.2.1.21"},
1608 		{0}
1609 	};
1610 #undef LEN_STR
1611 
1612 	int	found = 0, i;
1613 
1614 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s'", __func__, snmp_oid);
1615 
1616 	for (i = 0; 0 != mibs[i].sz; i++)
1617 	{
1618 		if (0 == strncmp(mibs[i].mib, snmp_oid, mibs[i].sz))
1619 		{
1620 			found = 1;
1621 			zbx_snprintf(oid_translated, max_oid_len, "%s%s", mibs[i].replace, snmp_oid + mibs[i].sz);
1622 			break;
1623 		}
1624 	}
1625 
1626 	if (0 == found)
1627 		zbx_strlcpy(oid_translated, snmp_oid, max_oid_len);
1628 
1629 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() oid_translated:'%s'", __func__, oid_translated);
1630 }
1631 
1632 /* discovered SNMP object, identified by its index */
1633 typedef struct
1634 {
1635 	/* object index returned by zbx_snmp_walk */
1636 	char	*index;
1637 
1638 	/* an array of OID values stored in the same order as defined in OID key */
1639 	char	**values;
1640 }
1641 zbx_snmp_dobject_t;
1642 
1643 /* helper data structure used by snmp discovery */
1644 typedef struct
1645 {
1646 	/* the index of OID being currently processed (walked) */
1647 	int			num;
1648 
1649 	/* the discovered SNMP objects */
1650 	zbx_hashset_t		objects;
1651 
1652 	/* the index (order) of discovered SNMP objects */
1653 	zbx_vector_ptr_t	index;
1654 
1655 	/* request data structure used to parse discovery OID key */
1656 	AGENT_REQUEST		request;
1657 }
1658 zbx_snmp_ddata_t;
1659 
1660 /* discovery objects hashset support */
zbx_snmp_dobject_hash(const void * data)1661 static zbx_hash_t	zbx_snmp_dobject_hash(const void *data)
1662 {
1663 	const char	*index = *(const char **)data;
1664 
1665 	return ZBX_DEFAULT_STRING_HASH_ALGO(index, strlen(index), ZBX_DEFAULT_HASH_SEED);
1666 }
1667 
zbx_snmp_dobject_compare(const void * d1,const void * d2)1668 static int	zbx_snmp_dobject_compare(const void *d1, const void *d2)
1669 {
1670 	const char	*i1 = *(const char **)d1;
1671 	const char	*i2 = *(const char **)d2;
1672 
1673 	return strcmp(i1, i2);
1674 }
1675 
1676 /******************************************************************************
1677  *                                                                            *
1678  * Function: zbx_snmp_ddata_init                                              *
1679  *                                                                            *
1680  * Purpose: initializes snmp discovery data object                            *
1681  *                                                                            *
1682  * Parameters: data          - [IN] snmp discovery data object                *
1683  *             key           - [IN] discovery OID key                         *
1684  *             error         - [OUT] a buffer to store error message          *
1685  *             max_error_len - [IN] maximum error message length              *
1686  *                                                                            *
1687  * Return value: CONFIG_ERROR - OID key configuration error                   *
1688  *               SUCCEED - if function successfully completed                 *
1689  *                                                                            *
1690  ******************************************************************************/
zbx_snmp_ddata_init(zbx_snmp_ddata_t * data,const char * key,char * error,size_t max_error_len)1691 static int	zbx_snmp_ddata_init(zbx_snmp_ddata_t *data, const char *key, char *error, size_t max_error_len)
1692 {
1693 	int	i, j, ret = CONFIG_ERROR;
1694 
1695 	init_request(&data->request);
1696 
1697 	if (SUCCEED != parse_item_key(key, &data->request))
1698 	{
1699 		zbx_strlcpy(error, "Invalid SNMP OID: cannot parse expression.", max_error_len);
1700 		goto out;
1701 	}
1702 
1703 	if (0 == data->request.nparam || 0 != (data->request.nparam & 1))
1704 	{
1705 		zbx_strlcpy(error, "Invalid SNMP OID: pairs of macro and OID are expected.", max_error_len);
1706 		goto out;
1707 	}
1708 
1709 	for (i = 0; i < data->request.nparam; i += 2)
1710 	{
1711 		if (SUCCEED != is_discovery_macro(data->request.params[i]))
1712 		{
1713 			zbx_snprintf(error, max_error_len, "Invalid SNMP OID: macro \"%s\" is invalid",
1714 					data->request.params[i]);
1715 			goto out;
1716 		}
1717 
1718 		if (0 == strcmp(data->request.params[i], "{#SNMPINDEX}"))
1719 		{
1720 			zbx_strlcpy(error, "Invalid SNMP OID: macro \"{#SNMPINDEX}\" is not allowed.", max_error_len);
1721 			goto out;
1722 		}
1723 	}
1724 
1725 	for (i = 2; i < data->request.nparam; i += 2)
1726 	{
1727 		for (j = 0; j < i; j += 2)
1728 		{
1729 			if (0 == strcmp(data->request.params[i], data->request.params[j]))
1730 			{
1731 				zbx_strlcpy(error, "Invalid SNMP OID: unique macros are expected.", max_error_len);
1732 				goto out;
1733 			}
1734 		}
1735 	}
1736 
1737 	zbx_hashset_create(&data->objects, 10, zbx_snmp_dobject_hash, zbx_snmp_dobject_compare);
1738 	zbx_vector_ptr_create(&data->index);
1739 
1740 	ret = SUCCEED;
1741 out:
1742 	if (SUCCEED != ret)
1743 		free_request(&data->request);
1744 
1745 	return ret;
1746 }
1747 
1748 /******************************************************************************
1749  *                                                                            *
1750  * Function: zbx_snmp_ddata_clean                                             *
1751  *                                                                            *
1752  * Purpose: releases data allocated by snmp discovery                         *
1753  *                                                                            *
1754  * Parameters: data - [IN] snmp discovery data object                         *
1755  *                                                                            *
1756  ******************************************************************************/
zbx_snmp_ddata_clean(zbx_snmp_ddata_t * data)1757 static void	zbx_snmp_ddata_clean(zbx_snmp_ddata_t *data)
1758 {
1759 	int			i;
1760 	zbx_hashset_iter_t	iter;
1761 	zbx_snmp_dobject_t	*obj;
1762 
1763 	zbx_vector_ptr_destroy(&data->index);
1764 
1765 	zbx_hashset_iter_reset(&data->objects, &iter);
1766 	while (NULL != (obj = (zbx_snmp_dobject_t *)zbx_hashset_iter_next(&iter)))
1767 	{
1768 		for (i = 0; i < data->request.nparam / 2; i++)
1769 			zbx_free(obj->values[i]);
1770 
1771 		zbx_free(obj->index);
1772 		zbx_free(obj->values);
1773 	}
1774 
1775 	zbx_hashset_destroy(&data->objects);
1776 
1777 	free_request(&data->request);
1778 }
1779 
zbx_snmp_walk_discovery_cb(void * arg,const char * snmp_oid,const char * index,const char * value)1780 static void	zbx_snmp_walk_discovery_cb(void *arg, const char *snmp_oid, const char *index, const char *value)
1781 {
1782 	zbx_snmp_ddata_t	*data = (zbx_snmp_ddata_t *)arg;
1783 	zbx_snmp_dobject_t	*obj;
1784 
1785 	ZBX_UNUSED(snmp_oid);
1786 
1787 	if (NULL == (obj = (zbx_snmp_dobject_t *)zbx_hashset_search(&data->objects, &index)))
1788 	{
1789 		zbx_snmp_dobject_t	new_obj;
1790 
1791 		new_obj.index = zbx_strdup(NULL, index);
1792 		new_obj.values = (char **)zbx_malloc(NULL, sizeof(char *) * data->request.nparam / 2);
1793 		memset(new_obj.values, 0, sizeof(char *) * data->request.nparam / 2);
1794 
1795 		obj = (zbx_snmp_dobject_t *)zbx_hashset_insert(&data->objects, &new_obj, sizeof(new_obj));
1796 		zbx_vector_ptr_append(&data->index, obj);
1797 	}
1798 
1799 	obj->values[data->num] = zbx_strdup(NULL, value);
1800 }
1801 
zbx_snmp_process_discovery(struct snmp_session * ss,const DC_ITEM * item,AGENT_RESULT * result,int * errcode,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int max_vars,int bulk)1802 static int	zbx_snmp_process_discovery(struct snmp_session *ss, const DC_ITEM *item, AGENT_RESULT *result,
1803 		int *errcode, char *error, size_t max_error_len, int *max_succeed, int *min_fail, int max_vars,
1804 		int bulk)
1805 {
1806 	int			i, j, ret;
1807 	char			oid_translated[ITEM_SNMP_OID_LEN_MAX];
1808 	struct zbx_json		js;
1809 	zbx_snmp_ddata_t	data;
1810 	zbx_snmp_dobject_t	*obj;
1811 
1812 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1813 
1814 	if (SUCCEED != (ret = zbx_snmp_ddata_init(&data, item->snmp_oid, error, max_error_len)))
1815 		goto out;
1816 
1817 	for (data.num = 0; data.num < data.request.nparam / 2; data.num++)
1818 	{
1819 		zbx_snmp_translate(oid_translated, data.request.params[data.num * 2 + 1], sizeof(oid_translated));
1820 
1821 		if (SUCCEED != (ret = zbx_snmp_walk(ss, item, oid_translated, error, max_error_len,
1822 				max_succeed, min_fail, max_vars, bulk, zbx_snmp_walk_discovery_cb, (void *)&data)))
1823 		{
1824 			goto clean;
1825 		}
1826 	}
1827 
1828 	zbx_json_initarray(&js, ZBX_JSON_STAT_BUF_LEN);
1829 
1830 	for (i = 0; i < data.index.values_num; i++)
1831 	{
1832 		obj = (zbx_snmp_dobject_t *)data.index.values[i];
1833 
1834 		zbx_json_addobject(&js, NULL);
1835 		zbx_json_addstring(&js, "{#SNMPINDEX}", obj->index, ZBX_JSON_TYPE_STRING);
1836 
1837 		for (j = 0; j < data.request.nparam / 2; j++)
1838 		{
1839 			if (NULL == obj->values[j])
1840 				continue;
1841 
1842 			zbx_json_addstring(&js, data.request.params[j * 2], obj->values[j], ZBX_JSON_TYPE_STRING);
1843 		}
1844 		zbx_json_close(&js);
1845 	}
1846 
1847 	zbx_json_close(&js);
1848 
1849 	SET_TEXT_RESULT(result, zbx_strdup(NULL, js.buffer));
1850 
1851 	zbx_json_free(&js);
1852 clean:
1853 	zbx_snmp_ddata_clean(&data);
1854 out:
1855 	if (SUCCEED != (*errcode = ret))
1856 		SET_MSG_RESULT(result, zbx_strdup(NULL, error));
1857 
1858 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1859 
1860 	return ret;
1861 }
1862 
zbx_snmp_walk_cache_cb(void * arg,const char * snmp_oid,const char * index,const char * value)1863 static void	zbx_snmp_walk_cache_cb(void *arg, const char *snmp_oid, const char *index, const char *value)
1864 {
1865 	cache_put_snmp_index((const DC_ITEM *)arg, snmp_oid, index, value);
1866 }
1867 
zbx_snmp_process_dynamic(struct snmp_session * ss,const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int bulk,unsigned char poller_type)1868 static int	zbx_snmp_process_dynamic(struct snmp_session *ss, const DC_ITEM *items, AGENT_RESULT *results,
1869 		int *errcodes, int num, char *error, size_t max_error_len, int *max_succeed, int *min_fail, int bulk,
1870 		unsigned char poller_type)
1871 {
1872 	int		i, j, k, ret;
1873 	int		to_walk[MAX_SNMP_ITEMS], to_walk_num = 0;
1874 	int		to_verify[MAX_SNMP_ITEMS], to_verify_num = 0;
1875 	char		to_verify_oids[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1876 	unsigned char	query_and_ignore_type[MAX_SNMP_ITEMS];
1877 	char		index_oids[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1878 	char		index_values[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1879 	char		oids_translated[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1880 	char		*idx = NULL, *pl;
1881 	size_t		idx_alloc = 32;
1882 
1883 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1884 
1885 	idx = (char *)zbx_malloc(idx, idx_alloc);
1886 
1887 	/* perform initial item validation */
1888 
1889 	for (i = 0; i < num; i++)
1890 	{
1891 		char	method[8];
1892 
1893 		if (SUCCEED != errcodes[i])
1894 			continue;
1895 
1896 		if (3 != num_key_param(items[i].snmp_oid))
1897 		{
1898 			SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "OID \"%s\" contains unsupported parameters.",
1899 					items[i].snmp_oid));
1900 			errcodes[i] = CONFIG_ERROR;
1901 			continue;
1902 		}
1903 
1904 		get_key_param(items[i].snmp_oid, 1, method, sizeof(method));
1905 		get_key_param(items[i].snmp_oid, 2, index_oids[i], sizeof(index_oids[i]));
1906 		get_key_param(items[i].snmp_oid, 3, index_values[i], sizeof(index_values[i]));
1907 
1908 		if (0 != strcmp("index", method))
1909 		{
1910 			SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "Unsupported method \"%s\" in the OID \"%s\".",
1911 					method, items[i].snmp_oid));
1912 			errcodes[i] = CONFIG_ERROR;
1913 			continue;
1914 		}
1915 
1916 		zbx_snmp_translate(oids_translated[i], index_oids[i], sizeof(oids_translated[i]));
1917 
1918 		if (SUCCEED == cache_get_snmp_index(&items[i], oids_translated[i], index_values[i], &idx, &idx_alloc))
1919 		{
1920 			zbx_snprintf(to_verify_oids[i], sizeof(to_verify_oids[i]), "%s.%s", oids_translated[i], idx);
1921 
1922 			to_verify[to_verify_num++] = i;
1923 			query_and_ignore_type[i] = 1;
1924 		}
1925 		else
1926 		{
1927 			to_walk[to_walk_num++] = i;
1928 			query_and_ignore_type[i] = 0;
1929 		}
1930 	}
1931 
1932 	/* verify that cached indices are still valid */
1933 
1934 	if (0 != to_verify_num)
1935 	{
1936 		ret = zbx_snmp_get_values(ss, items, to_verify_oids, results, errcodes, query_and_ignore_type, num, 0,
1937 				error, max_error_len, max_succeed, min_fail, poller_type);
1938 
1939 		if (SUCCEED != ret && NOTSUPPORTED != ret)
1940 			goto exit;
1941 
1942 		for (i = 0; i < to_verify_num; i++)
1943 		{
1944 			j = to_verify[i];
1945 
1946 			if (SUCCEED != errcodes[j])
1947 				continue;
1948 
1949 			if (NULL == GET_STR_RESULT(&results[j]) || 0 != strcmp(results[j].str, index_values[j]))
1950 			{
1951 				to_walk[to_walk_num++] = j;
1952 			}
1953 			else
1954 			{
1955 				/* ready to construct the final OID with index */
1956 
1957 				size_t	len;
1958 
1959 				len = strlen(oids_translated[j]);
1960 
1961 				pl = strchr(items[j].snmp_oid, '[');
1962 
1963 				*pl = '\0';
1964 				zbx_snmp_translate(oids_translated[j], items[j].snmp_oid, sizeof(oids_translated[j]));
1965 				*pl = '[';
1966 
1967 				zbx_strlcat(oids_translated[j], to_verify_oids[j] + len, sizeof(oids_translated[j]));
1968 			}
1969 
1970 			free_result(&results[j]);
1971 		}
1972 	}
1973 
1974 	/* walk OID trees to build index cache for cache misses */
1975 
1976 	if (0 != to_walk_num)
1977 	{
1978 		for (i = 0; i < to_walk_num; i++)
1979 		{
1980 			int	errcode;
1981 
1982 			j = to_walk[i];
1983 
1984 			/* see whether this OID tree was already walked for another item */
1985 
1986 			for (k = 0; k < i; k++)
1987 			{
1988 				if (0 == strcmp(oids_translated[to_walk[k]], oids_translated[j]))
1989 					break;
1990 			}
1991 
1992 			if (k != i)
1993 				continue;
1994 
1995 			/* walk */
1996 
1997 			cache_del_snmp_index_subtree(&items[j], oids_translated[j]);
1998 
1999 			errcode = zbx_snmp_walk(ss, &items[j], oids_translated[j], error, max_error_len, max_succeed,
2000 					min_fail, num, bulk, zbx_snmp_walk_cache_cb, (void *)&items[j]);
2001 
2002 			if (NETWORK_ERROR == errcode)
2003 			{
2004 				/* consider a network error as relating to all items passed to */
2005 				/* this function, including those we did not just try to walk for */
2006 
2007 				ret = NETWORK_ERROR;
2008 				goto exit;
2009 			}
2010 
2011 			if (CONFIG_ERROR == errcode || NOTSUPPORTED == errcode)
2012 			{
2013 				/* consider a configuration or "not supported" error as */
2014 				/* relating only to the items we have just tried to walk for */
2015 
2016 				for (k = i; k < to_walk_num; k++)
2017 				{
2018 					if (0 == strcmp(oids_translated[to_walk[k]], oids_translated[j]))
2019 					{
2020 						SET_MSG_RESULT(&results[to_walk[k]], zbx_strdup(NULL, error));
2021 						errcodes[to_walk[k]] = errcode;
2022 					}
2023 				}
2024 			}
2025 		}
2026 
2027 		for (i = 0; i < to_walk_num; i++)
2028 		{
2029 			j = to_walk[i];
2030 
2031 			if (SUCCEED != errcodes[j])
2032 				continue;
2033 
2034 			if (SUCCEED == cache_get_snmp_index(&items[j], oids_translated[j], index_values[j], &idx,
2035 						&idx_alloc))
2036 			{
2037 				/* ready to construct the final OID with index */
2038 
2039 				pl = strchr(items[j].snmp_oid, '[');
2040 
2041 				*pl = '\0';
2042 				zbx_snmp_translate(oids_translated[j], items[j].snmp_oid, sizeof(oids_translated[j]));
2043 				*pl = '[';
2044 
2045 				zbx_strlcat(oids_translated[j], ".", sizeof(oids_translated[j]));
2046 				zbx_strlcat(oids_translated[j], idx, sizeof(oids_translated[j]));
2047 			}
2048 			else
2049 			{
2050 				SET_MSG_RESULT(&results[j], zbx_dsprintf(NULL,
2051 						"Cannot find index of \"%s\" in \"%s\".",
2052 						index_values[j], index_oids[j]));
2053 				errcodes[j] = NOTSUPPORTED;
2054 			}
2055 		}
2056 	}
2057 
2058 	/* query values based on the indices verified and/or determined above */
2059 
2060 	ret = zbx_snmp_get_values(ss, items, oids_translated, results, errcodes, NULL, num, 0, error, max_error_len,
2061 			max_succeed, min_fail, poller_type);
2062 exit:
2063 	zbx_free(idx);
2064 
2065 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2066 
2067 	return ret;
2068 }
2069 
zbx_snmp_process_standard(struct snmp_session * ss,const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,char * error,size_t max_error_len,int * max_succeed,int * min_fail,unsigned char poller_type)2070 static int	zbx_snmp_process_standard(struct snmp_session *ss, const DC_ITEM *items, AGENT_RESULT *results,
2071 		int *errcodes, int num, char *error, size_t max_error_len, int *max_succeed, int *min_fail,
2072 		unsigned char poller_type)
2073 {
2074 	int	i, ret;
2075 	char	oids_translated[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
2076 
2077 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2078 
2079 	for (i = 0; i < num; i++)
2080 	{
2081 		if (SUCCEED != errcodes[i])
2082 			continue;
2083 
2084 		if (0 != num_key_param(items[i].snmp_oid))
2085 		{
2086 			SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "OID \"%s\" contains unsupported parameters.",
2087 					items[i].snmp_oid));
2088 			errcodes[i] = CONFIG_ERROR;
2089 			continue;
2090 		}
2091 
2092 		zbx_snmp_translate(oids_translated[i], items[i].snmp_oid, sizeof(oids_translated[i]));
2093 	}
2094 
2095 	ret = zbx_snmp_get_values(ss, items, oids_translated, results, errcodes, NULL, num, 0, error, max_error_len,
2096 			max_succeed, min_fail, poller_type);
2097 
2098 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2099 
2100 	return ret;
2101 }
2102 
get_value_snmp(const DC_ITEM * item,AGENT_RESULT * result,unsigned char poller_type)2103 int	get_value_snmp(const DC_ITEM *item, AGENT_RESULT *result, unsigned char poller_type)
2104 {
2105 	int	errcode = SUCCEED;
2106 
2107 	get_values_snmp(item, result, &errcode, 1, poller_type);
2108 
2109 	return errcode;
2110 }
2111 
zbx_init_snmp(void)2112 static void	zbx_init_snmp(void)
2113 {
2114 	sigset_t	mask, orig_mask;
2115 
2116 	if (1 == zbx_snmp_init_done)
2117 		return;
2118 
2119 	sigemptyset(&mask);
2120 	sigaddset(&mask, SIGTERM);
2121 	sigaddset(&mask, SIGUSR2);
2122 	sigaddset(&mask, SIGHUP);
2123 	sigaddset(&mask, SIGQUIT);
2124 	sigprocmask(SIG_BLOCK, &mask, &orig_mask);
2125 
2126 	init_snmp(progname);
2127 	zbx_snmp_init_done = 1;
2128 
2129 	sigprocmask(SIG_SETMASK, &orig_mask, NULL);
2130 }
2131 
get_values_snmp(const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,unsigned char poller_type)2132 void	get_values_snmp(const DC_ITEM *items, AGENT_RESULT *results, int *errcodes, int num, unsigned char poller_type)
2133 {
2134 	struct snmp_session	*ss;
2135 	char			error[MAX_STRING_LEN];
2136 	int			i, j, err = SUCCEED, max_succeed = 0, min_fail = MAX_SNMP_ITEMS + 1,
2137 				bulk = SNMP_BULK_ENABLED;
2138 
2139 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'%s' addr:'%s' num:%d",
2140 			__func__, items[0].host.host, items[0].interface.addr, num);
2141 
2142 	zbx_init_snmp();	/* avoid high CPU usage by only initializing SNMP once used */
2143 
2144 	for (j = 0; j < num; j++)	/* locate first supported item to use as a reference */
2145 	{
2146 		if (SUCCEED == errcodes[j])
2147 			break;
2148 	}
2149 
2150 	if (j == num)	/* all items already NOTSUPPORTED (with invalid key, port or SNMP parameters) */
2151 		goto out;
2152 
2153 	if (NULL == (ss = zbx_snmp_open_session(&items[j], error, sizeof(error))))
2154 	{
2155 		err = NETWORK_ERROR;
2156 		goto exit;
2157 	}
2158 
2159 	if (0 != (ZBX_FLAG_DISCOVERY_RULE & items[j].flags) || 0 == strncmp(items[j].snmp_oid, "discovery[", 10))
2160 	{
2161 		int	max_vars;
2162 
2163 		max_vars = DCconfig_get_suggested_snmp_vars(items[j].interface.interfaceid, &bulk);
2164 
2165 		err = zbx_snmp_process_discovery(ss, &items[j], &results[j], &errcodes[j], error, sizeof(error),
2166 				&max_succeed, &min_fail, max_vars, bulk);
2167 	}
2168 	else if (NULL != strchr(items[j].snmp_oid, '['))
2169 	{
2170 		(void)DCconfig_get_suggested_snmp_vars(items[j].interface.interfaceid, &bulk);
2171 
2172 		err = zbx_snmp_process_dynamic(ss, items + j, results + j, errcodes + j, num - j, error, sizeof(error),
2173 				&max_succeed, &min_fail, bulk, poller_type);
2174 	}
2175 	else
2176 	{
2177 		err = zbx_snmp_process_standard(ss, items + j, results + j, errcodes + j, num - j, error, sizeof(error),
2178 				&max_succeed, &min_fail, poller_type);
2179 	}
2180 
2181 	zbx_snmp_close_session(ss);
2182 exit:
2183 	if (SUCCEED != err)
2184 	{
2185 		zabbix_log(LOG_LEVEL_DEBUG, "getting SNMP values failed: %s", error);
2186 
2187 		for (i = j; i < num; i++)
2188 		{
2189 			if (SUCCEED != errcodes[i])
2190 				continue;
2191 
2192 			SET_MSG_RESULT(&results[i], zbx_strdup(NULL, error));
2193 			errcodes[i] = err;
2194 		}
2195 	}
2196 	else if (SNMP_BULK_ENABLED == bulk && (0 != max_succeed || MAX_SNMP_ITEMS + 1 != min_fail))
2197 	{
2198 		DCconfig_update_interface_snmp_stats(items[j].interface.interfaceid, max_succeed, min_fail);
2199 	}
2200 out:
2201 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
2202 }
2203 
zbx_shutdown_snmp(void)2204 static void	zbx_shutdown_snmp(void)
2205 {
2206 	sigset_t	mask, orig_mask;
2207 
2208 	sigemptyset(&mask);
2209 	sigaddset(&mask, SIGTERM);
2210 	sigaddset(&mask, SIGUSR2);
2211 	sigaddset(&mask, SIGHUP);
2212 	sigaddset(&mask, SIGQUIT);
2213 	sigprocmask(SIG_BLOCK, &mask, &orig_mask);
2214 
2215 	snmp_shutdown(progname);
2216 	zbx_snmp_init_done = 0;
2217 
2218 	sigprocmask(SIG_SETMASK, &orig_mask, NULL);
2219 }
2220 
zbx_clear_cache_snmp(unsigned char process_type,int process_num)2221 void	zbx_clear_cache_snmp(unsigned char process_type, int process_num)
2222 {
2223 	zabbix_log(LOG_LEVEL_WARNING, "forced reloading of the snmp cache on [%s #%d]", get_process_type_string(process_type),
2224 			process_num);
2225 
2226 	if (0 == zbx_snmp_init_done)
2227 		return;
2228 
2229 	netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
2230 	zbx_shutdown_snmp();
2231 }
2232 
2233 #endif	/* HAVE_NETSNMP */
2234