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