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 
26 #include "zbxserver.h"
27 #include "httpmacro.h"
28 #include "httptest.h"
29 #include "zbxregexp.h"
30 
31 typedef struct
32 {
33 	char	*data;
34 	size_t	allocated;
35 	size_t	offset;
36 }
37 zbx_httppage_t;
38 
39 typedef struct
40 {
41 	long   	rspcode;
42 	double 	total_time;
43 	double 	speed_download;
44 	double	test_total_time;
45 	int	test_last_step;
46 }
47 zbx_httpstat_t;
48 
49 extern int	CONFIG_HTTPPOLLER_FORKS;
50 extern char	*CONFIG_SOURCE_IP;
51 
52 #ifdef HAVE_LIBCURL
53 
54 extern char	*CONFIG_SSL_CA_LOCATION;
55 extern char	*CONFIG_SSL_CERT_LOCATION;
56 extern char	*CONFIG_SSL_KEY_LOCATION;
57 
58 #define ZBX_RETRIEVE_MODE_CONTENT	0
59 #define ZBX_RETRIEVE_MODE_HEADERS	1
60 
61 static zbx_httppage_t	page;
62 
WRITEFUNCTION2(void * ptr,size_t size,size_t nmemb,void * userdata)63 static size_t	WRITEFUNCTION2(void *ptr, size_t size, size_t nmemb, void *userdata)
64 {
65 	size_t	r_size = size * nmemb;
66 
67 	/* first piece of data */
68 	if (NULL == page.data)
69 	{
70 		page.allocated = MAX(8096, r_size);
71 		page.offset = 0;
72 		page.data = zbx_malloc(page.data, page.allocated);
73 	}
74 
75 	zbx_strncpy_alloc(&page.data, &page.allocated, &page.offset, ptr, r_size);
76 
77 	return r_size;
78 }
79 
HEADERFUNCTION2(void * ptr,size_t size,size_t nmemb,void * userdata)80 static size_t	HEADERFUNCTION2(void *ptr, size_t size, size_t nmemb, void *userdata)
81 {
82 	return size * nmemb;
83 }
84 
85 #endif	/* HAVE_LIBCURL */
86 
87 /******************************************************************************
88  *                                                                            *
89  * Function: httptest_remove_macros                                           *
90  *                                                                            *
91  * Purpose: remove all macro variables cached during http test execution      *
92  *                                                                            *
93  * Parameters: httptest - [IN] the http test data                             *
94  *                                                                            *
95  * Author: Andris Zeila                                                       *
96  *                                                                            *
97  ******************************************************************************/
httptest_remove_macros(zbx_httptest_t * httptest)98 static void	httptest_remove_macros(zbx_httptest_t *httptest)
99 {
100 	int	i;
101 
102 	for (i = 0; i < httptest->macros.values_num; i++)
103 	{
104 		zbx_ptr_pair_t	*pair = &httptest->macros.values[i];
105 
106 		zbx_free(pair->first);
107 		zbx_free(pair->second);
108 	}
109 
110 	zbx_vector_ptr_pair_clear(&httptest->macros);
111 }
112 
process_test_data(zbx_uint64_t httptestid,int lastfailedstep,double speed_download,const char * err_str,zbx_timespec_t * ts)113 static void	process_test_data(zbx_uint64_t httptestid, int lastfailedstep, double speed_download,
114 		const char *err_str, zbx_timespec_t *ts)
115 {
116 	const char	*__function_name = "process_test_data";
117 
118 	DB_RESULT	result;
119 	DB_ROW		row;
120 	unsigned char	types[3];
121 	DC_ITEM		items[3];
122 	zbx_uint64_t	itemids[3];
123 	int		errcodes[3];
124 	size_t		i, num = 0;
125 	AGENT_RESULT    value;
126 
127 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
128 
129 	result = DBselect("select type,itemid from httptestitem where httptestid=" ZBX_FS_UI64, httptestid);
130 
131 	while (NULL != (row = DBfetch(result)))
132 	{
133 		if (3 == num)
134 		{
135 			THIS_SHOULD_NEVER_HAPPEN;
136 			break;
137 		}
138 
139 		switch (types[num] = (unsigned char)atoi(row[0]))
140 		{
141 			case ZBX_HTTPITEM_TYPE_SPEED:
142 			case ZBX_HTTPITEM_TYPE_LASTSTEP:
143 				break;
144 			case ZBX_HTTPITEM_TYPE_LASTERROR:
145 				if (NULL == err_str)
146 					continue;
147 				break;
148 			default:
149 				THIS_SHOULD_NEVER_HAPPEN;
150 				continue;
151 		}
152 
153 		ZBX_STR2UINT64(itemids[num], row[1]);
154 		num++;
155 	}
156 	DBfree_result(result);
157 
158 	DCconfig_get_items_by_itemids(items, itemids, errcodes, num);
159 
160 	for (i = 0; i < num; i++)
161 	{
162 		if (SUCCEED != errcodes[i])
163 			continue;
164 
165 		if (ITEM_STATUS_ACTIVE != items[i].status)
166 			continue;
167 
168 		if (HOST_STATUS_MONITORED != items[i].host.status)
169 			continue;
170 
171 		if (HOST_MAINTENANCE_STATUS_ON == items[i].host.maintenance_status &&
172 				MAINTENANCE_TYPE_NODATA == items[i].host.maintenance_type)
173 		{
174 			continue;
175 		}
176 
177 		init_result(&value);
178 
179 		switch (types[i])
180 		{
181 			case ZBX_HTTPITEM_TYPE_SPEED:
182 				SET_UI64_RESULT(&value, speed_download);
183 				break;
184 			case ZBX_HTTPITEM_TYPE_LASTSTEP:
185 				SET_UI64_RESULT(&value, lastfailedstep);
186 				break;
187 			case ZBX_HTTPITEM_TYPE_LASTERROR:
188 				SET_STR_RESULT(&value, zbx_strdup(NULL, err_str));
189 				break;
190 		}
191 
192 		items[i].state = ITEM_STATE_NORMAL;
193 		dc_add_history(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state, NULL);
194 
195 		free_result(&value);
196 	}
197 
198 	DCconfig_clean_items(items, errcodes, num);
199 
200 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
201 }
202 
203 #ifdef HAVE_LIBCURL
process_step_data(zbx_uint64_t httpstepid,zbx_httpstat_t * stat,zbx_timespec_t * ts)204 static void	process_step_data(zbx_uint64_t httpstepid, zbx_httpstat_t *stat, zbx_timespec_t *ts)
205 {
206 	const char	*__function_name = "process_step_data";
207 
208 	DB_RESULT	result;
209 	DB_ROW		row;
210 	unsigned char	types[3];
211 	DC_ITEM		items[3];
212 	zbx_uint64_t	itemids[3];
213 	int		errcodes[3];
214 	size_t		i, num = 0;
215 	AGENT_RESULT	value;
216 
217 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() rspcode:%ld time:" ZBX_FS_DBL " speed:" ZBX_FS_DBL,
218 			__function_name, stat->rspcode, stat->total_time, stat->speed_download);
219 
220 	result = DBselect("select type,itemid from httpstepitem where httpstepid=" ZBX_FS_UI64, httpstepid);
221 
222 	while (NULL != (row = DBfetch(result)))
223 	{
224 		if (3 == num)
225 		{
226 			THIS_SHOULD_NEVER_HAPPEN;
227 			break;
228 		}
229 
230 		if (ZBX_HTTPITEM_TYPE_RSPCODE != (types[num] = (unsigned char)atoi(row[0])) &&
231 				ZBX_HTTPITEM_TYPE_TIME != types[num] && ZBX_HTTPITEM_TYPE_SPEED != types[num])
232 		{
233 			THIS_SHOULD_NEVER_HAPPEN;
234 			continue;
235 		}
236 
237 		ZBX_STR2UINT64(itemids[num], row[1]);
238 		num++;
239 	}
240 	DBfree_result(result);
241 
242 	DCconfig_get_items_by_itemids(items, itemids, errcodes, num);
243 
244 	for (i = 0; i < num; i++)
245 	{
246 		if (SUCCEED != errcodes[i])
247 			continue;
248 
249 		if (ITEM_STATUS_ACTIVE != items[i].status)
250 			continue;
251 
252 		if (HOST_STATUS_MONITORED != items[i].host.status)
253 			continue;
254 
255 		if (HOST_MAINTENANCE_STATUS_ON == items[i].host.maintenance_status &&
256 				MAINTENANCE_TYPE_NODATA == items[i].host.maintenance_type)
257 		{
258 			continue;
259 		}
260 
261 		init_result(&value);
262 
263 		switch (types[i])
264 		{
265 			case ZBX_HTTPITEM_TYPE_RSPCODE:
266 				SET_UI64_RESULT(&value, stat->rspcode);
267 				break;
268 			case ZBX_HTTPITEM_TYPE_TIME:
269 				SET_DBL_RESULT(&value, stat->total_time);
270 				break;
271 			case ZBX_HTTPITEM_TYPE_SPEED:
272 				SET_DBL_RESULT(&value, stat->speed_download);
273 				break;
274 		}
275 
276 		items[i].state = ITEM_STATE_NORMAL;
277 		dc_add_history(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state, NULL);
278 
279 		free_result(&value);
280 	}
281 
282 	DCconfig_clean_items(items, errcodes, num);
283 
284 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
285 }
286 
add_headers(char * headers,struct curl_slist ** headers_slist)287 static void	add_headers(char *headers, struct curl_slist **headers_slist)
288 {
289 	char      *p_begin;
290 
291 	p_begin = headers;
292 
293 	while ('\0' != *p_begin)
294 	{
295 		char    c, *p_end, *line;
296 
297 		while ('\r' == *p_begin || '\n' == *p_begin)
298 			p_begin++;
299 
300 		p_end = p_begin;
301 
302 		while ('\0' != *p_end && '\r' != *p_end && '\n' != *p_end)
303 			p_end++;
304 
305 		if (p_begin == p_end)
306 			break;
307 
308 		if ('\0' != (c = *p_end))
309 			*p_end = '\0';
310 		line = zbx_strdup(NULL, p_begin);
311 		if ('\0' != c)
312 			*p_end = c;
313 
314 		zbx_lrtrim(line, " \t");
315 		if ('\0' != *line)
316 			*headers_slist = curl_slist_append(*headers_slist, line);
317 		zbx_free(line);
318 
319 		p_begin = p_end;
320 	}
321 }
322 #endif
323 
324 /******************************************************************************
325  *                                                                            *
326  * Function: process_httptest                                                 *
327  *                                                                            *
328  * Purpose: process single scenario of http test                              *
329  *                                                                            *
330  * Parameters:                                                                *
331  *                                                                            *
332  * Return value:                                                              *
333  *                                                                            *
334  * Author: Alexei Vladishev                                                   *
335  *                                                                            *
336  * Comments:                                                                  *
337  *                                                                            *
338  ******************************************************************************/
process_httptest(DC_HOST * host,zbx_httptest_t * httptest)339 static void	process_httptest(DC_HOST *host, zbx_httptest_t *httptest)
340 {
341 	const char	*__function_name = "process_httptest";
342 
343 	DB_RESULT	result;
344 	DB_HTTPSTEP	httpstep;
345 	char		*err_str = NULL;
346 	int		lastfailedstep;
347 	zbx_timespec_t	ts;
348 	double		speed_download = 0;
349 	int		speed_download_num = 0;
350 #ifdef HAVE_LIBCURL
351 	DB_ROW		row;
352 	zbx_httpstat_t	stat;
353 	int		err;
354 	char		*auth = NULL, errbuf[CURL_ERROR_SIZE];
355 	size_t		auth_alloc = 0, auth_offset;
356 	CURL            *easyhandle = NULL;
357 #endif
358 
359 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() httptestid:" ZBX_FS_UI64 " name:'%s'",
360 			__function_name, httptest->httptest.httptestid, httptest->httptest.name);
361 
362 	lastfailedstep = 0;
363 
364 	result = DBselect(
365 			"select httpstepid,no,name,url,timeout,posts,required,status_codes,variables,follow_redirects,"
366 				"retrieve_mode,headers"
367 			" from httpstep"
368 			" where httptestid=" ZBX_FS_UI64
369 			" order by no",
370 			httptest->httptest.httptestid);
371 
372 	/* Explicitly initialize the name. If we compile without libCURL support, */
373 	/* we avoid the potential usage of unititialized values. */
374 	httpstep.name = NULL;
375 
376 #ifdef HAVE_LIBCURL
377 	if (NULL == (easyhandle = curl_easy_init()))
378 	{
379 		err_str = zbx_strdup(err_str, "cannot initialize cURL library");
380 		goto clean;
381 	}
382 
383 	if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_PROXY, httptest->httptest.http_proxy)) ||
384 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_COOKIEFILE, "")) ||
385 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, httptest->httptest.agent)) ||
386 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WRITEFUNCTION2)) ||
387 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, HEADERFUNCTION2)) ||
388 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER,
389 					0 == httptest->httptest.verify_peer ? 0L : 1L)) ||
390 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST,
391 					0 == httptest->httptest.verify_host ? 0L : 2L)) ||
392 			CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, errbuf)))
393 	{
394 		err_str = zbx_strdup(err_str, curl_easy_strerror(err));
395 		goto clean;
396 	}
397 
398 	if (NULL != CONFIG_SOURCE_IP)
399 	{
400 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_INTERFACE, CONFIG_SOURCE_IP)))
401 		{
402 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
403 			goto clean;
404 		}
405 	}
406 
407 	if (0 != httptest->httptest.verify_peer && NULL != CONFIG_SSL_CA_LOCATION)
408 	{
409 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_CAPATH, CONFIG_SSL_CA_LOCATION)))
410 		{
411 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
412 			goto clean;
413 		}
414 	}
415 
416 	if ('\0' != *httptest->httptest.ssl_cert_file)
417 	{
418 		char	*file_name;
419 
420 		file_name = zbx_dsprintf(NULL, "%s/%s", CONFIG_SSL_CERT_LOCATION, httptest->httptest.ssl_cert_file);
421 		zabbix_log(LOG_LEVEL_DEBUG, "using SSL certificate file: '%s'", file_name);
422 
423 		err = curl_easy_setopt(easyhandle, CURLOPT_SSLCERT, file_name);
424 		zbx_free(file_name);
425 
426 		if (CURLE_OK != err || CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSLCERTTYPE, "PEM")))
427 		{
428 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
429 			goto clean;
430 		}
431 	}
432 
433 	if ('\0' != *httptest->httptest.ssl_key_file)
434 	{
435 		char	*file_name;
436 
437 		file_name = zbx_dsprintf(NULL, "%s/%s", CONFIG_SSL_KEY_LOCATION, httptest->httptest.ssl_key_file);
438 		zabbix_log(LOG_LEVEL_DEBUG, "using SSL private key file: '%s'", file_name);
439 
440 		err = curl_easy_setopt(easyhandle, CURLOPT_SSLKEY, file_name);
441 		zbx_free(file_name);
442 
443 		if (CURLE_OK != err || CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_SSLKEYTYPE, "PEM")))
444 		{
445 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
446 			goto clean;
447 		}
448 	}
449 
450 	if ('\0' != httptest->httptest.ssl_key_password)
451 	{
452 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_KEYPASSWD,
453 				httptest->httptest.ssl_key_password)))
454 		{
455 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
456 			goto clean;
457 		}
458 	}
459 
460 	while (NULL != (row = DBfetch(result)))
461 	{
462 		struct curl_slist	*headers_slist = NULL;
463 
464 		/* NOTE: do not break or return from this block! */
465 		/*       process_step_data() call is required! */
466 
467 		ZBX_STR2UINT64(httpstep.httpstepid, row[0]);
468 		httpstep.httptestid = httptest->httptest.httptestid;
469 		httpstep.no = atoi(row[1]);
470 		httpstep.name = row[2];
471 
472 		httpstep.url = zbx_strdup(NULL, row[3]);
473 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
474 				&httpstep.url, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
475 
476 		httpstep.timeout = atoi(row[4]);
477 
478 		httpstep.posts = zbx_strdup(NULL, row[5]);
479 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
480 				&httpstep.posts, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
481 
482 		httpstep.required = zbx_strdup(NULL, row[6]);
483 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
484 				&httpstep.required, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
485 
486 		httpstep.status_codes = zbx_strdup(NULL, row[7]);
487 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host->hostid, NULL, NULL, NULL,
488 				&httpstep.status_codes, MACRO_TYPE_COMMON, NULL, 0);
489 
490 		httpstep.variables = row[8];
491 		httpstep.follow_redirects = atoi(row[9]);
492 		httpstep.retrieve_mode = atoi(row[10]);
493 
494 		httpstep.headers = zbx_strdup(NULL, row[11]);
495 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, host, NULL, NULL,
496 				&httpstep.headers, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
497 
498 		memset(&stat, 0, sizeof(stat));
499 
500 		http_substitute_variables(httptest, &httpstep.url);
501 		http_substitute_variables(httptest, &httpstep.posts);
502 
503 		zabbix_log(LOG_LEVEL_DEBUG, "%s() use step \"%s\"", __function_name, httpstep.name);
504 
505 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, httpstep.posts)))
506 		{
507 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
508 			goto httpstep_error;
509 		}
510 
511 		if ('\0' != *httpstep.posts)
512 			zabbix_log(LOG_LEVEL_DEBUG, "%s() use post \"%s\"", __function_name, httpstep.posts);
513 
514 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_POST, '\0' != *httpstep.posts ? 1L : 0L)))
515 		{
516 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
517 			goto httpstep_error;
518 		}
519 
520 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION,
521 				0 == httpstep.follow_redirects ? 0L : 1L)))
522 		{
523 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
524 			goto httpstep_error;
525 		}
526 
527 		if (0 != httpstep.follow_redirects)
528 		{
529 			if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_MAXREDIRS, ZBX_CURLOPT_MAXREDIRS)))
530 			{
531 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
532 				goto httpstep_error;
533 			}
534 		}
535 
536 		http_substitute_variables(httptest, &httpstep.headers);
537 
538 		/* headers defined in a step overwrite headers defined in scenario */
539 		if ('\0' != *httpstep.headers)
540 			add_headers(httpstep.headers, &headers_slist);
541 		else if ('\0' != *httptest->httptest.headers)
542 			add_headers(httptest->httptest.headers, &headers_slist);
543 
544 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers_slist)))
545 		{
546 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
547 			goto httpstep_error;
548 		}
549 
550 		/* enable/disable fetching the body */
551 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_NOBODY,
552 				ZBX_RETRIEVE_MODE_HEADERS == httpstep.retrieve_mode ? 1L : 0L)))
553 		{
554 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
555 			goto httpstep_error;
556 		}
557 
558 		if (HTTPTEST_AUTH_NONE != httptest->httptest.authentication)
559 		{
560 			long	curlauth = 0;
561 
562 			zabbix_log(LOG_LEVEL_DEBUG, "%s() setting HTTPAUTH [%d]",
563 					__function_name, httptest->httptest.authentication);
564 			zabbix_log(LOG_LEVEL_DEBUG, "%s() setting USERPWD for authentication", __function_name);
565 
566 			switch (httptest->httptest.authentication)
567 			{
568 				case HTTPTEST_AUTH_BASIC:
569 					curlauth = CURLAUTH_BASIC;
570 					break;
571 				case HTTPTEST_AUTH_NTLM:
572 					curlauth = CURLAUTH_NTLM;
573 					break;
574 				default:
575 					THIS_SHOULD_NEVER_HAPPEN;
576 					break;
577 			}
578 
579 			auth_offset = 0;
580 			zbx_snprintf_alloc(&auth, &auth_alloc, &auth_offset, "%s:%s", httptest->httptest.http_user,
581 					httptest->httptest.http_password);
582 
583 			if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_HTTPAUTH, curlauth)) ||
584 					CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_USERPWD, auth)))
585 			{
586 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
587 				goto httpstep_error;
588 			}
589 		}
590 
591 		zabbix_log(LOG_LEVEL_DEBUG, "%s() go to URL \"%s\"", __function_name, httpstep.url);
592 
593 		if (CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, (long)httpstep.timeout)) ||
594 				CURLE_OK != (err = curl_easy_setopt(easyhandle, CURLOPT_URL, httpstep.url)))
595 		{
596 			err_str = zbx_strdup(err_str, curl_easy_strerror(err));
597 			goto httpstep_error;
598 		}
599 
600 		/* try to retrieve page several times depending on number of retries */
601 		do
602 		{
603 			memset(&page, 0, sizeof(page));
604 
605 			if (CURLE_OK == (err = curl_easy_perform(easyhandle)))
606 				break;
607 
608 			zbx_free(page.data);
609 		}
610 		while (0 < --httptest->httptest.retries);
611 
612 		curl_slist_free_all(headers_slist);	/* must be called after curl_easy_perform() */
613 
614 		if (CURLE_OK == err)
615 		{
616 			zabbix_log(LOG_LEVEL_TRACE, "%s() page.data from %s:'%s'", __function_name, httpstep.url, page.data);
617 
618 			/* first get the data that is needed even if step fails */
619 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_RESPONSE_CODE, &stat.rspcode)))
620 			{
621 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
622 			}
623 			else if ('\0' != *httpstep.status_codes &&
624 					FAIL == int_in_list(httpstep.status_codes, stat.rspcode))
625 			{
626 				err_str = zbx_dsprintf(err_str, "response code \"%ld\" did not match any of the"
627 						" required status codes \"%s\"", stat.rspcode, httpstep.status_codes);
628 			}
629 
630 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_TOTAL_TIME, &stat.total_time)) &&
631 					NULL == err_str)
632 			{
633 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
634 			}
635 
636 			if (CURLE_OK != (err = curl_easy_getinfo(easyhandle, CURLINFO_SPEED_DOWNLOAD,
637 					&stat.speed_download)) && NULL == err_str)
638 			{
639 				err_str = zbx_strdup(err_str, curl_easy_strerror(err));
640 			}
641 			else
642 			{
643 				speed_download += stat.speed_download;
644 				speed_download_num++;
645 			}
646 
647 			if (ZBX_RETRIEVE_MODE_CONTENT == httpstep.retrieve_mode)
648 			{
649 				char	*var_err_str = NULL;
650 
651 				/* required pattern */
652 				if (NULL == err_str && '\0' != *httpstep.required && NULL == zbx_regexp_match(page.data,
653 						httpstep.required, NULL))
654 				{
655 					err_str = zbx_dsprintf(err_str, "required pattern \"%s\" was not found on %s",
656 							httpstep.required, httpstep.url);
657 				}
658 
659 				/* variables defined in scenario */
660 				if (NULL == err_str && FAIL == http_process_variables(httptest,
661 						httptest->httptest.variables, page.data, &var_err_str))
662 				{
663 					char	*variables;
664 
665 					variables = string_replace(httptest->httptest.variables, "\r\n", " ");
666 					err_str = zbx_dsprintf(err_str, "error in scenario variables \"%s\": %s",
667 							variables, var_err_str);
668 
669 					zbx_free(variables);
670 				}
671 
672 				/* variables defined in a step */
673 				if (NULL == err_str && FAIL == http_process_variables(httptest, httpstep.variables,
674 						page.data, &var_err_str))
675 				{
676 					char	*variables;
677 
678 					variables = string_replace(httpstep.variables, "\r\n", " ");
679 					err_str = zbx_dsprintf(err_str, "error in step variables \"%s\": %s",
680 							variables, var_err_str);
681 
682 					zbx_free(variables);
683 				}
684 
685 				zbx_free(var_err_str);
686 			}
687 
688 			zbx_timespec(&ts);
689 			process_step_data(httpstep.httpstepid, &stat, &ts);
690 
691 			zbx_free(page.data);
692 		}
693 		else
694 			err_str = zbx_dsprintf(err_str, "%s: %s", curl_easy_strerror(err), errbuf);
695 
696 httpstep_error:
697 		zbx_free(httpstep.headers);
698 		zbx_free(httpstep.status_codes);
699 		zbx_free(httpstep.required);
700 		zbx_free(httpstep.posts);
701 		zbx_free(httpstep.url);
702 
703 		if (NULL != err_str)
704 		{
705 			lastfailedstep = httpstep.no;
706 			break;
707 		}
708 	}
709 
710 	zbx_free(auth);
711 clean:
712 	curl_easy_cleanup(easyhandle);
713 #else
714 	err_str = zbx_strdup(err_str, "cURL library is required for Web monitoring support");
715 #endif	/* HAVE_LIBCURL */
716 
717 	zbx_timespec(&ts);
718 
719 	if (NULL != err_str)
720 	{
721 		if (0 == lastfailedstep)
722 		{
723 			/* we are here either because cURL initialization failed */
724 			/* or we have been compiled without cURL library */
725 
726 			lastfailedstep = 1;
727 		}
728 
729 		if (NULL != httpstep.name)
730 		{
731 			zabbix_log(LOG_LEVEL_DEBUG, "cannot process step \"%s\" of web scenario \"%s\" on host \"%s\": %s",
732 					httpstep.name, httptest->httptest.name, host->name, err_str);
733 		}
734 	}
735 	DBfree_result(result);
736 
737 	DBexecute("update httptest set nextcheck=%d+delay where httptestid=" ZBX_FS_UI64,
738 			ts.sec, httptest->httptest.httptestid);
739 
740 	if (0 != speed_download_num)
741 		speed_download /= speed_download_num;
742 
743 	process_test_data(httptest->httptest.httptestid, lastfailedstep, speed_download, err_str, &ts);
744 
745 	zbx_free(err_str);
746 
747 	dc_flush_history();
748 
749 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
750 }
751 
752 /******************************************************************************
753  *                                                                            *
754  * Function: process_httptests                                                *
755  *                                                                            *
756  * Purpose: process httptests                                                 *
757  *                                                                            *
758  * Parameters: now - current timestamp                                        *
759  *                                                                            *
760  * Return value: number of processed httptests                                *
761  *                                                                            *
762  * Author: Alexei Vladishev                                                   *
763  *                                                                            *
764  * Comments: always SUCCEED                                                   *
765  *                                                                            *
766  ******************************************************************************/
process_httptests(int httppoller_num,int now)767 int	process_httptests(int httppoller_num, int now)
768 {
769 	const char	*__function_name = "process_httptests";
770 
771 	DB_RESULT	result;
772 	DB_ROW		row;
773 	zbx_httptest_t	httptest;
774 	DC_HOST		host;
775 	int		httptests_count = 0;
776 
777 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
778 
779 	/* create macro cache to use in http tests */
780 	zbx_vector_ptr_pair_create(&httptest.macros);
781 
782 	result = DBselect(
783 			"select h.hostid,h.host,h.name,t.httptestid,t.name,t.variables,t.headers,t.agent,"
784 				"t.authentication,t.http_user,t.http_password,t.http_proxy,t.retries,t.ssl_cert_file,"
785 				"t.ssl_key_file,t.ssl_key_password,t.verify_peer,t.verify_host"
786 			" from httptest t,hosts h"
787 			" where t.hostid=h.hostid"
788 				" and t.nextcheck<=%d"
789 				" and " ZBX_SQL_MOD(t.httptestid,%d) "=%d"
790 				" and t.status=%d"
791 				" and h.proxy_hostid is null"
792 				" and h.status=%d"
793 				" and (h.maintenance_status=%d or h.maintenance_type=%d)",
794 			now,
795 			CONFIG_HTTPPOLLER_FORKS, httppoller_num - 1,
796 			HTTPTEST_STATUS_MONITORED,
797 			HOST_STATUS_MONITORED,
798 			HOST_MAINTENANCE_STATUS_OFF, MAINTENANCE_TYPE_NORMAL);
799 
800 	while (NULL != (row = DBfetch(result)))
801 	{
802 		ZBX_STR2UINT64(host.hostid, row[0]);
803 		strscpy(host.host, row[1]);
804 		zbx_strlcpy_utf8(host.name, row[2], sizeof(host.name));
805 
806 		ZBX_STR2UINT64(httptest.httptest.httptestid, row[3]);
807 		httptest.httptest.name = row[4];
808 
809 		httptest.httptest.variables = zbx_strdup(NULL, row[5]);
810 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL,
811 				&httptest.httptest.variables, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
812 
813 		httptest.httptest.headers = zbx_strdup(NULL, row[6]);
814 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL,
815 				&httptest.httptest.headers, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
816 
817 		httptest.httptest.agent = zbx_strdup(NULL, row[7]);
818 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL,
819 				&httptest.httptest.agent, MACRO_TYPE_COMMON, NULL, 0);
820 
821 		if (HTTPTEST_AUTH_NONE != (httptest.httptest.authentication = atoi(row[8])))
822 		{
823 			httptest.httptest.http_user = zbx_strdup(NULL, row[9]);
824 			substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL,
825 					&httptest.httptest.http_user, MACRO_TYPE_COMMON, NULL, 0);
826 
827 			httptest.httptest.http_password = zbx_strdup(NULL, row[10]);
828 			substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL,
829 					&httptest.httptest.http_password, MACRO_TYPE_COMMON, NULL, 0);
830 		}
831 
832 		if ('\0' != *row[11])
833 		{
834 			httptest.httptest.http_proxy = zbx_strdup(NULL, row[11]);
835 			substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL,
836 					&httptest.httptest.http_proxy, MACRO_TYPE_COMMON, NULL, 0);
837 		}
838 		else
839 			httptest.httptest.http_proxy = NULL;
840 
841 		httptest.httptest.retries = atoi(row[12]);
842 
843 		httptest.httptest.ssl_cert_file = zbx_strdup(NULL, row[13]);
844 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL,
845 				&httptest.httptest.ssl_cert_file, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
846 
847 		httptest.httptest.ssl_key_file = zbx_strdup(NULL, row[14]);
848 		substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &host, NULL, NULL,
849 				&httptest.httptest.ssl_key_file, MACRO_TYPE_HTTPTEST_FIELD, NULL, 0);
850 
851 		httptest.httptest.ssl_key_password = zbx_strdup(NULL, row[15]);
852 		substitute_simple_macros(NULL, NULL, NULL, NULL, &host.hostid, NULL, NULL, NULL,
853 				&httptest.httptest.ssl_key_password, MACRO_TYPE_COMMON, NULL, 0);
854 
855 		httptest.httptest.verify_peer = atoi(row[16]);
856 		httptest.httptest.verify_host = atoi(row[17]);
857 
858 		/* add httptest variables to the current test macro cache */
859 		http_process_variables(&httptest, httptest.httptest.variables, NULL, NULL);
860 
861 		process_httptest(&host, &httptest);
862 
863 		zbx_free(httptest.httptest.ssl_key_password);
864 		zbx_free(httptest.httptest.ssl_key_file);
865 		zbx_free(httptest.httptest.ssl_cert_file);
866 		zbx_free(httptest.httptest.http_proxy);
867 
868 		if (HTTPTEST_AUTH_NONE != httptest.httptest.authentication)
869 		{
870 			zbx_free(httptest.httptest.http_password);
871 			zbx_free(httptest.httptest.http_user);
872 		}
873 		zbx_free(httptest.httptest.agent);
874 		zbx_free(httptest.httptest.headers);
875 		zbx_free(httptest.httptest.variables);
876 
877 		/* clear the macro cache used in this http test */
878 		httptest_remove_macros(&httptest);
879 
880 		httptests_count++;	/* performance metric */
881 	}
882 	/* destroy the macro cache used in http tests */
883 	zbx_vector_ptr_pair_destroy(&httptest.macros);
884 
885 	DBfree_result(result);
886 
887 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
888 
889 	return httptests_count;
890 }
891