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 
22 #include "db.h"
23 #include "log.h"
24 #include "dbcache.h"
25 #include "preproc.h"
26 #include "daemon.h"
27 
28 #include "zbxserver.h"
29 #include "zbxregexp.h"
30 #include "zbxhttp.h"
31 
32 #include "httptest.h"
33 #include "httpmacro.h"
34 
35 typedef struct
36 {
37 	long	rspcode;
38 	double	total_time;
39 	double	speed_download;
40 }
41 zbx_httpstat_t;
42 
43 extern int	CONFIG_HTTPPOLLER_FORKS;
44 
45 #ifdef HAVE_LIBCURL
46 
47 typedef struct
48 {
49 	char	*data;
50 	size_t	allocated;
51 	size_t	offset;
52 }
53 zbx_httppage_t;
54 
55 static zbx_httppage_t	page;
56 
curl_write_cb(void * ptr,size_t size,size_t nmemb,void * userdata)57 static size_t	curl_write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
58 {
59 	size_t	r_size = size * nmemb;
60 
61 	ZBX_UNUSED(userdata);
62 
63 	/* first piece of data */
64 	if (NULL == page.data)
65 	{
66 		page.allocated = MAX(8096, r_size);
67 		page.offset = 0;
68 		page.data = (char *)zbx_malloc(page.data, page.allocated);
69 	}
70 
71 	zbx_strncpy_alloc(&page.data, &page.allocated, &page.offset, (char *)ptr, r_size);
72 
73 	return r_size;
74 }
75 
curl_ignore_cb(void * ptr,size_t size,size_t nmemb,void * userdata)76 static size_t	curl_ignore_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
77 {
78 	ZBX_UNUSED(ptr);
79 	ZBX_UNUSED(userdata);
80 
81 	return size * nmemb;
82 }
83 
84 #endif	/* HAVE_LIBCURL */
85 
86 /******************************************************************************
87  *                                                                            *
88  * Function: httptest_remove_macros                                           *
89  *                                                                            *
90  * Purpose: remove all macro variables cached during http test execution      *
91  *                                                                            *
92  * Parameters: httptest - [IN] the http test data                             *
93  *                                                                            *
94  * Author: Andris Zeila                                                       *
95  *                                                                            *
96  ******************************************************************************/
httptest_remove_macros(zbx_httptest_t * httptest)97 static void	httptest_remove_macros(zbx_httptest_t *httptest)
98 {
99 	int	i;
100 
101 	for (i = 0; i < httptest->macros.values_num; i++)
102 	{
103 		zbx_ptr_pair_t	*pair = &httptest->macros.values[i];
104 
105 		zbx_free(pair->first);
106 		zbx_free(pair->second);
107 	}
108 
109 	zbx_vector_ptr_pair_clear(&httptest->macros);
110 }
111 
process_test_data(zbx_uint64_t httptestid,int lastfailedstep,double speed_download,const char * err_str,zbx_timespec_t * ts)112 static void	process_test_data(zbx_uint64_t httptestid, int lastfailedstep, double speed_download,
113 		const char *err_str, zbx_timespec_t *ts)
114 {
115 	DB_RESULT	result;
116 	DB_ROW		row;
117 	unsigned char	types[3];
118 	DC_ITEM		items[3];
119 	zbx_uint64_t	itemids[3];
120 	int		errcodes[3];
121 	size_t		i, num = 0;
122 	AGENT_RESULT	value;
123 
124 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
125 
126 	result = DBselect("select type,itemid from httptestitem where httptestid=" ZBX_FS_UI64, httptestid);
127 
128 	while (NULL != (row = DBfetch(result)))
129 	{
130 		if (3 == num)
131 		{
132 			THIS_SHOULD_NEVER_HAPPEN;
133 			break;
134 		}
135 
136 		switch (types[num] = (unsigned char)atoi(row[0]))
137 		{
138 			case ZBX_HTTPITEM_TYPE_SPEED:
139 			case ZBX_HTTPITEM_TYPE_LASTSTEP:
140 				break;
141 			case ZBX_HTTPITEM_TYPE_LASTERROR:
142 				if (NULL == err_str)
143 					continue;
144 				break;
145 			default:
146 				THIS_SHOULD_NEVER_HAPPEN;
147 				continue;
148 		}
149 
150 		ZBX_STR2UINT64(itemids[num], row[1]);
151 		num++;
152 	}
153 	DBfree_result(result);
154 
155 	if (0 < num)
156 	{
157 		DCconfig_get_items_by_itemids(items, itemids, errcodes, num);
158 
159 		for (i = 0; i < num; i++)
160 		{
161 			if (SUCCEED != errcodes[i])
162 				continue;
163 
164 			if (ITEM_STATUS_ACTIVE != items[i].status)
165 				continue;
166 
167 			if (HOST_STATUS_MONITORED != items[i].host.status)
168 				continue;
169 
170 			if (HOST_MAINTENANCE_STATUS_ON == items[i].host.maintenance_status &&
171 					MAINTENANCE_TYPE_NODATA == items[i].host.maintenance_type)
172 			{
173 				continue;
174 			}
175 
176 			init_result(&value);
177 
178 			switch (types[i])
179 			{
180 				case ZBX_HTTPITEM_TYPE_SPEED:
181 					SET_UI64_RESULT(&value, speed_download);
182 					break;
183 				case ZBX_HTTPITEM_TYPE_LASTSTEP:
184 					SET_UI64_RESULT(&value, lastfailedstep);
185 					break;
186 				case ZBX_HTTPITEM_TYPE_LASTERROR:
187 					SET_STR_RESULT(&value, zbx_strdup(NULL, err_str));
188 					break;
189 			}
190 
191 			items[i].state = ITEM_STATE_NORMAL;
192 			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
193 					ts, items[i].state, NULL);
194 
195 			free_result(&value);
196 		}
197 
198 		DCconfig_clean_items(items, errcodes, num);
199 	}
200 
201 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
202 }
203 
204 /******************************************************************************
205  *                                                                            *
206  * Function: httpstep_pairs_join                                              *
207  *                                                                            *
208  * Purpose: performs concatenation of vector of pairs into delimited string   *
209  *                                                                            *
210  * Parameters: str             - [IN/OUT] result string                       *
211  *             alloc_len       - [IN/OUT] allocated memory size               *
212  *             offset          - [IN/OUT] offset within string                *
213  *             value_delimiter - [IN] delimiter to be used between name and   *
214  *                                    value                                   *
215  *             pair_delimiter  - [IN] delimiter to be used between pairs      *
216  *             pairs           - [IN] vector of pairs                         *
217  *                                                                            *
218  ******************************************************************************/
httpstep_pairs_join(char ** str,size_t * alloc_len,size_t * offset,const char * value_delimiter,const char * pair_delimiter,zbx_vector_ptr_pair_t * pairs)219 static void	httpstep_pairs_join(char **str, size_t *alloc_len, size_t *offset, const char *value_delimiter,
220 		const char *pair_delimiter, zbx_vector_ptr_pair_t *pairs)
221 {
222 	int	p;
223 	char	*key, *value;
224 
225 	for (p = 0; p < pairs->values_num; p++)
226 	{
227 		key = (char *)pairs->values[p].first;
228 		value = (char *)pairs->values[p].second;
229 
230 		if (0 != p)
231 			zbx_strcpy_alloc(str, alloc_len, offset, pair_delimiter);
232 
233 		zbx_strcpy_alloc(str, alloc_len, offset, key);
234 		zbx_strcpy_alloc(str, alloc_len, offset, value_delimiter);
235 		zbx_strcpy_alloc(str, alloc_len, offset, value);
236 	}
237 }
238 
239 /******************************************************************************
240  *                                                                            *
241  * Function: httppairs_free                                                   *
242  *                                                                            *
243  * Purpose: frees memory allocated for vector of pairs                        *
244  *                                                                            *
245  * Parameters: pairs           - [IN] vector of pairs                         *
246  *                                                                            *
247  ******************************************************************************/
httppairs_free(zbx_vector_ptr_pair_t * pairs)248 static void	httppairs_free(zbx_vector_ptr_pair_t *pairs)
249 {
250 	int	p;
251 
252 	for (p = 0; p < pairs->values_num; p++)
253 	{
254 		zbx_free(pairs->values[p].first);
255 		zbx_free(pairs->values[p].second);
256 	}
257 
258 	zbx_vector_ptr_pair_destroy(pairs);
259 }
260 
261 #ifdef HAVE_LIBCURL
process_step_data(zbx_uint64_t httpstepid,zbx_httpstat_t * stat,zbx_timespec_t * ts)262 static void	process_step_data(zbx_uint64_t httpstepid, zbx_httpstat_t *stat, zbx_timespec_t *ts)
263 {
264 	DB_RESULT	result;
265 	DB_ROW		row;
266 	unsigned char	types[3];
267 	DC_ITEM		items[3];
268 	zbx_uint64_t	itemids[3];
269 	int		errcodes[3];
270 	size_t		i, num = 0;
271 	AGENT_RESULT	value;
272 
273 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() rspcode:%ld time:" ZBX_FS_DBL " speed:" ZBX_FS_DBL,
274 			__func__, stat->rspcode, stat->total_time, stat->speed_download);
275 
276 	result = DBselect("select type,itemid from httpstepitem where httpstepid=" ZBX_FS_UI64, httpstepid);
277 
278 	while (NULL != (row = DBfetch(result)))
279 	{
280 		if (3 == num)
281 		{
282 			THIS_SHOULD_NEVER_HAPPEN;
283 			break;
284 		}
285 
286 		if (ZBX_HTTPITEM_TYPE_RSPCODE != (types[num] = (unsigned char)atoi(row[0])) &&
287 				ZBX_HTTPITEM_TYPE_TIME != types[num] && ZBX_HTTPITEM_TYPE_SPEED != types[num])
288 		{
289 			THIS_SHOULD_NEVER_HAPPEN;
290 			continue;
291 		}
292 
293 		ZBX_STR2UINT64(itemids[num], row[1]);
294 		num++;
295 	}
296 	DBfree_result(result);
297 
298 	if (0 < num)
299 	{
300 		DCconfig_get_items_by_itemids(items, itemids, errcodes, num);
301 
302 		for (i = 0; i < num; i++)
303 		{
304 			if (SUCCEED != errcodes[i])
305 				continue;
306 
307 			if (ITEM_STATUS_ACTIVE != items[i].status)
308 				continue;
309 
310 			if (HOST_STATUS_MONITORED != items[i].host.status)
311 				continue;
312 
313 			if (HOST_MAINTENANCE_STATUS_ON == items[i].host.maintenance_status &&
314 					MAINTENANCE_TYPE_NODATA == items[i].host.maintenance_type)
315 			{
316 				continue;
317 			}
318 
319 			init_result(&value);
320 
321 			switch (types[i])
322 			{
323 				case ZBX_HTTPITEM_TYPE_RSPCODE:
324 					SET_UI64_RESULT(&value, stat->rspcode);
325 					break;
326 				case ZBX_HTTPITEM_TYPE_TIME:
327 					SET_DBL_RESULT(&value, stat->total_time);
328 					break;
329 				case ZBX_HTTPITEM_TYPE_SPEED:
330 					SET_DBL_RESULT(&value, stat->speed_download);
331 					break;
332 			}
333 
334 			items[i].state = ITEM_STATE_NORMAL;
335 			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
336 					ts, items[i].state, NULL);
337 
338 			free_result(&value);
339 		}
340 
341 		DCconfig_clean_items(items, errcodes, num);
342 	}
343 
344 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
345 }
346 
347 /******************************************************************************
348  *                                                                            *
349  * Function: httpstep_load_pairs                                              *
350  *                                                                            *
351  * Purpose: loads http fields of web scenario step                            *
352  *                                                                            *
353  * Parameters: host            - [IN] host to be used in macro expansion      *
354  *             httpstep        - [IN/OUT] web scenario step                   *
355  *                                                                            *
356  * Return value: SUCCEED if http fields were loaded and macro expansion was   *
357  *               successful. FAIL on error.                                   *
358  *                                                                            *
359  ******************************************************************************/
httpstep_load_pairs(DC_HOST * host,zbx_httpstep_t * httpstep)360 static int	httpstep_load_pairs(DC_HOST *host, zbx_httpstep_t *httpstep)
361 {
362 	int			type, ret = SUCCEED;
363 	DB_RESULT		result;
364 	DB_ROW			row;
365 	size_t			alloc_len = 0, offset;
366 	zbx_ptr_pair_t		pair;
367 	zbx_vector_ptr_pair_t	*vector, headers, query_fields, post_fields;
368 	char			*key, *value, *url = NULL, query_delimiter = '?';
369 
370 	httpstep->url = NULL;
371 	httpstep->posts = NULL;
372 	httpstep->headers = NULL;
373 
374 	zbx_vector_ptr_pair_create(&headers);
375 	zbx_vector_ptr_pair_create(&query_fields);
376 	zbx_vector_ptr_pair_create(&post_fields);
377 	zbx_vector_ptr_pair_create(&httpstep->variables);
378 
379 	result = DBselect(
380 			"select name,value,type"
381 			" from httpstep_field"
382 			" where httpstepid=" ZBX_FS_UI64
383 			" order by httpstep_fieldid",
384 			httpstep->httpstep->httpstepid);
385 
386 	while (NULL != (row = DBfetch(result)))
387 	{
388 		type = atoi(row[2]);
389 
390 		value = zbx_strdup(NULL, row[1]);
391 
392 		/* from now on variable values can contain macros so proper URL encoding can be performed */
393 		if (SUCCEED != (ret = substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
394 				NULL, NULL, &value, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0)))
395 		{
396 			zbx_free(value);
397 			goto out;
398 		}
399 
400 		key = zbx_strdup(NULL, row[0]);
401 
402 		/* variable names cannot contain macros, and both variable names and variable values cannot contain */
403 		/* another variables */
404 		if (ZBX_HTTPFIELD_VARIABLE != type && (SUCCEED != (ret = substitute_simple_macros(NULL, NULL, NULL,
405 				NULL, NULL, host, NULL, NULL, NULL, NULL, &key, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0)) ||
406 				SUCCEED != (ret = http_substitute_variables(httpstep->httptest, &key)) ||
407 				SUCCEED != (ret = http_substitute_variables(httpstep->httptest, &value))))
408 		{
409 			httppairs_free(&httpstep->variables);
410 			zbx_free(key);
411 			zbx_free(value);
412 			goto out;
413 		}
414 
415 		/* keys and values of query fields / post fields should be encoded */
416 		if (ZBX_HTTPFIELD_QUERY_FIELD == type || ZBX_HTTPFIELD_POST_FIELD == type)
417 		{
418 			zbx_http_url_encode(key, &key);
419 			zbx_http_url_encode(value, &value);
420 		}
421 
422 		switch (type)
423 		{
424 			case ZBX_HTTPFIELD_HEADER:
425 				vector = &headers;
426 				break;
427 			case ZBX_HTTPFIELD_VARIABLE:
428 				vector = &httpstep->variables;
429 				break;
430 			case ZBX_HTTPFIELD_QUERY_FIELD:
431 				vector = &query_fields;
432 				break;
433 			case ZBX_HTTPFIELD_POST_FIELD:
434 				vector = &post_fields;
435 				break;
436 			default:
437 				THIS_SHOULD_NEVER_HAPPEN;
438 				zbx_free(key);
439 				zbx_free(value);
440 				ret = FAIL;
441 				goto out;
442 		}
443 
444 		pair.first = key;
445 		pair.second = value;
446 
447 		zbx_vector_ptr_pair_append(vector, pair);
448 	}
449 
450 	/* URL is created from httpstep->httpstep->url, query_fields and fragment */
451 	zbx_strcpy_alloc(&url, &alloc_len, &offset, httpstep->httpstep->url);
452 
453 	value = strchr(url, '#');
454 
455 	if (NULL != value)
456 	{
457 		/* URL contains fragment delimiter, so it must be dropped */
458 
459 		zabbix_log(LOG_LEVEL_DEBUG, "URL contains fragment delimiter, fragment part is deleted from URL");
460 		*value = '\0';
461 		offset = value - url;
462 	}
463 
464 	if (0 < query_fields.values_num)
465 	{
466 		/* url can contain '?' so proper delimiter should be selected */
467 		if (NULL != strchr(url, '?'))
468 			query_delimiter = '&';
469 
470 		zbx_chrcpy_alloc(&url, &alloc_len, &offset, query_delimiter);
471 		httpstep_pairs_join(&url, &alloc_len, &offset, "=", "&", &query_fields);
472 	}
473 
474 	if (SUCCEED != (ret = zbx_http_punycode_encode_url(&url)))
475 	{
476 		zabbix_log(LOG_LEVEL_WARNING, "cannot encode unicode URL into punycode");
477 		httppairs_free(&httpstep->variables);
478 		zbx_free(url);
479 		goto out;
480 	}
481 
482 	httpstep->url = url;
483 
484 	/* POST data can be saved as raw data or as form data */
485 	if (ZBX_POSTTYPE_FORM == httpstep->httpstep->post_type)
486 		httpstep_pairs_join(&httpstep->posts, &alloc_len, &offset, "=", "&", &post_fields);
487 	else
488 		httpstep->posts = httpstep->httpstep->posts;	/* post data in raw format */
489 
490 	httpstep_pairs_join(&httpstep->headers, &alloc_len, &offset, ":", "\r\n", &headers);
491 out:
492 	httppairs_free(&headers);
493 	httppairs_free(&query_fields);
494 	httppairs_free(&post_fields);
495 	DBfree_result(result);
496 
497 	return ret;
498 }
499 
500 /******************************************************************************
501  *                                                                            *
502  * Function: add_http_headers                                                 *
503  *                                                                            *
504  * Purpose: adds HTTP headers to curl_slist and prepares cookie header string *
505  *                                                                            *
506  * Parameters: headers         - [IN] HTTP headers as string                  *
507  *             headers_slist   - [IN/OUT] curl_slist                          *
508  *             header_cookie   - [IN/OUT] cookie header as string             *
509  *                                                                            *
510  ******************************************************************************/
add_http_headers(char * headers,struct curl_slist ** headers_slist,char ** header_cookie)511 static void	add_http_headers(char *headers, struct curl_slist **headers_slist, char **header_cookie)
512 {
513 #define COOKIE_HEADER_STR	"Cookie:"
514 #define COOKIE_HEADER_STR_LEN	ZBX_CONST_STRLEN(COOKIE_HEADER_STR)
515 
516 	char	*line;
517 
518 	while (NULL != (line = zbx_http_parse_header(&headers)))
519 	{
520 		if (0 == strncmp(COOKIE_HEADER_STR, line, COOKIE_HEADER_STR_LEN))
521 			*header_cookie = zbx_strdup(*header_cookie, line + COOKIE_HEADER_STR_LEN);
522 		else
523 			*headers_slist = curl_slist_append(*headers_slist, line);
524 
525 		zbx_free(line);
526 	}
527 
528 #undef COOKIE_HEADER_STR
529 #undef COOKIE_HEADER_STR_LEN
530 }
531 #endif
532 
533 /******************************************************************************
534  *                                                                            *
535  * Function: httptest_load_pairs                                              *
536  *                                                                            *
537  * Purpose: loads http fields of web scenario                                 *
538  *                                                                            *
539  * Parameters: host            - [IN] host to be used in macro expansion      *
540  *             httptest        - [IN/OUT] web scenario                        *
541  *                                                                            *
542  * Return value: SUCCEED if http fields were loaded and macro expansion was   *
543  *               successful. FAIL on error.                                   *
544  *                                                                            *
545  ******************************************************************************/
httptest_load_pairs(DC_HOST * host,zbx_httptest_t * httptest)546 static int	httptest_load_pairs(DC_HOST *host, zbx_httptest_t *httptest)
547 {
548 	int			type, ret = SUCCEED;
549 	DB_RESULT		result;
550 	DB_ROW			row;
551 	size_t			alloc_len = 0, offset;
552 	zbx_ptr_pair_t		pair;
553 	zbx_vector_ptr_pair_t	*vector, headers;
554 	char			*key, *value;
555 
556 	zbx_vector_ptr_pair_create(&headers);
557 	zbx_vector_ptr_pair_create(&httptest->variables);
558 
559 	httptest->headers = NULL;
560 	result = DBselect(
561 			"select name,value,type"
562 			" from httptest_field"
563 			" where httptestid=" ZBX_FS_UI64
564 			" order by httptest_fieldid",
565 			httptest->httptest.httptestid);
566 
567 	while (NULL != (row = DBfetch(result)))
568 	{
569 		type = atoi(row[2]);
570 		value = zbx_strdup(NULL, row[1]);
571 
572 		/* from now on variable values can contain macros so proper URL encoding can be performed */
573 		if (SUCCEED != (ret = substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
574 				NULL, NULL, &value, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0)))
575 		{
576 			zbx_free(value);
577 			goto out;
578 		}
579 
580 		key = zbx_strdup(NULL, row[0]);
581 
582 		/* variable names cannot contain macros, and both variable names and variable values cannot contain */
583 		/* another variables */
584 		if (ZBX_HTTPFIELD_VARIABLE != type && SUCCEED != (ret = substitute_simple_macros(NULL, NULL, NULL,
585 				NULL, NULL, host, NULL, NULL, NULL, NULL, &key, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0)))
586 		{
587 			httppairs_free(&httptest->variables);
588 			zbx_free(key);
589 			zbx_free(value);
590 			goto out;
591 		}
592 
593 		switch (type)
594 		{
595 			case ZBX_HTTPFIELD_HEADER:
596 				vector = &headers;
597 				break;
598 			case ZBX_HTTPFIELD_VARIABLE:
599 				vector = &httptest->variables;
600 				break;
601 			default:
602 				zbx_free(key);
603 				zbx_free(value);
604 				ret = FAIL;
605 				goto out;
606 		}
607 
608 		pair.first = key;
609 		pair.second = value;
610 
611 		zbx_vector_ptr_pair_append(vector, pair);
612 	}
613 
614 	httpstep_pairs_join(&httptest->headers, &alloc_len, &offset, ":", "\r\n", &headers);
615 out:
616 	httppairs_free(&headers);
617 	DBfree_result(result);
618 
619 	return ret;
620 }
621 
622 /******************************************************************************
623  *                                                                            *
624  * Function: process_httptest                                                 *
625  *                                                                            *
626  * Purpose: process single scenario of http test                              *
627  *                                                                            *
628  * Parameters:                                                                *
629  *                                                                            *
630  * Return value:                                                              *
631  *                                                                            *
632  * Author: Alexei Vladishev                                                   *
633  *                                                                            *
634  * Comments:                                                                  *
635  *                                                                            *
636  ******************************************************************************/
process_httptest(DC_HOST * host,zbx_httptest_t * httptest)637 static void	process_httptest(DC_HOST *host, zbx_httptest_t *httptest)
638 {
639 	DB_RESULT	result;
640 	DB_HTTPSTEP	db_httpstep;
641 	char		*err_str = NULL, *buffer = NULL;
642 	int		lastfailedstep = 0;
643 	zbx_timespec_t	ts;
644 	int		delay;
645 	double		speed_download = 0;
646 	int		speed_download_num = 0;
647 #ifdef HAVE_LIBCURL
648 	DB_ROW		row;
649 	zbx_httpstat_t	stat;
650 	char		errbuf[CURL_ERROR_SIZE];
651 	CURL		*easyhandle = NULL;
652 	CURLcode	err;
653 	zbx_httpstep_t	httpstep;
654 #endif
655 
656 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() httptestid:" ZBX_FS_UI64 " name:'%s'",
657 			__func__, httptest->httptest.httptestid, httptest->httptest.name);
658 
659 	result = DBselect(
660 			"select httpstepid,no,name,url,timeout,posts,required,status_codes,post_type,follow_redirects,"
661 				"retrieve_mode"
662 			" from httpstep"
663 			" where httptestid=" ZBX_FS_UI64
664 			" order by no",
665 			httptest->httptest.httptestid);
666 
667 	buffer = zbx_strdup(buffer, httptest->httptest.delay);
668 	substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL, &buffer,
669 			MACRO_TYPE_COMMON, NULL, 0);
670 
671 	/* Avoid the potential usage of uninitialized values when: */
672 	/* 1) compile without libCURL support */
673 	/* 2) update interval is invalid */
674 	db_httpstep.name = NULL;
675 
676 	if (SUCCEED != is_time_suffix(buffer, &delay, ZBX_LENGTH_UNLIMITED))
677 	{
678 		err_str = zbx_dsprintf(err_str, "update interval \"%s\" is invalid", buffer);
679 		lastfailedstep = -1;
680 		goto httptest_error;
681 	}
682 
683 #ifdef HAVE_LIBCURL
684 	if (NULL == (easyhandle = curl_easy_init()))
685 	{
686 		err_str = zbx_strdup(err_str, "cannot initialize cURL library");
687 		goto clean;
688 	}
689 
690 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_PROXY, httptest->httptest.http_proxy)) ||
691 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, "")) ||
692 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, httptest->httptest.agent)) ||
693 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, errbuf)))
694 	{
695 		err_str = zbx_strdup(err_str, curl_easy_strerror(err));
696 		goto clean;
697 	}
698 
699 	if (SUCCEED != zbx_http_prepare_ssl(easyhandle, httptest->httptest.ssl_cert_file,
700 			httptest->httptest.ssl_key_file, httptest->httptest.ssl_key_password,
701 			httptest->httptest.verify_peer, httptest->httptest.verify_host, &err_str))
702 	{
703 		goto clean;
704 	}
705 
706 	httpstep.httptest = httptest;
707 	httpstep.httpstep = &db_httpstep;
708 
709 	while (NULL != (row = DBfetch(result)) && ZBX_IS_RUNNING())
710 	{
711 		struct curl_slist	*headers_slist = NULL;
712 		char			*header_cookie = NULL;
713 		size_t			(*curl_header_cb)(void *ptr, size_t size, size_t nmemb, void *userdata);
714 		size_t			(*curl_body_cb)(void *ptr, size_t size, size_t nmemb, void *userdata);
715 
716 		/* NOTE: do not break or return from this block! */
717 		/*       process_step_data() call is required! */
718 
719 		ZBX_STR2UINT64(db_httpstep.httpstepid, row[0]);
720 		db_httpstep.httptestid = httptest->httptest.httptestid;
721 		db_httpstep.no = atoi(row[1]);
722 		db_httpstep.name = row[2];
723 
724 		db_httpstep.url = zbx_strdup(NULL, row[3]);
725 		substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL,
726 				&db_httpstep.url, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
727 		http_substitute_variables(httptest, &db_httpstep.url);
728 
729 		db_httpstep.required = zbx_strdup(NULL, row[6]);
730 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL,
731 				&db_httpstep.required, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
732 
733 		db_httpstep.status_codes = zbx_strdup(NULL, row[7]);
734 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL,
735 				&db_httpstep.status_codes, MACRO_TYPE_COMMON, NULL, 0);
736 
737 		db_httpstep.post_type = atoi(row[8]);
738 
739 		if (ZBX_POSTTYPE_RAW == db_httpstep.post_type)
740 		{
741 			db_httpstep.posts = zbx_strdup(NULL, row[5]);
742 			substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL,
743 					&db_httpstep.posts, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
744 			http_substitute_variables(httptest, &db_httpstep.posts);
745 		}
746 		else
747 			db_httpstep.posts = NULL;
748 
749 		if (SUCCEED != httpstep_load_pairs(host, &httpstep))
750 		{
751 			err_str = zbx_strdup(err_str, "cannot load web scenario step data");
752 			goto httpstep_error;
753 		}
754 
755 		buffer = zbx_strdup(buffer, row[4]);
756 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL, NULL, NULL, &buffer,
757 				MACRO_TYPE_COMMON, NULL, 0);
758 
759 		if (SUCCEED != is_time_suffix(buffer, &db_httpstep.timeout, ZBX_LENGTH_UNLIMITED))
760 		{
761 			err_str = zbx_dsprintf(err_str, "timeout \"%s\" is invalid", buffer);
762 			goto httpstep_error;
763 		}
764 		else if (db_httpstep.timeout < 1 || SEC_PER_HOUR < db_httpstep.timeout)
765 		{
766 			err_str = zbx_dsprintf(err_str, "timeout \"%s\" is out of 1-3600 seconds bounds", buffer);
767 			goto httpstep_error;
768 		}
769 
770 		db_httpstep.follow_redirects = atoi(row[9]);
771 		db_httpstep.retrieve_mode = atoi(row[10]);
772 
773 		memset(&stat, 0, sizeof(stat));
774 
775 		zabbix_log(LOG_LEVEL_DEBUG, "%s() use step \"%s\"", __func__, db_httpstep.name);
776 		zabbix_log(LOG_LEVEL_DEBUG, "%s() use post \"%s\"", __func__, ZBX_NULL2EMPTY_STR(httpstep.posts));
777 
778 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, httpstep.posts)))
779 		{
780 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
781 			goto httpstep_error;
782 		}
783 
784 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_POST, (NULL != httpstep.posts &&
785 				'\0' != *httpstep.posts) ? 1L : 0L)))
786 		{
787 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
788 			goto httpstep_error;
789 		}
790 
791 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION,
792 				0 == db_httpstep.follow_redirects ? 0L : 1L)))
793 		{
794 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
795 			goto httpstep_error;
796 		}
797 
798 		if (0 != db_httpstep.follow_redirects)
799 		{
800 			if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_MAXREDIRS, ZBX_CURLOPT_MAXREDIRS)))
801 			{
802 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
803 				goto httpstep_error;
804 			}
805 		}
806 
807 		/* headers defined in a step overwrite headers defined in scenario */
808 		if (NULL != httpstep.headers && '\0' != *httpstep.headers)
809 			add_http_headers(httpstep.headers, &headers_slist, &header_cookie);
810 		else if (NULL != httptest->headers && '\0' != *httptest->headers)
811 			add_http_headers(httptest->headers, &headers_slist, &header_cookie);
812 
813 		err = curl_easy_setopt(easyhandle, CURLOPT_COOKIE, header_cookie);
814 		zbx_free(header_cookie);
815 
816 		if (CURLE_OK != err)
817 		{
818 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
819 			goto httpstep_error;
820 		}
821 
822 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers_slist)))
823 		{
824 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
825 			goto httpstep_error;
826 		}
827 
828 		switch (db_httpstep.retrieve_mode)
829 		{
830 			case ZBX_RETRIEVE_MODE_CONTENT:
831 				curl_header_cb = curl_ignore_cb;
832 				curl_body_cb = curl_write_cb;
833 				break;
834 			case ZBX_RETRIEVE_MODE_BOTH:
835 				curl_header_cb = curl_body_cb = curl_write_cb;
836 				break;
837 			case ZBX_RETRIEVE_MODE_HEADERS:
838 				curl_header_cb = curl_write_cb;
839 				curl_body_cb = curl_ignore_cb;
840 				break;
841 			default:
842 				THIS_SHOULD_NEVER_HAPPEN;
843 				err_str = zbx_strdup(err_str, "invalid retrieve mode");
844 				goto httpstep_error;
845 		}
846 
847 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, curl_body_cb)) ||
848 				CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, curl_header_cb)))
849 		{
850 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
851 			goto httpstep_error;
852 		}
853 
854 		/* enable/disable fetching the body */
855 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_NOBODY,
856 				ZBX_RETRIEVE_MODE_HEADERS == db_httpstep.retrieve_mode ? 1L : 0L)))
857 		{
858 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
859 			goto httpstep_error;
860 		}
861 
862 		if (SUCCEED != zbx_http_prepare_auth(easyhandle, httptest->httptest.authentication,
863 				httptest->httptest.http_user, httptest->httptest.http_password, &err_str))
864 		{
865 			goto httpstep_error;
866 		}
867 
868 		zabbix_log(LOG_LEVEL_DEBUG, "%s() go to URL \"%s\"", __func__, httpstep.url);
869 
870 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, (long)db_httpstep.timeout)) ||
871 				CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_URL, httpstep.url)))
872 		{
873 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
874 			goto httpstep_error;
875 		}
876 
877 		/* try to retrieve page several times depending on number of retries */
878 		do
879 		{
880 			memset(&page, 0, sizeof(page));
881 			errbuf[0] = '\0';
882 
883 			if (CURLE_OK == (err = curl_easy_perform(easyhandle)))
884 				break;
885 
886 			zbx_free(page.data);
887 		}
888 		while (0 < --httptest->httptest.retries);
889 
890 		curl_slist_free_all(headers_slist);	/* must be called after curl_easy_perform() */
891 
892 		if (CURLE_OK == err)
893 		{
894 			char	*var_err_str = NULL;
895 
896 			zabbix_log(LOG_LEVEL_TRACE, "%s() page.data from %s:'%s'", __func__, httpstep.url, page.data);
897 
898 			/* first get the data that is needed even if step fails */
899 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_RESPONSE_CODE, &stat.rspcode)))
900 			{
901 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
902 			}
903 			else if ('\0' != *db_httpstep.status_codes &&
904 					FAIL == int_in_list(db_httpstep.status_codes, stat.rspcode))
905 			{
906 				err_str = zbx_dsprintf(err_str, "response code \"%ld\" did not match any of the"
907 						" required status codes \"%s\"", stat.rspcode,
908 						db_httpstep.status_codes);
909 			}
910 
911 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_TOTAL_TIME, &stat.total_time)) &&
912 					NULL == err_str)
913 			{
914 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
915 			}
916 
917 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_SPEED_DOWNLOAD,
918 					&stat.speed_download)) && NULL == err_str)
919 			{
920 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
921 			}
922 			else
923 			{
924 				speed_download += stat.speed_download;
925 				speed_download_num++;
926 			}
927 
928 			/* required pattern */
929 			if (NULL == err_str && '\0' != *db_httpstep.required &&
930 					NULL == zbx_regexp_match(page.data, db_httpstep.required, NULL))
931 			{
932 				err_str = zbx_dsprintf(err_str, "required pattern \"%s\" was not found on %s",
933 						db_httpstep.required, httpstep.url);
934 			}
935 
936 			/* variables defined in scenario */
937 			if (NULL == err_str && FAIL == http_process_variables(httptest, &httptest->variables, page.data,
938 					&var_err_str))
939 			{
940 				char	*variables = NULL;
941 				size_t	alloc_len = 0, offset;
942 
943 				httpstep_pairs_join(&variables, &alloc_len, &offset, "=", " ", &httptest->variables);
944 
945 				err_str = zbx_dsprintf(err_str, "error in scenario variables \"%s\": %s", variables,
946 						var_err_str);
947 
948 				zbx_free(variables);
949 			}
950 
951 			/* variables defined in a step */
952 			if (NULL == err_str && FAIL == http_process_variables(httptest, &httpstep.variables, page.data,
953 					&var_err_str))
954 			{
955 				char	*variables = NULL;
956 				size_t	alloc_len = 0, offset;
957 
958 				httpstep_pairs_join(&variables, &alloc_len, &offset, "=", " ", &httpstep.variables);
959 
960 				err_str = zbx_dsprintf(err_str, "error in step variables \"%s\": %s", variables,
961 						var_err_str);
962 
963 				zbx_free(variables);
964 			}
965 
966 			zbx_free(var_err_str);
967 
968 			zbx_timespec(&ts);
969 			process_step_data(db_httpstep.httpstepid, &stat, &ts);
970 
971 			zbx_free(page.data);
972 		}
973 		else
974 			err_str = zbx_dsprintf(err_str, "%s: %s", curl_easy_strerror(err), errbuf);
975 
976 httpstep_error:
977 		zbx_free(db_httpstep.status_codes);
978 		zbx_free(db_httpstep.required);
979 		zbx_free(db_httpstep.posts);
980 		zbx_free(db_httpstep.url);
981 
982 		httppairs_free(&httpstep.variables);
983 
984 		if (ZBX_POSTTYPE_FORM == httpstep.httpstep->post_type)
985 			zbx_free(httpstep.posts);
986 
987 		zbx_free(httpstep.url);
988 		zbx_free(httpstep.headers);
989 
990 		if (NULL != err_str)
991 		{
992 			lastfailedstep = db_httpstep.no;
993 			break;
994 		}
995 	}
996 clean:
997 	curl_easy_cleanup(easyhandle);
998 #else
999 	err_str = zbx_strdup(err_str, "cURL library is required for Web monitoring support");
1000 #endif	/* HAVE_LIBCURL */
1001 
1002 httptest_error:
1003 	zbx_timespec(&ts);
1004 
1005 	if (0 > lastfailedstep)	/* update interval is invalid, delay is uninitialized */
1006 	{
1007 		DBexecute("update httptest set nextcheck=%d where httptestid=" ZBX_FS_UI64,
1008 				0 > ts.sec ? ZBX_JAN_2038 : ts.sec, httptest->httptest.httptestid);
1009 	}
1010 	else if (0 > ts.sec + delay)
1011 	{
1012 		zabbix_log(LOG_LEVEL_WARNING, "nextcheck update causes overflow for web scenario \"%s\" on host \"%s\"",
1013 				httptest->httptest.name, host->name);
1014 		DBexecute("update httptest set nextcheck=%d where httptestid=" ZBX_FS_UI64,
1015 				ZBX_JAN_2038, httptest->httptest.httptestid);
1016 	}
1017 	else
1018 	{
1019 		DBexecute("update httptest set nextcheck=%d where httptestid=" ZBX_FS_UI64,
1020 				ts.sec + delay, httptest->httptest.httptestid);
1021 	}
1022 
1023 	if (NULL != err_str)
1024 	{
1025 		if (0 >= lastfailedstep)
1026 		{
1027 			/* we are here because web scenario update interval is invalid, */
1028 			/* cURL initialization failed or we have been compiled without cURL library */
1029 
1030 			lastfailedstep = 1;
1031 		}
1032 
1033 		if (NULL != db_httpstep.name)
1034 		{
1035 			zabbix_log(LOG_LEVEL_DEBUG, "cannot process step \"%s\" of web scenario \"%s\" on host \"%s\": "
1036 					"%s", db_httpstep.name, httptest->httptest.name, host->name, err_str);
1037 		}
1038 	}
1039 	DBfree_result(result);
1040 
1041 	if (0 != speed_download_num)
1042 		speed_download /= speed_download_num;
1043 
1044 	process_test_data(httptest->httptest.httptestid, lastfailedstep, speed_download, err_str, &ts);
1045 
1046 	zbx_free(buffer);
1047 	zbx_free(err_str);
1048 	zbx_preprocessor_flush();
1049 
1050 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1051 }
1052 
1053 /******************************************************************************
1054  *                                                                            *
1055  * Function: process_httptests                                                *
1056  *                                                                            *
1057  * Purpose: process httptests                                                 *
1058  *                                                                            *
1059  * Parameters: now - current timestamp                                        *
1060  *                                                                            *
1061  * Return value: number of processed httptests                                *
1062  *                                                                            *
1063  * Author: Alexei Vladishev                                                   *
1064  *                                                                            *
1065  * Comments: always SUCCEED                                                   *
1066  *                                                                            *
1067  ******************************************************************************/
process_httptests(int httppoller_num,int now)1068 int	process_httptests(int httppoller_num, int now)
1069 {
1070 	DB_RESULT	result;
1071 	DB_ROW		row;
1072 	zbx_httptest_t	httptest;
1073 	DC_HOST		host;
1074 	int		httptests_count = 0;
1075 
1076 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1077 
1078 	/* create macro cache to use in http tests */
1079 	zbx_vector_ptr_pair_create(&httptest.macros);
1080 
1081 	result = DBselect(
1082 			"select h.hostid,h.host,h.name,t.httptestid,t.name,t.agent,"
1083 				"t.authentication,t.http_user,t.http_password,t.http_proxy,t.retries,t.ssl_cert_file,"
1084 				"t.ssl_key_file,t.ssl_key_password,t.verify_peer,t.verify_host,t.delay"
1085 			" from httptest t,hosts h"
1086 			" where t.hostid=h.hostid"
1087 				" and t.nextcheck<=%d"
1088 				" and " ZBX_SQL_MOD(t.httptestid,%d) "=%d"
1089 				" and t.status=%d"
1090 				" and h.proxy_hostid is null"
1091 				" and h.status=%d"
1092 				" and (h.maintenance_status=%d or h.maintenance_type=%d)",
1093 			now,
1094 			CONFIG_HTTPPOLLER_FORKS, httppoller_num - 1,
1095 			HTTPTEST_STATUS_MONITORED,
1096 			HOST_STATUS_MONITORED,
1097 			HOST_MAINTENANCE_STATUS_OFF, MAINTENANCE_TYPE_NORMAL);
1098 
1099 	while (NULL != (row = DBfetch(result)) && ZBX_IS_RUNNING())
1100 	{
1101 		ZBX_STR2UINT64(host.hostid, row[0]);
1102 		strscpy(host.host, row[1]);
1103 		zbx_strlcpy_utf8(host.name, row[2], sizeof(host.name));
1104 
1105 		ZBX_STR2UINT64(httptest.httptest.httptestid, row[3]);
1106 		httptest.httptest.name = row[4];
1107 
1108 		if (SUCCEED != httptest_load_pairs(&host, &httptest))
1109 		{
1110 			zabbix_log(LOG_LEVEL_WARNING, "cannot process web scenario \"%s\" on host \"%s\": "
1111 					"cannot load web scenario data", httptest.httptest.name, host.name);
1112 			THIS_SHOULD_NEVER_HAPPEN;
1113 			continue;
1114 		}
1115 
1116 		httptest.httptest.agent = zbx_strdup(NULL, row[5]);
1117 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL, NULL, NULL,
1118 				&httptest.httptest.agent, MACRO_TYPE_COMMON, NULL, 0);
1119 
1120 		if (HTTPTEST_AUTH_NONE != (httptest.httptest.authentication = atoi(row[6])))
1121 		{
1122 			httptest.httptest.http_user = zbx_strdup(NULL, row[7]);
1123 			substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL, NULL,
1124 					NULL, &httptest.httptest.http_user, MACRO_TYPE_COMMON, NULL, 0);
1125 
1126 			httptest.httptest.http_password = zbx_strdup(NULL, row[8]);
1127 			substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL, NULL,
1128 					NULL, &httptest.httptest.http_password, MACRO_TYPE_COMMON, NULL, 0);
1129 		}
1130 
1131 		if ('\0' != *row[9])
1132 		{
1133 			httptest.httptest.http_proxy = zbx_strdup(NULL, row[9]);
1134 			substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL, NULL, NULL,
1135 					&httptest.httptest.http_proxy, MACRO_TYPE_COMMON, NULL, 0);
1136 		}
1137 		else
1138 			httptest.httptest.http_proxy = NULL;
1139 
1140 		httptest.httptest.retries = atoi(row[10]);
1141 
1142 		httptest.httptest.ssl_cert_file = zbx_strdup(NULL, row[11]);
1143 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL, NULL, NULL,
1144 				&httptest.httptest.ssl_cert_file, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
1145 
1146 		httptest.httptest.ssl_key_file = zbx_strdup(NULL, row[12]);
1147 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL, NULL, NULL,
1148 				&httptest.httptest.ssl_key_file, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
1149 
1150 		httptest.httptest.ssl_key_password = zbx_strdup(NULL, row[13]);
1151 		substitute_simple_macros_unmasked(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL, NULL, NULL,
1152 				&httptest.httptest.ssl_key_password, MACRO_TYPE_COMMON, NULL, 0);
1153 
1154 		httptest.httptest.verify_peer = atoi(row[14]);
1155 		httptest.httptest.verify_host = atoi(row[15]);
1156 
1157 		httptest.httptest.delay = row[16];
1158 
1159 		/* add httptest variables to the current test macro cache */
1160 		http_process_variables(&httptest, &httptest.variables, NULL, NULL);
1161 
1162 		process_httptest(&host, &httptest);
1163 
1164 		zbx_free(httptest.httptest.ssl_key_password);
1165 		zbx_free(httptest.httptest.ssl_key_file);
1166 		zbx_free(httptest.httptest.ssl_cert_file);
1167 		zbx_free(httptest.httptest.http_proxy);
1168 
1169 		if (HTTPTEST_AUTH_NONE != httptest.httptest.authentication)
1170 		{
1171 			zbx_free(httptest.httptest.http_password);
1172 			zbx_free(httptest.httptest.http_user);
1173 		}
1174 		zbx_free(httptest.httptest.agent);
1175 		zbx_free(httptest.headers);
1176 		httppairs_free(&httptest.variables);
1177 
1178 		/* clear the macro cache used in this http test */
1179 		httptest_remove_macros(&httptest);
1180 
1181 		httptests_count++;	/* performance metric */
1182 	}
1183 	/* destroy the macro cache used in http tests */
1184 	zbx_vector_ptr_pair_destroy(&httptest.macros);
1185 
1186 	DBfree_result(result);
1187 
1188 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1189 
1190 	return httptests_count;
1191 }
1192