1 #include <ctype.h>
2 
3 #include <common/cfgparse.h>
4 #include <common/mini-clist.h>
5 
6 #include <types/vars.h>
7 
8 #include <proto/arg.h>
9 #include <proto/proto_http.h>
10 #include <proto/sample.h>
11 #include <proto/stream.h>
12 #include <proto/tcp_rules.h>
13 #include <proto/vars.h>
14 
15 /* This contains a pool of struct vars */
16 static struct pool_head *var_pool = NULL;
17 
18 /* This array contain all the names of all the HAProxy vars.
19  * This permits to identify two variables name with
20  * only one pointer. It permits to not using  strdup() for
21  * each variable name used during the runtime.
22  */
23 static char **var_names = NULL;
24 static int var_names_nb = 0;
25 
26 /* This array of int contains the system limits per context. */
27 static unsigned int var_global_limit = 0;
28 static unsigned int var_global_size = 0;
29 static unsigned int var_proc_limit = 0;
30 static unsigned int var_sess_limit = 0;
31 static unsigned int var_txn_limit = 0;
32 static unsigned int var_reqres_limit = 0;
33 
34 
35 __decl_hathreads(HA_RWLOCK_T var_names_rwlock);
36 
37 /* returns the struct vars pointer for a session, stream and scope, or NULL if
38  * it does not exist.
39  */
get_vars(struct session * sess,struct stream * strm,enum vars_scope scope)40 static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
41 {
42 	switch (scope) {
43 	case SCOPE_PROC:
44 		return &global.vars;
45 	case SCOPE_SESS:
46 		return &sess->vars;
47 	case SCOPE_TXN:
48 		return strm ? &strm->vars_txn : NULL;
49 	case SCOPE_REQ:
50 	case SCOPE_RES:
51 	default:
52 		return strm ? &strm->vars_reqres : NULL;
53 	}
54 }
55 
56 /* This function adds or remove memory size from the accounting. The inner
57  * pointers may be null when setting the outer ones only.
58  */
var_accounting_diff(struct vars * vars,struct session * sess,struct stream * strm,int size)59 static void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
60 {
61 	switch (vars->scope) {
62 	case SCOPE_REQ:
63 	case SCOPE_RES:
64 		if (strm)
65 			HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
66 		/* fall through */
67 	case SCOPE_TXN:
68 		if (strm)
69 			HA_ATOMIC_ADD(&strm->vars_txn.size, size);
70 		/* fall through */
71 	case SCOPE_SESS:
72 		HA_ATOMIC_ADD(&sess->vars.size, size);
73 		/* fall through */
74 	case SCOPE_PROC:
75 		HA_ATOMIC_ADD(&global.vars.size, size);
76 		HA_ATOMIC_ADD(&var_global_size, size);
77 	}
78 }
79 
80 /* This function returns 1 if the <size> is available in the var
81  * pool <vars>, otherwise returns 0. If the space is avalaible,
82  * the size is reserved. The inner pointers may be null when setting
83  * the outer ones only. The accounting uses either <sess> or <strm>
84  * depending on the scope. <strm> may be NULL when no stream is known
85  * and only the session exists (eg: tcp-request connection).
86  */
var_accounting_add(struct vars * vars,struct session * sess,struct stream * strm,int size)87 static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
88 {
89 	switch (vars->scope) {
90 	case SCOPE_REQ:
91 	case SCOPE_RES:
92 		if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
93 			return 0;
94 		/* fall through */
95 	case SCOPE_TXN:
96 		if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
97 			return 0;
98 		/* fall through */
99 	case SCOPE_SESS:
100 		if (var_sess_limit && sess->vars.size + size > var_sess_limit)
101 			return 0;
102 		/* fall through */
103 	case SCOPE_PROC:
104 		if (var_proc_limit && global.vars.size + size > var_proc_limit)
105 			return 0;
106 		if (var_global_limit && var_global_size + size > var_global_limit)
107 			return 0;
108 	}
109 	var_accounting_diff(vars, sess, strm, size);
110 	return 1;
111 }
112 
113 /* This fnuction remove a variable from the list and free memory it used */
var_clear(struct var * var)114 unsigned int var_clear(struct var *var)
115 {
116 	unsigned int size = 0;
117 
118 	if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
119 		free(var->data.u.str.str);
120 		size += var->data.u.str.len;
121 	}
122 	else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
123 		free(var->data.u.meth.str.str);
124 		size += var->data.u.meth.str.len;
125 	}
126 	LIST_DEL(&var->l);
127 	pool_free(var_pool, var);
128 	size += sizeof(struct var);
129 	return size;
130 }
131 
132 /* This function free all the memory used by all the varaibles
133  * in the list.
134  */
vars_prune(struct vars * vars,struct session * sess,struct stream * strm)135 void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
136 {
137 	struct var *var, *tmp;
138 	unsigned int size = 0;
139 
140 	HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
141 	list_for_each_entry_safe(var, tmp, &vars->head, l) {
142 		size += var_clear(var);
143 	}
144 	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
145 	var_accounting_diff(vars, sess, strm, -size);
146 }
147 
148 /* This function frees all the memory used by all the session variables in the
149  * list starting at <vars>.
150  */
vars_prune_per_sess(struct vars * vars)151 void vars_prune_per_sess(struct vars *vars)
152 {
153 	struct var *var, *tmp;
154 	unsigned int size = 0;
155 
156 	HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
157 	list_for_each_entry_safe(var, tmp, &vars->head, l) {
158 		size += var_clear(var);
159 	}
160 	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
161 
162 	HA_ATOMIC_SUB(&vars->size, size);
163 	HA_ATOMIC_SUB(&global.vars.size, size);
164 	HA_ATOMIC_SUB(&var_global_size, size);
165 }
166 
167 /* This function init a list of variabes. */
vars_init(struct vars * vars,enum vars_scope scope)168 void vars_init(struct vars *vars, enum vars_scope scope)
169 {
170 	LIST_INIT(&vars->head);
171 	vars->scope = scope;
172 	vars->size = 0;
173 	HA_RWLOCK_INIT(&vars->rwlock);
174 }
175 
176 /* This function declares a new variable name. It returns a pointer
177  * on the string identifying the name. This function assures that
178  * the same name exists only once.
179  *
180  * This function check if the variable name is acceptable.
181  *
182  * The function returns NULL if an error occurs, and <err> is filled.
183  * In this case, the HAProxy must be stopped because the structs are
184  * left inconsistent. Otherwise, it returns the pointer on the global
185  * name.
186  */
register_name(const char * name,int len,enum vars_scope * scope,int alloc,char ** err)187 static char *register_name(const char *name, int len, enum vars_scope *scope,
188 			   int alloc, char **err)
189 {
190 	int i;
191 	char **var_names2;
192 	const char *tmp;
193 	char *res = NULL;
194 
195 	/* Check length. */
196 	if (len == 0) {
197 		memprintf(err, "Empty variable name cannot be accepted");
198 		return res;
199 	}
200 
201 	/* Check scope. */
202 	if (len > 5 && strncmp(name, "proc.", 5) == 0) {
203 		name += 5;
204 		len -= 5;
205 		*scope = SCOPE_PROC;
206 	}
207 	else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
208 		name += 5;
209 		len -= 5;
210 		*scope = SCOPE_SESS;
211 	}
212 	else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
213 		name += 4;
214 		len -= 4;
215 		*scope = SCOPE_TXN;
216 	}
217 	else if (len > 4 && strncmp(name, "req.", 4) == 0) {
218 		name += 4;
219 		len -= 4;
220 		*scope = SCOPE_REQ;
221 	}
222 	else if (len > 4 && strncmp(name, "res.", 4) == 0) {
223 		name += 4;
224 		len -= 4;
225 		*scope = SCOPE_RES;
226 	}
227 	else {
228 		memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. "
229 		               "The scope can be 'proc', 'sess', 'txn', 'req' or 'res'", name);
230 		return res;
231 	}
232 
233 	if (alloc)
234 		HA_RWLOCK_WRLOCK(VARS_LOCK, &var_names_rwlock);
235 	else
236 		HA_RWLOCK_RDLOCK(VARS_LOCK, &var_names_rwlock);
237 
238 
239 	/* Look for existing variable name. */
240 	for (i = 0; i < var_names_nb; i++)
241 		if (strncmp(var_names[i], name, len) == 0 && var_names[i][len] == '\0') {
242 			res = var_names[i];
243 			goto end;
244 		}
245 
246 	if (!alloc) {
247 		res = NULL;
248 		goto end;
249 	}
250 
251 	/* Store variable name. If realloc fails, var_names remains valid */
252 	var_names2 = realloc(var_names, (var_names_nb + 1) * sizeof(*var_names));
253 	if (!var_names2) {
254 		memprintf(err, "out of memory error");
255 		res = NULL;
256 		goto end;
257 	}
258 	var_names_nb++;
259 	var_names = var_names2;
260 	var_names[var_names_nb - 1] = malloc(len + 1);
261 	if (!var_names[var_names_nb - 1]) {
262 		memprintf(err, "out of memory error");
263 		res = NULL;
264 		goto end;
265 	}
266 	memcpy(var_names[var_names_nb - 1], name, len);
267 	var_names[var_names_nb - 1][len] = '\0';
268 
269 	/* Check variable name syntax. */
270 	tmp = var_names[var_names_nb - 1];
271 	while (*tmp) {
272 		if (!isalnum((int)(unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
273 			memprintf(err, "invalid syntax at char '%s'", tmp);
274 			res = NULL;
275 			goto end;
276 		}
277 		tmp++;
278 	}
279 	res = var_names[var_names_nb - 1];
280 
281   end:
282 	if (alloc)
283 		HA_RWLOCK_WRUNLOCK(VARS_LOCK, &var_names_rwlock);
284 	else
285 		HA_RWLOCK_RDUNLOCK(VARS_LOCK, &var_names_rwlock);
286 
287 	return res;
288 }
289 
290 /* This function returns an existing variable or returns NULL. */
var_get(struct vars * vars,const char * name)291 static inline struct var *var_get(struct vars *vars, const char *name)
292 {
293 	struct var *var;
294 
295 	list_for_each_entry(var, &vars->head, l)
296 		if (var->name == name)
297 			return var;
298 	return NULL;
299 }
300 
301 /* Returns 0 if fails, else returns 1. */
smp_fetch_var(const struct arg * args,struct sample * smp,const char * kw,void * private)302 static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
303 {
304 	const struct var_desc *var_desc = &args[0].data.var;
305 	struct var *var;
306 	struct vars *vars;
307 
308 	/* Check the availibity of the variable. */
309 	vars = get_vars(smp->sess, smp->strm, var_desc->scope);
310 	if (!vars || vars->scope != var_desc->scope)
311 		return 0;
312 
313 	HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
314 	var = var_get(vars, var_desc->name);
315 
316 	/* check for the variable avalaibility */
317 	if (!var) {
318 		HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
319 		return 0;
320 	}
321 
322 	/* Duplicate the sample data because it could modified by another
323 	 * thread */
324 	smp->data = var->data;
325 	smp_dup(smp);
326 	smp->flags |= SMP_F_CONST;
327 
328 	HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
329 	return 1;
330 }
331 
332 /* This function search in the <head> a variable with the same
333  * pointer value that the <name>. If the variable doesn't exists,
334  * create it. The function stores a copy of smp> if the variable.
335  * It returns 0 if fails, else returns 1.
336  */
sample_store(struct vars * vars,const char * name,struct sample * smp)337 static int sample_store(struct vars *vars, const char *name, struct sample *smp)
338 {
339 	struct var *var;
340 
341 	/* Look for existing variable name. */
342 	var = var_get(vars, name);
343 
344 	if (var) {
345 		/* free its used memory. */
346 		if (var->data.type == SMP_T_STR ||
347 		    var->data.type == SMP_T_BIN) {
348 			free(var->data.u.str.str);
349 			var_accounting_diff(vars, smp->sess, smp->strm, -var->data.u.str.len);
350 		}
351 		else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
352 			free(var->data.u.meth.str.str);
353 			var_accounting_diff(vars, smp->sess, smp->strm, -var->data.u.meth.str.len);
354 		}
355 	} else {
356 
357 		/* Check memory avalaible. */
358 		if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
359 			return 0;
360 
361 		/* Create new entry. */
362 		var = pool_alloc(var_pool);
363 		if (!var)
364 			return 0;
365 		LIST_ADDQ(&vars->head, &var->l);
366 		var->name = name;
367 	}
368 
369 	/* Set type. */
370 	var->data.type = smp->data.type;
371 
372 	/* Copy data. If the data needs memory, the function can fail. */
373 	switch (var->data.type) {
374 	case SMP_T_BOOL:
375 	case SMP_T_SINT:
376 		var->data.u.sint = smp->data.u.sint;
377 		break;
378 	case SMP_T_IPV4:
379 		var->data.u.ipv4 = smp->data.u.ipv4;
380 		break;
381 	case SMP_T_IPV6:
382 		var->data.u.ipv6 = smp->data.u.ipv6;
383 		break;
384 	case SMP_T_STR:
385 	case SMP_T_BIN:
386 		if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.len)) {
387 			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
388 			return 0;
389 		}
390 		var->data.u.str.str = malloc(smp->data.u.str.len);
391 		if (!var->data.u.str.str) {
392 			var_accounting_diff(vars, smp->sess, smp->strm, -smp->data.u.str.len);
393 			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
394 			return 0;
395 		}
396 		var->data.u.str.len = smp->data.u.str.len;
397 		memcpy(var->data.u.str.str, smp->data.u.str.str, var->data.u.str.len);
398 		break;
399 	case SMP_T_METH:
400 		var->data.u.meth.meth = smp->data.u.meth.meth;
401 		if (smp->data.u.meth.meth != HTTP_METH_OTHER)
402 			break;
403 
404 		if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.len)) {
405 			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
406 			return 0;
407 		}
408 		var->data.u.meth.str.str = malloc(smp->data.u.meth.str.len);
409 		if (!var->data.u.meth.str.str) {
410 			var_accounting_diff(vars, smp->sess, smp->strm, -smp->data.u.meth.str.len);
411 			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
412 			return 0;
413 		}
414 		var->data.u.meth.str.len = smp->data.u.meth.str.len;
415 		var->data.u.meth.str.size = smp->data.u.meth.str.len;
416 		memcpy(var->data.u.meth.str.str, smp->data.u.meth.str.str, var->data.u.meth.str.len);
417 		break;
418 	}
419 	return 1;
420 }
421 
422 /* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
sample_store_stream(const char * name,enum vars_scope scope,struct sample * smp)423 static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp)
424 {
425 	struct vars *vars;
426 	int ret;
427 
428 	vars = get_vars(smp->sess, smp->strm, scope);
429 	if (!vars || vars->scope != scope)
430 		return 0;
431 
432 	HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
433 	ret = sample_store(vars, name, smp);
434 	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
435 	return ret;
436 }
437 
438 /* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
sample_clear_stream(const char * name,enum vars_scope scope,struct sample * smp)439 static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp)
440 {
441 	struct vars *vars;
442 	struct var  *var;
443 	unsigned int size = 0;
444 
445 	vars = get_vars(smp->sess, smp->strm, scope);
446 	if (!vars || vars->scope != scope)
447 		return 0;
448 
449 	/* Look for existing variable name. */
450 	HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
451 	var = var_get(vars, name);
452 	if (var) {
453 		size = var_clear(var);
454 		var_accounting_diff(vars, smp->sess, smp->strm, -size);
455 	}
456 	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
457 	return 1;
458 }
459 
460 /* Returns 0 if fails, else returns 1. */
smp_conv_store(const struct arg * args,struct sample * smp,void * private)461 static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
462 {
463 	return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp);
464 }
465 
466 /* Returns 0 if fails, else returns 1. */
smp_conv_clear(const struct arg * args,struct sample * smp,void * private)467 static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
468 {
469 	return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp);
470 }
471 
472 /* This fucntions check an argument entry and fill it with a variable
473  * type. The argumen must be a string. If the variable lookup fails,
474  * the function retuns 0 and fill <err>, otherwise it returns 1.
475  */
vars_check_arg(struct arg * arg,char ** err)476 int vars_check_arg(struct arg *arg, char **err)
477 {
478 	char *name;
479 	enum vars_scope scope;
480 
481 	/* Check arg type. */
482 	if (arg->type != ARGT_STR) {
483 		memprintf(err, "unexpected argument type");
484 		return 0;
485 	}
486 
487 	/* Register new variable name. */
488 	name = register_name(arg->data.str.str, arg->data.str.len, &scope, 1, err);
489 	if (!name)
490 		return 0;
491 
492 	/* Use the global variable name pointer. */
493 	arg->type = ARGT_VAR;
494 	arg->data.var.name = name;
495 	arg->data.var.scope = scope;
496 	return 1;
497 }
498 
499 /* This function store a sample in a variable if it was already defined.
500  * In error case, it fails silently.
501  */
vars_set_by_name_ifexist(const char * name,size_t len,struct sample * smp)502 void vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
503 {
504 	enum vars_scope scope;
505 
506 	/* Resolve name and scope. */
507 	name = register_name(name, len, &scope, 0, NULL);
508 	if (!name)
509 		return;
510 
511 	sample_store_stream(name, scope, smp);
512 }
513 
514 
515 /* This function store a sample in a variable.
516  * In error case, it fails silently.
517  */
vars_set_by_name(const char * name,size_t len,struct sample * smp)518 void vars_set_by_name(const char *name, size_t len, struct sample *smp)
519 {
520 	enum vars_scope scope;
521 
522 	/* Resolve name and scope. */
523 	name = register_name(name, len, &scope, 1, NULL);
524 	if (!name)
525 		return;
526 
527 	sample_store_stream(name, scope, smp);
528 }
529 
530 /* This function unset a variable if it was already defined.
531  * In error case, it fails silently.
532  */
vars_unset_by_name_ifexist(const char * name,size_t len,struct sample * smp)533 void vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
534 {
535 	enum vars_scope scope;
536 
537 	/* Resolve name and scope. */
538 	name = register_name(name, len, &scope, 0, NULL);
539 	if (!name)
540 		return;
541 
542 	sample_clear_stream(name, scope, smp);
543 }
544 
545 
546 /* This function unset a variable.
547  * In error case, it fails silently.
548  */
vars_unset_by_name(const char * name,size_t len,struct sample * smp)549 void vars_unset_by_name(const char *name, size_t len, struct sample *smp)
550 {
551 	enum vars_scope scope;
552 
553 	/* Resolve name and scope. */
554 	name = register_name(name, len, &scope, 1, NULL);
555 	if (!name)
556 		return;
557 
558 	sample_clear_stream(name, scope, smp);
559 }
560 
561 /* this function fills a sample with the
562  * variable content. Returns 1 if the sample
563  * is filled, otherwise it returns 0.
564  */
vars_get_by_name(const char * name,size_t len,struct sample * smp)565 int vars_get_by_name(const char *name, size_t len, struct sample *smp)
566 {
567 	struct vars *vars;
568 	struct var *var;
569 	enum vars_scope scope;
570 
571 	/* Resolve name and scope. */
572 	name = register_name(name, len, &scope, 1, NULL);
573 	if (!name)
574 		return 0;
575 
576 	/* Select "vars" pool according with the scope. */
577 	vars = get_vars(smp->sess, smp->strm, scope);
578 	if (!vars || vars->scope != scope)
579 		return 0;
580 
581 	/* Get the variable entry. */
582 	var = var_get(vars, name);
583 	if (!var)
584 		return 0;
585 
586 	/* Copy sample. */
587 	smp->data = var->data;
588 	smp->flags = SMP_F_CONST;
589 	return 1;
590 }
591 
592 /* this function fills a sample with the
593  * content of the varaible described by <var_desc>. Returns 1
594  * if the sample is filled, otherwise it returns 0.
595  */
vars_get_by_desc(const struct var_desc * var_desc,struct sample * smp)596 int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
597 {
598 	struct vars *vars;
599 	struct var *var;
600 
601 	/* Select "vars" pool according with the scope. */
602 	vars = get_vars(smp->sess, smp->strm, var_desc->scope);
603 
604 	/* Check if the scope is available a this point of processing. */
605 	if (!vars || vars->scope != var_desc->scope)
606 		return 0;
607 
608 	/* Get the variable entry. */
609 	var = var_get(vars, var_desc->name);
610 	if (!var)
611 		return 0;
612 
613 	/* Copy sample. */
614 	smp->data = var->data;
615 	smp->flags = SMP_F_CONST;
616 	return 1;
617 }
618 
619 /* Always returns ACT_RET_CONT even if an error occurs. */
action_store(struct act_rule * rule,struct proxy * px,struct session * sess,struct stream * s,int flags)620 static enum act_return action_store(struct act_rule *rule, struct proxy *px,
621                                     struct session *sess, struct stream *s, int flags)
622 {
623 	struct sample smp;
624 	int dir;
625 
626 	switch (rule->from) {
627 	case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
628 	case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
629 	case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
630 	case ACT_F_HTTP_REQ:    dir = SMP_OPT_DIR_REQ; break;
631 	case ACT_F_HTTP_RES:    dir = SMP_OPT_DIR_RES; break;
632 	default:
633 		send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
634 		if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
635 			ha_alert("Vars: internal error while execute action store.\n");
636 		return ACT_RET_CONT;
637 	}
638 
639 	/* Process the expression. */
640 	memset(&smp, 0, sizeof(smp));
641 	if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
642 	                    rule->arg.vars.expr, &smp))
643 		return ACT_RET_CONT;
644 
645 	/* Store the sample, and ignore errors. */
646 	sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
647 	return ACT_RET_CONT;
648 }
649 
650 /* Always returns ACT_RET_CONT even if an error occurs. */
action_clear(struct act_rule * rule,struct proxy * px,struct session * sess,struct stream * s,int flags)651 static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
652                                     struct session *sess, struct stream *s, int flags)
653 {
654 	struct sample smp;
655 
656 	memset(&smp, 0, sizeof(smp));
657 	smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
658 
659 	/* Clear the variable using the sample context, and ignore errors. */
660 	sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
661 	return ACT_RET_CONT;
662 }
663 
664 /* This two function checks the variable name and replace the
665  * configuration string name by the global string name. its
666  * the same string, but the global pointer can be easy to
667  * compare.
668  *
669  * The first function checks a sample-fetch and the second
670  * checks a converter.
671  */
smp_check_var(struct arg * args,char ** err)672 static int smp_check_var(struct arg *args, char **err)
673 {
674 	return vars_check_arg(&args[0], err);
675 }
676 
conv_check_var(struct arg * args,struct sample_conv * conv,const char * file,int line,char ** err_msg)677 static int conv_check_var(struct arg *args, struct sample_conv *conv,
678                           const char *file, int line, char **err_msg)
679 {
680 	return vars_check_arg(&args[0], err_msg);
681 }
682 
683 /* This function is a common parser for using variables. It understands
684  * the format:
685  *
686  *   set-var(<variable-name>) <expression>
687  *   unset-var(<variable-name>)
688  *
689  * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
690  * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
691  * is filled with the pointer to the expression to execute.
692  */
parse_store(const char ** args,int * arg,struct proxy * px,struct act_rule * rule,char ** err)693 static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
694                                       struct act_rule *rule, char **err)
695 {
696 	const char *var_name = args[*arg-1];
697 	int var_len;
698 	const char *kw_name;
699 	int flags, set_var = 0;
700 
701 	if (!strncmp(var_name, "set-var", 7)) {
702 		var_name += 7;
703 		set_var   = 1;
704 	}
705 	if (!strncmp(var_name, "unset-var", 9)) {
706 		var_name += 9;
707 		set_var   = 0;
708 	}
709 
710 	if (*var_name != '(') {
711 		memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
712 			  args[*arg-1]);
713 		return ACT_RET_PRS_ERR;
714 	}
715 	var_name++; /* jump the '(' */
716 	var_len = strlen(var_name);
717 	var_len--; /* remove the ')' */
718 	if (var_name[var_len] != ')') {
719 		memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
720 			  args[*arg-1]);
721 		return ACT_RET_PRS_ERR;
722 	}
723 
724 	rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err);
725 	if (!rule->arg.vars.name)
726 		return ACT_RET_PRS_ERR;
727 
728 	/* There is no fetch method when variable is unset. Just set the right
729 	 * action and return. */
730 	if (!set_var) {
731 		rule->action     = ACT_CUSTOM;
732 		rule->action_ptr = action_clear;
733 		return ACT_RET_PRS_OK;
734 	}
735 
736 	kw_name = args[*arg-1];
737 
738 	rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
739 	                                        px->conf.args.line, err, &px->conf.args);
740 	if (!rule->arg.vars.expr)
741 		return ACT_RET_PRS_ERR;
742 
743 	switch (rule->from) {
744 	case ACT_F_TCP_REQ_SES: flags = SMP_VAL_FE_SES_ACC; break;
745 	case ACT_F_TCP_REQ_CNT: flags = SMP_VAL_FE_REQ_CNT; break;
746 	case ACT_F_TCP_RES_CNT: flags = SMP_VAL_BE_RES_CNT; break;
747 	case ACT_F_HTTP_REQ:    flags = SMP_VAL_FE_HRQ_HDR; break;
748 	case ACT_F_HTTP_RES:    flags = SMP_VAL_BE_HRS_HDR; break;
749 	default:
750 		memprintf(err,
751 			  "internal error, unexpected rule->from=%d, please report this bug!",
752 			  rule->from);
753 		return ACT_RET_PRS_ERR;
754 	}
755 	if (!(rule->arg.vars.expr->fetch->val & flags)) {
756 		memprintf(err,
757 			  "fetch method '%s' extracts information from '%s', none of which is available here",
758 			  kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
759 		free(rule->arg.vars.expr);
760 		return ACT_RET_PRS_ERR;
761 	}
762 
763 	rule->action     = ACT_CUSTOM;
764 	rule->action_ptr = action_store;
765 	return ACT_RET_PRS_OK;
766 }
767 
vars_max_size(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err,unsigned int * limit)768 static int vars_max_size(char **args, int section_type, struct proxy *curpx,
769                          struct proxy *defpx, const char *file, int line,
770                          char **err, unsigned int *limit)
771 {
772 	char *error;
773 
774 	*limit = strtol(args[1], &error, 10);
775 	if (*error != 0) {
776 		memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
777 		return -1;
778 	}
779 	return 0;
780 }
781 
vars_max_size_global(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)782 static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
783                                 struct proxy *defpx, const char *file, int line,
784                                 char **err)
785 {
786 	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
787 }
788 
vars_max_size_proc(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)789 static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
790                                 struct proxy *defpx, const char *file, int line,
791                                 char **err)
792 {
793 	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
794 }
795 
vars_max_size_sess(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)796 static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
797                               struct proxy *defpx, const char *file, int line,
798                               char **err)
799 {
800 	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
801 }
802 
vars_max_size_txn(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)803 static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
804                              struct proxy *defpx, const char *file, int line,
805                              char **err)
806 {
807 	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
808 }
809 
vars_max_size_reqres(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)810 static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
811                                 struct proxy *defpx, const char *file, int line,
812                                 char **err)
813 {
814 	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
815 }
816 
817 static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
818 
819 	{ "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_L4CLI },
820 	{ /* END */ },
821 }};
822 
823 static struct sample_conv_kw_list sample_conv_kws = {ILH, {
824 	{ "set-var",   smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
825 	{ "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
826 	{ /* END */ },
827 }};
828 
829 static struct action_kw_list tcp_req_sess_kws = { { }, {
830 	{ "set-var",   parse_store, 1 },
831 	{ "unset-var", parse_store, 1 },
832 	{ /* END */ }
833 }};
834 
835 static struct action_kw_list tcp_req_cont_kws = { { }, {
836 	{ "set-var",   parse_store, 1 },
837 	{ "unset-var", parse_store, 1 },
838 	{ /* END */ }
839 }};
840 
841 static struct action_kw_list tcp_res_kws = { { }, {
842 	{ "set-var",   parse_store, 1 },
843 	{ "unset-var", parse_store, 1 },
844 	{ /* END */ }
845 }};
846 
847 static struct action_kw_list http_req_kws = { { }, {
848 	{ "set-var",   parse_store, 1 },
849 	{ "unset-var", parse_store, 1 },
850 	{ /* END */ }
851 }};
852 
853 static struct action_kw_list http_res_kws = { { }, {
854 	{ "set-var",   parse_store, 1 },
855 	{ "unset-var", parse_store, 1 },
856 	{ /* END */ }
857 }};
858 
859 static struct cfg_kw_list cfg_kws = {{ },{
860 	{ CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
861 	{ CFG_GLOBAL, "tune.vars.proc-max-size",   vars_max_size_proc   },
862 	{ CFG_GLOBAL, "tune.vars.sess-max-size",   vars_max_size_sess   },
863 	{ CFG_GLOBAL, "tune.vars.txn-max-size",    vars_max_size_txn    },
864 	{ CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
865 	{ /* END */ }
866 }};
867 
868 __attribute__((constructor))
__vars_init(void)869 static void __vars_init(void)
870 {
871 	var_pool = create_pool("vars", sizeof(struct var), MEM_F_SHARED);
872 
873 	sample_register_fetches(&sample_fetch_keywords);
874 	sample_register_convs(&sample_conv_kws);
875 	tcp_req_sess_keywords_register(&tcp_req_sess_kws);
876 	tcp_req_cont_keywords_register(&tcp_req_cont_kws);
877 	tcp_res_cont_keywords_register(&tcp_res_kws);
878 	http_req_keywords_register(&http_req_kws);
879 	http_res_keywords_register(&http_res_kws);
880 	cfg_register_keywords(&cfg_kws);
881 
882 	HA_RWLOCK_INIT(&var_names_rwlock);
883 }
884