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 "db.h"
22 #include "log.h"
23 #include "zbxregexp.h"
24 #include "zbxhttp.h"
25
26 #include "httpmacro.h"
27
28 #define REGEXP_PREFIX "regex:"
29 #define REGEXP_PREFIX_SIZE ZBX_CONST_STRLEN(REGEXP_PREFIX)
30
31 /******************************************************************************
32 * *
33 * Function: httpmacro_cmp_func *
34 * *
35 * Purpose: compare two macros by name *
36 * *
37 * Parameters: d1 - [IN] the first macro *
38 * d2 - [IN] the second macro *
39 * *
40 * Return value: <0 - the first macro name is 'less' than second *
41 * 0 - the macro names are equal *
42 * >0 - the first macro name is 'greater' than second *
43 * *
44 * Author: Andris Zeila *
45 * *
46 ******************************************************************************/
httpmacro_cmp_func(const void * d1,const void * d2)47 static int httpmacro_cmp_func(const void *d1, const void *d2)
48 {
49 const zbx_ptr_pair_t *pair1 = (const zbx_ptr_pair_t *)d1;
50 const zbx_ptr_pair_t *pair2 = (const zbx_ptr_pair_t *)d2;
51
52 return strcmp((char *)pair1->first, (char *)pair2->first);
53 }
54
55 /******************************************************************************
56 * *
57 * Function: httpmacro_append_pair *
58 * *
59 * Purpose: appends key/value pair to the http test macro cache. *
60 * If the value format is 'regex:<pattern>', then regular expression *
61 * match is performed against the supplied data value and specified *
62 * pattern. The first captured group is assigned to the macro value. *
63 * *
64 * Parameters: httptest - [IN/OUT] the http test data *
65 * pkey - [IN] a pointer to the macro name (key) data *
66 * nkey - [IN] the macro name (key) size *
67 * pvalue - [IN] a pointer to the macro value data *
68 * nvalue - [IN] the value size *
69 * data - [IN] the data for regexp matching (optional) *
70 * err_str - [OUT] the error message (optional) *
71 * *
72 * Return value: SUCCEED - the key/value pair was added successfully *
73 * FAIL - key/value pair adding to cache failed. *
74 * The failure reason can be either empty key/value, *
75 * wrong key format or failed regular expression *
76 * match. *
77 * *
78 * Author: Andris Zeila *
79 * *
80 ******************************************************************************/
httpmacro_append_pair(zbx_httptest_t * httptest,const char * pkey,size_t nkey,const char * pvalue,size_t nvalue,const char * data,char ** err_str)81 static int httpmacro_append_pair(zbx_httptest_t *httptest, const char *pkey, size_t nkey,
82 const char *pvalue, size_t nvalue, const char *data, char **err_str)
83 {
84 const char *__function_name = "httpmacro_append_pair";
85 char *value_str = NULL;
86 size_t key_size = 0, key_offset = 0, value_size = 0, value_offset = 0;
87 zbx_ptr_pair_t pair = {NULL, NULL};
88 int index, ret = FAIL;
89
90 zabbix_log(LOG_LEVEL_DEBUG, "In %s() pkey:'%.*s' pvalue:'%.*s'",
91 __function_name, (int)nkey, pkey, (int)nvalue, pvalue);
92
93 if (0 == nkey && 0 != nvalue)
94 {
95 zabbix_log(LOG_LEVEL_DEBUG, "%s() missing variable name (only value provided): \"%.*s\"",
96 __function_name, (int)nvalue, pvalue);
97
98 if (NULL != err_str && NULL == *err_str)
99 {
100 *err_str = zbx_dsprintf(*err_str, "missing variable name (only value provided):"
101 " \"%.*s\"", (int)nvalue, pvalue);
102 }
103
104 goto out;
105 }
106
107 if ('{' != pkey[0] || '}' != pkey[nkey - 1])
108 {
109 zabbix_log(LOG_LEVEL_DEBUG, "%s() \"%.*s\" not enclosed in {}", __function_name, (int)nkey, pkey);
110
111 if (NULL != err_str && NULL == *err_str)
112 *err_str = zbx_dsprintf(*err_str, "\"%.*s\" not enclosed in {}", (int)nkey, pkey);
113
114 goto out;
115 }
116
117 /* get macro value */
118 zbx_strncpy_alloc(&value_str, &value_size, &value_offset, pvalue, nvalue);
119 if (0 == strncmp(REGEXP_PREFIX, value_str, REGEXP_PREFIX_SIZE))
120 {
121 int rc;
122 /* The value contains regexp pattern, retrieve the first captured group or fail. */
123 /* The \@ sequence is a special construct to fail if the pattern matches but does */
124 /* not contain groups to capture. */
125
126 rc = zbx_mregexp_sub(data, value_str + REGEXP_PREFIX_SIZE, "\\@", (char **)&pair.second);
127 zbx_free(value_str);
128
129 if (SUCCEED != rc || NULL == pair.second)
130 {
131 zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot extract the value of \"%.*s\" from response",
132 __function_name, (int)nkey, pkey);
133
134 if (NULL != err_str && NULL == *err_str)
135 {
136 *err_str = zbx_dsprintf(*err_str, "cannot extract the value of \"%.*s\""
137 " from response", (int)nkey, pkey);
138 }
139
140 goto out;
141 }
142 }
143 else
144 pair.second = value_str;
145
146 /* get macro name */
147 zbx_strncpy_alloc((char **)&pair.first, &key_size, &key_offset, pkey, nkey);
148
149 /* remove existing macro if necessary */
150 index = zbx_vector_ptr_pair_search(&httptest->macros, pair, httpmacro_cmp_func);
151 if (FAIL != index)
152 {
153 zbx_ptr_pair_t *ppair = &httptest->macros.values[index];
154
155 zbx_free(ppair->first);
156 zbx_free(ppair->second);
157 zbx_vector_ptr_pair_remove_noorder(&httptest->macros, index);
158 }
159 zbx_vector_ptr_pair_append(&httptest->macros, pair);
160
161 zabbix_log(LOG_LEVEL_DEBUG, "append macro '%s'='%s' in cache", (char *)pair.first, (char *)pair.second);
162
163 ret = SUCCEED;
164 out:
165 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
166
167 return ret;
168 }
169
170 /******************************************************************************
171 * *
172 * Function: http_substitute_variables *
173 * *
174 * Purpose: substitute variables in input string with their values from http *
175 * test config *
176 * *
177 * Parameters: httptest - [IN] the http test data *
178 * data - [IN/OUT] string to substitute macros in *
179 * *
180 * Author: Alexei Vladishev, Andris Zeila *
181 * *
182 ******************************************************************************/
http_substitute_variables(const zbx_httptest_t * httptest,char ** data)183 int http_substitute_variables(const zbx_httptest_t *httptest, char **data)
184 {
185 const char *__function_name = "http_substitute_variables";
186 char replace_char, *substitute;
187 size_t left, right, len, offset;
188 int index, ret = SUCCEED;
189 zbx_ptr_pair_t pair = {NULL, NULL};
190
191 zabbix_log(LOG_LEVEL_DEBUG, "In %s() data:'%s'", __function_name, *data);
192
193 for (left = 0; '\0' != (*data)[left]; left++)
194 {
195 if ('{' != (*data)[left])
196 continue;
197
198 offset = ('{' == (*data)[left + 1] ? 1 : 0);
199
200 for (right = left + 1; '\0' != (*data)[right] && '}' != (*data)[right]; right++)
201 ;
202
203 if ('}' != (*data)[right])
204 break;
205
206 replace_char = (*data)[right + 1];
207 (*data)[right + 1] = '\0';
208
209 pair.first = *data + left + offset;
210 index = zbx_vector_ptr_pair_search(&httptest->macros, pair, httpmacro_cmp_func);
211
212 (*data)[right + 1] = replace_char;
213
214 if (FAIL == index)
215 continue;
216
217 substitute = (char *)httptest->macros.values[index].second;
218
219 if ('.' == replace_char && 1 == offset)
220 {
221 right += 2;
222 offset = right;
223
224 for (; '\0' != (*data)[right] && '}' != (*data)[right]; right++)
225 ;
226
227 if ('}' != (*data)[right])
228 break;
229
230 len = right - offset;
231
232 if (ZBX_CONST_STRLEN("urlencode()") == len && 0 == strncmp(*data + offset, "urlencode()", len))
233 {
234 /* http_variable_urlencode cannot fail (except for "out of memory") */
235 /* so no check is needed */
236 substitute = NULL;
237 zbx_http_url_encode((char *)httptest->macros.values[index].second, &substitute);
238 }
239 else if (ZBX_CONST_STRLEN("urldecode()") == len &&
240 0 == strncmp(*data + offset, "urldecode()", len))
241 {
242 /* on error substitute will remain unchanged */
243 substitute = NULL;
244 if (FAIL == (ret = zbx_http_url_decode((char *)httptest->macros.values[index].second,
245 &substitute)))
246 {
247 break;
248 }
249 }
250 else
251 continue;
252
253 }
254 else
255 left += offset;
256
257 zbx_replace_string(data, left, &right, substitute);
258 if (substitute != (char *)httptest->macros.values[index].second)
259 zbx_free(substitute);
260
261 left = right;
262 }
263
264 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() data:'%s'", __function_name, *data);
265
266 return ret;
267 }
268
269 /******************************************************************************
270 * *
271 * Function: http_process_variables *
272 * *
273 * Purpose: parses http test/step variable string and stores results into *
274 * httptest macro cache. *
275 * The variables are specified as {<key>}=><value> pairs *
276 * If the value format is 'regex:<pattern>', then regular expression *
277 * match is performed against the supplied data value and specified *
278 * pattern. The first captured group is assigned to the macro value. *
279 * *
280 * Parameters: httptest - [IN/OUT] the http test data *
281 * variables - [IN] the variable vector *
282 * data - [IN] the data for variable regexp matching *
283 * (optional). *
284 * err_str - [OUT] the error message (optional) *
285 * *
286 * Return value: SUCCEED - the variables were processed successfully *
287 * FAIL - the variable processing failed (regexp match *
288 * failed). *
289 * *
290 * Author: Andris Zeila *
291 * *
292 ******************************************************************************/
http_process_variables(zbx_httptest_t * httptest,zbx_vector_ptr_pair_t * variables,const char * data,char ** err_str)293 int http_process_variables(zbx_httptest_t *httptest, zbx_vector_ptr_pair_t *variables, const char *data,
294 char **err_str)
295 {
296 const char *__function_name = "http_process_variables";
297 char *key, *value;
298 int i, ret = FAIL;
299
300 zabbix_log(LOG_LEVEL_DEBUG, "In %s() %d variables", __function_name, variables->values_num);
301
302 for (i = 0; i < variables->values_num; i++)
303 {
304 key = (char *)variables->values[i].first;
305 value = (char *)variables->values[i].second;
306 if (FAIL == httpmacro_append_pair(httptest, key, strlen(key), value, strlen(value), data, err_str))
307 goto out;
308 }
309
310 ret = SUCCEED;
311 out:
312 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
313
314 return ret;
315 }
316