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 "common.h"
21 #include "zbxjson.h"
22 #include "zbxalgo.h"
23 #include "preproc.h"
24 #include "../preprocessor/preproc_history.h"
25 
26 #include "trapper_auth.h"
27 #include "trapper_preproc.h"
28 
29 #define ZBX_STATE_NOT_SUPPORTED	1
30 
31 extern int	CONFIG_DOUBLE_PRECISION;
32 
33 /******************************************************************************
34  *                                                                            *
35  * Function: trapper_parse_preproc_test                                       *
36  *                                                                            *
37  * Purpose: parses preprocessing test request                                 *
38  *                                                                            *
39  * Parameters: jp           - [IN] the request                                *
40  *             values       - [OUT] the values to test optional               *
41  *                                  (history + current)                       *
42  *             ts           - [OUT] value timestamps                          *
43  *             values_num   - [OUT] the number of values                      *
44  *             value_type   - [OUT] the value type                            *
45  *             steps        - [OUT] the preprocessing steps                   *
46  *             single     - [OUT] is preproc step single                      *
47  *             state        - [OUT] the item state                            *
48  *             bypass_first - [OUT] the flag to bypass first step             *
49  *             error      - [OUT] the error message                           *
50  *                                                                            *
51  * Return value: SUCCEED - the request was parsed successfully                *
52  *               FAIL    - otherwise                                          *
53  *                                                                            *
54  ******************************************************************************/
trapper_parse_preproc_test(const struct zbx_json_parse * jp,char ** values,zbx_timespec_t * ts,int * values_num,unsigned char * value_type,zbx_vector_ptr_t * steps,int * single,int * state,int * bypass_first,char ** error)55 static int	trapper_parse_preproc_test(const struct zbx_json_parse *jp, char **values, zbx_timespec_t *ts,
56 		int *values_num, unsigned char *value_type, zbx_vector_ptr_t *steps, int *single, int *state,
57 		int *bypass_first, char **error)
58 {
59 	char			buffer[MAX_STRING_LEN], *step_params = NULL, *error_handler_params = NULL;
60 	const char		*ptr;
61 	zbx_user_t		user;
62 	int			ret = FAIL;
63 	struct zbx_json_parse	jp_data, jp_history, jp_steps, jp_step;
64 	size_t			size;
65 	zbx_timespec_t		ts_now;
66 
67 	if (FAIL == zbx_get_user_from_json(jp, &user, NULL) || USER_TYPE_ZABBIX_ADMIN > user.type)
68 	{
69 		*error = zbx_strdup(NULL, "Permission denied.");
70 		goto out;
71 	}
72 
73 	if (FAIL == zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data))
74 	{
75 		*error = zbx_strdup(NULL, "Missing data field.");
76 		goto out;
77 	}
78 
79 	if (FAIL == zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_VALUE_TYPE, buffer, sizeof(buffer), NULL))
80 	{
81 		*error = zbx_strdup(NULL, "Missing value type field.");
82 		goto out;
83 	}
84 	*value_type = atoi(buffer);
85 
86 	if (FAIL == zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_SINGLE, buffer, sizeof(buffer), NULL))
87 		*single = 0;
88 	else
89 		*single = (0 == strcmp(buffer, "true") ? 1 : 0);
90 
91 	*state = 0;
92 	if (SUCCEED == zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_STATE, buffer, sizeof(buffer), NULL))
93 		*state = atoi(buffer);
94 
95 	zbx_timespec(&ts_now);
96 	if (SUCCEED == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_HISTORY, &jp_history))
97 	{
98 		size = 0;
99 		if (FAIL == zbx_json_value_by_name_dyn(&jp_history, ZBX_PROTO_TAG_VALUE, values, &size, NULL))
100 		{
101 			*error = zbx_strdup(NULL, "Missing history value field.");
102 			goto out;
103 		}
104 		(*values_num)++;
105 
106 		if (FAIL == zbx_json_value_by_name(&jp_history, ZBX_PROTO_TAG_TIMESTAMP, buffer, sizeof(buffer), NULL))
107 		{
108 			*error = zbx_strdup(NULL, "Missing history timestamp field.");
109 			goto out;
110 		}
111 
112 		if (0 != strncmp(buffer, "now", ZBX_CONST_STRLEN("now")))
113 		{
114 			*error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", buffer);
115 			goto out;
116 		}
117 
118 		ts[0] = ts_now;
119 		ptr = buffer + ZBX_CONST_STRLEN("now");
120 
121 		if ('\0' != *ptr)
122 		{
123 			int	delay;
124 
125 			if ('-' != *ptr || FAIL == is_time_suffix(ptr + 1, &delay, strlen(ptr + 1)))
126 			{
127 				*error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", buffer);
128 				goto out;
129 			}
130 
131 			ts[0].sec -= delay;
132 		}
133 	}
134 
135 	size = 0;
136 	if (FAIL == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_VALUE, &values[*values_num], &size, NULL))
137 	{
138 		*error = zbx_strdup(NULL, "Missing value field.");
139 		goto out;
140 	}
141 	ts[(*values_num)++] = ts_now;
142 
143 	if (FAIL == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_STEPS, &jp_steps))
144 	{
145 		*error = zbx_strdup(NULL, "Missing preprocessing steps field.");
146 		goto out;
147 	}
148 
149 	*bypass_first = 0;
150 
151 	for (ptr = NULL; NULL != (ptr = zbx_json_next(&jp_steps, ptr));)
152 	{
153 		zbx_preproc_op_t	*step;
154 		unsigned char		step_type, error_handler;
155 
156 		if (FAIL == zbx_json_brackets_open(ptr, &jp_step))
157 		{
158 			*error = zbx_strdup(NULL, "Cannot parse preprocessing step.");
159 			goto out;
160 		}
161 
162 		if (FAIL == zbx_json_value_by_name(&jp_step, ZBX_PROTO_TAG_TYPE, buffer, sizeof(buffer), NULL))
163 		{
164 			*error = zbx_strdup(NULL, "Missing preprocessing step type field.");
165 			goto out;
166 		}
167 		step_type = atoi(buffer);
168 
169 		if (FAIL == zbx_json_value_by_name(&jp_step, ZBX_PROTO_TAG_ERROR_HANDLER, buffer, sizeof(buffer), NULL))
170 		{
171 			*error = zbx_strdup(NULL, "Missing preprocessing step type error handler field.");
172 			goto out;
173 		}
174 		error_handler = atoi(buffer);
175 
176 		size = 0;
177 		if (FAIL == zbx_json_value_by_name_dyn(&jp_step, ZBX_PROTO_TAG_PARAMS, &step_params, &size, NULL))
178 		{
179 			*error = zbx_strdup(NULL, "Missing preprocessing step type params field.");
180 			goto out;
181 		}
182 
183 		size = 0;
184 		if (FAIL == zbx_json_value_by_name_dyn(&jp_step, ZBX_PROTO_TAG_ERROR_HANDLER_PARAMS,
185 				&error_handler_params, &size, NULL))
186 		{
187 			*error = zbx_strdup(NULL, "Missing preprocessing step type error handler params field.");
188 			goto out;
189 		}
190 
191 		if (ZBX_PREPROC_VALIDATE_NOT_SUPPORTED != step_type || ZBX_STATE_NOT_SUPPORTED == *state)
192 		{
193 			step = (zbx_preproc_op_t *)zbx_malloc(NULL, sizeof(zbx_preproc_op_t));
194 			step->type = step_type;
195 			step->params = step_params;
196 			step->error_handler = error_handler;
197 			step->error_handler_params = error_handler_params;
198 			zbx_vector_ptr_append(steps, step);
199 		}
200 		else
201 		{
202 			zbx_free(step_params);
203 			zbx_free(error_handler_params);
204 			*bypass_first = 1;
205 		}
206 
207 		step_params = NULL;
208 		error_handler_params = NULL;
209 	}
210 
211 	ret = SUCCEED;
212 out:
213 	if (FAIL == ret)
214 	{
215 		zbx_vector_ptr_clear_ext(steps, (zbx_clean_func_t)zbx_preproc_op_free);
216 		zbx_free(values[0]);
217 		zbx_free(values[1]);
218 	}
219 
220 	zbx_free(step_params);
221 	zbx_free(error_handler_params);
222 
223 	return ret;
224 }
225 
226 /******************************************************************************
227  *                                                                            *
228  * Function: trapper_preproc_test_run                                         *
229  *                                                                            *
230  * Purpose: executes preprocessing test request                               *
231  *                                                                            *
232  * Parameters: jp    - [IN] the request                                       *
233  *             json  - [OUT] the output json                                  *
234  *             error - [OUT] the error message                                *
235  *                                                                            *
236  * Return value: SUCCEED - the request was executed successfully              *
237  *               FAIL    - otherwise                                          *
238  *                                                                            *
239  * Comments: This function will fail if the request format is not valid or    *
240  *           there was connection (to preprocessing manager) error.           *
241  *           Any errors in the preprocessing itself are reported in output    *
242  *           json and success is returned.                                    *
243  *                                                                            *
244  ******************************************************************************/
trapper_preproc_test_run(const struct zbx_json_parse * jp,struct zbx_json * json,char ** error)245 static int	trapper_preproc_test_run(const struct zbx_json_parse *jp, struct zbx_json *json, char **error)
246 {
247 	char			*values[2] = {NULL, NULL}, *preproc_error = NULL;
248 	int			i, single, state, bypass_first, ret = FAIL, values_num = 0;
249 	unsigned char		value_type, first_step_type;
250 	zbx_vector_ptr_t	steps, results, history;
251 	zbx_timespec_t		ts[2];
252 	zbx_preproc_result_t	*result;
253 
254 	zbx_vector_ptr_create(&steps);
255 	zbx_vector_ptr_create(&results);
256 	zbx_vector_ptr_create(&history);
257 
258 	if (FAIL == trapper_parse_preproc_test(jp, values, ts, &values_num, &value_type, &steps, &single, &state,
259 			&bypass_first, error))
260 	{
261 		goto out;
262 	}
263 
264 	first_step_type = 0;
265 	if (0 != steps.values_num)
266 		first_step_type  = ((zbx_preproc_op_t *)steps.values[0])->type;
267 
268 	if (ZBX_PREPROC_VALIDATE_NOT_SUPPORTED != first_step_type && ZBX_STATE_NOT_SUPPORTED == state)
269 	{
270 		preproc_error = zbx_strdup(NULL, "This item is not supported. Please, add a preprocessing step"
271 				" \"Check for not supported value\" to process it.");
272 		zbx_json_addstring(json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING);
273 		zbx_json_addobject(json, ZBX_PROTO_TAG_DATA);
274 		zbx_json_addarray(json, ZBX_PROTO_TAG_STEPS);
275 		goto err;
276 	}
277 
278 	for (i = 0; i < values_num; i++)
279 	{
280 		zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_preproc_result_free);
281 
282 		if (0 == steps.values_num)
283 		{
284 			zbx_variant_t	value;
285 
286 			result = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t));
287 
288 			result->error = NULL;
289 			zbx_variant_set_str(&value, values[i]);
290 			zbx_variant_copy(&result->value, &value);
291 			zbx_vector_ptr_append(&results, result);
292 		}
293 		else if (FAIL == zbx_preprocessor_test(value_type, values[i], &ts[i], &steps, &results, &history,
294 				&preproc_error, error))
295 		{
296 			goto out;
297 		}
298 
299 		if (NULL != preproc_error)
300 			break;
301 
302 		if (0 == single)
303 		{
304 			result = (zbx_preproc_result_t *)results.values[results.values_num - 1];
305 			if (ZBX_VARIANT_NONE != result->value.type && FAIL == zbx_variant_to_value_type(&result->value,
306 					value_type, CONFIG_DOUBLE_PRECISION, &preproc_error))
307 			{
308 				break;
309 			}
310 		}
311 	}
312 
313 	zbx_json_addstring(json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING);
314 	zbx_json_addobject(json, ZBX_PROTO_TAG_DATA);
315 
316 	if (i + 1 < values_num)
317 		zbx_json_addstring(json, ZBX_PROTO_TAG_PREVIOUS, "true", ZBX_JSON_TYPE_INT);
318 
319 	zbx_json_addarray(json, ZBX_PROTO_TAG_STEPS);
320 
321 	if (1 == bypass_first)
322 	{
323 		zbx_json_addobject(json, NULL);
324 		zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, ZBX_PROTO_TAG_VALUE, ZBX_JSON_TYPE_STRING);
325 		zbx_json_close(json);
326 	}
327 
328 	if (0 != steps.values_num)
329 	{
330 		for (i = 0; i < results.values_num; i++)
331 		{
332 			result = (zbx_preproc_result_t *)results.values[i];
333 
334 			zbx_json_addobject(json, NULL);
335 
336 			if (NULL != result->error)
337 				zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, result->error, ZBX_JSON_TYPE_STRING);
338 
339 			if (ZBX_PREPROC_FAIL_DEFAULT != result->action)
340 				zbx_json_adduint64(json, ZBX_PROTO_TAG_ACTION, result->action);
341 
342 			if (i == results.values_num - 1 && NULL != result->error)
343 			{
344 				if (ZBX_PREPROC_FAIL_SET_ERROR == result->action)
345 				{
346 					zbx_json_addstring(json, ZBX_PROTO_TAG_FAILED, preproc_error,
347 							ZBX_JSON_TYPE_STRING);
348 				}
349 			}
350 
351 			if (ZBX_VARIANT_NONE != result->value.type)
352 			{
353 				zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, zbx_variant_value_desc(&result->value),
354 						ZBX_JSON_TYPE_STRING);
355 			}
356 			else if (NULL == result->error || ZBX_PREPROC_FAIL_DISCARD_VALUE == result->action)
357 				zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, NULL, ZBX_JSON_TYPE_NULL);
358 
359 			zbx_json_close(json);
360 		}
361 	}
362 err:
363 	zbx_json_close(json);
364 
365 	if (NULL == preproc_error)
366 	{
367 		result = (zbx_preproc_result_t *)results.values[results.values_num - 1];
368 
369 		if (ZBX_VARIANT_NONE != result->value.type)
370 		{
371 			zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, zbx_variant_value_desc(&result->value),
372 					ZBX_JSON_TYPE_STRING);
373 		}
374 		else
375 			zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, NULL, ZBX_JSON_TYPE_NULL);
376 	}
377 	else
378 		zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, preproc_error, ZBX_JSON_TYPE_STRING);
379 
380 	ret = SUCCEED;
381 out:
382 	for (i = 0; i < values_num; i++)
383 		zbx_free(values[i]);
384 
385 	zbx_free(preproc_error);
386 
387 	zbx_vector_ptr_clear_ext(&history, (zbx_clean_func_t)zbx_preproc_op_history_free);
388 	zbx_vector_ptr_destroy(&history);
389 	zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_preproc_result_free);
390 	zbx_vector_ptr_destroy(&results);
391 	zbx_vector_ptr_clear_ext(&steps, (zbx_clean_func_t)zbx_preproc_op_free);
392 	zbx_vector_ptr_destroy(&steps);
393 
394 	return ret;
395 }
396 
397 /******************************************************************************
398  *                                                                            *
399  * Function: zbx_trapper_preproc_test                                         *
400  *                                                                            *
401  * Purpose: processes preprocessing test request                              *
402  *                                                                            *
403  * Parameters: sock - [IN] the request source socket (frontend)               *
404  *             jp   - [IN] the request                                        *
405  *                                                                            *
406  * Return value: SUCCEED - the request was processed successfully             *
407  *               FAIL    - otherwise                                          *
408  *                                                                            *
409  * Comments: This function will send proper (success/fail) response to the    *
410  *           request socket.                                                  *
411  *           Preprocessing failure (error returned by a preprocessing step)   *
412  *           is counted as successful test and will return success response.  *
413  *                                                                            *
414  ******************************************************************************/
zbx_trapper_preproc_test(zbx_socket_t * sock,const struct zbx_json_parse * jp)415 int	zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp)
416 {
417 	char		*error = NULL;
418 	int		ret;
419 	struct zbx_json	json;
420 
421 	zbx_json_init(&json, 1024);
422 
423 	if (SUCCEED == (ret = trapper_preproc_test_run(jp, &json, &error)))
424 	{
425 		zbx_tcp_send_bytes_to(sock, json.buffer, json.buffer_size, CONFIG_TIMEOUT);
426 	}
427 	else
428 	{
429 		zbx_send_response(sock, ret, error, CONFIG_TIMEOUT);
430 		zbx_free(error);
431 	}
432 
433 	zbx_json_free(&json);
434 
435 	return ret;
436 }
437 
438 #ifdef HAVE_TESTS
439 #	include "../../../tests/zabbix_server/trapper/trapper_preproc_test_run.c"
440 #endif
441