2015-06-06 13:29:07 -04:00
# include <ctype.h>
2020-05-27 06:58:42 -04:00
# include <haproxy/api.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/arg.h>
2021-03-26 09:51:31 -04:00
# include <haproxy/buf.h>
2020-06-04 18:00:29 -04:00
# include <haproxy/cfgparse.h>
2020-06-04 12:21:56 -04:00
# include <haproxy/check.h>
2021-03-26 09:51:31 -04:00
# include <haproxy/cli.h>
2020-06-04 17:46:14 -04:00
# include <haproxy/global.h>
2020-06-02 13:11:26 -04:00
# include <haproxy/http.h>
2020-06-04 05:40:28 -04:00
# include <haproxy/http_rules.h>
2020-05-27 12:01:47 -04:00
# include <haproxy/list.h>
2020-06-05 11:27:29 -04:00
# include <haproxy/log.h>
2020-06-04 09:33:47 -04:00
# include <haproxy/sample.h>
2021-09-03 03:02:47 -04:00
# include <haproxy/session.h>
2020-06-04 17:46:14 -04:00
# include <haproxy/stream-t.h>
2020-06-04 11:42:48 -04:00
# include <haproxy/tcp_rules.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/tcpcheck.h>
2021-05-08 07:56:31 -04:00
# include <haproxy/tools.h>
2020-06-04 10:25:31 -04:00
# include <haproxy/vars.h>
2021-10-06 11:11:51 -04:00
# include <haproxy/xxhash.h>
2015-06-06 13:29:07 -04:00
/* This contains a pool of struct vars */
2018-11-26 05:58:30 -05:00
DECLARE_STATIC_POOL ( var_pool , " vars " , sizeof ( struct var ) ) ;
2015-06-06 13:29:07 -04:00
2021-05-08 05:41:28 -04:00
/* list of variables for the process scope. */
struct vars proc_vars THREAD_ALIGNED ( 64 ) ;
2015-06-06 13:29:07 -04:00
/* This array of int contains the system limits per context. */
static unsigned int var_global_limit = 0 ;
2016-11-09 05:36:17 -05:00
static unsigned int var_proc_limit = 0 ;
2015-06-06 13:29:07 -04:00
static unsigned int var_sess_limit = 0 ;
static unsigned int var_txn_limit = 0 ;
static unsigned int var_reqres_limit = 0 ;
2020-02-21 12:13:44 -05:00
static unsigned int var_check_limit = 0 ;
2021-08-31 02:48:55 -04:00
static uint64_t var_name_hash_seed = 0 ;
2015-06-06 13:29:07 -04:00
2021-12-16 11:14:37 -05:00
/* Structure and array matching set-var conditions to their respective flag
* value .
*/
struct var_set_condition {
const char * cond_str ;
uint flag ;
} ;
static struct var_set_condition conditions_array [ ] = {
{ " ifexists " , VF_COND_IFEXISTS } ,
{ " ifnotexists " , VF_COND_IFNOTEXISTS } ,
{ " ifempty " , VF_COND_IFEMPTY } ,
{ " ifnotempty " , VF_COND_IFNOTEMPTY } ,
{ " ifset " , VF_COND_IFSET } ,
{ " ifnotset " , VF_COND_IFNOTSET } ,
{ " ifgt " , VF_COND_IFGT } ,
{ " iflt " , VF_COND_IFLT } ,
{ NULL , 0 }
} ;
2019-06-04 10:27:36 -04:00
/* returns the struct vars pointer for a session, stream and scope, or NULL if
* it does not exist .
*/
static inline struct vars * get_vars ( struct session * sess , struct stream * strm , enum vars_scope scope )
{
switch ( scope ) {
case SCOPE_PROC :
2021-05-08 05:41:28 -04:00
return & proc_vars ;
2019-06-04 10:27:36 -04:00
case SCOPE_SESS :
2021-03-26 06:27:59 -04:00
return sess ? & sess - > vars : NULL ;
2020-02-21 12:13:44 -05:00
case SCOPE_CHECK : {
2021-06-02 05:48:42 -04:00
struct check * check = sess ? objt_check ( sess - > origin ) : NULL ;
2020-02-21 12:13:44 -05:00
2020-04-21 05:53:32 -04:00
return check ? & check - > vars : NULL ;
2020-02-21 12:13:44 -05:00
}
2019-06-04 10:27:36 -04:00
case SCOPE_TXN :
return strm ? & strm - > vars_txn : NULL ;
case SCOPE_REQ :
case SCOPE_RES :
default :
return strm ? & strm - > vars_reqres : NULL ;
}
}
2015-06-19 05:21:56 -04:00
/* This function adds or remove memory size from the accounting. The inner
* pointers may be null when setting the outer ones only .
*/
2020-12-09 10:34:29 -05:00
void var_accounting_diff ( struct vars * vars , struct session * sess , struct stream * strm , int size )
2015-06-06 13:29:07 -04:00
{
switch ( vars - > scope ) {
case SCOPE_REQ :
case SCOPE_RES :
2021-09-08 09:51:06 -04:00
if ( var_reqres_limit & & strm )
2019-06-04 10:27:36 -04:00
_HA_ATOMIC_ADD ( & strm - > vars_reqres . size , size ) ;
2016-03-10 10:33:04 -05:00
/* fall through */
2015-06-06 13:29:07 -04:00
case SCOPE_TXN :
2021-09-08 09:51:06 -04:00
if ( var_txn_limit & & strm )
2019-06-04 10:27:36 -04:00
_HA_ATOMIC_ADD ( & strm - > vars_txn . size , size ) ;
2020-02-21 12:13:44 -05:00
goto scope_sess ;
2021-09-08 09:51:06 -04:00
case SCOPE_CHECK :
if ( var_check_limit ) {
2020-04-21 05:53:32 -04:00
struct check * check = objt_check ( sess - > origin ) ;
2020-02-21 12:13:44 -05:00
2020-04-21 05:53:32 -04:00
if ( check )
_HA_ATOMIC_ADD ( & check - > vars . size , size ) ;
2020-02-21 12:13:44 -05:00
}
2016-03-10 10:33:04 -05:00
/* fall through */
2020-02-21 12:13:44 -05:00
scope_sess :
2015-06-06 13:29:07 -04:00
case SCOPE_SESS :
2021-09-08 09:51:06 -04:00
if ( var_sess_limit )
_HA_ATOMIC_ADD ( & sess - > vars . size , size ) ;
2016-11-09 05:36:17 -05:00
/* fall through */
case SCOPE_PROC :
2021-09-08 09:51:06 -04:00
if ( var_proc_limit | | var_global_limit )
_HA_ATOMIC_ADD ( & proc_vars . size , size ) ;
2015-06-06 13:29:07 -04:00
}
}
/* This function returns 1 if the <size> is available in the var
2018-11-15 12:19:50 -05:00
* pool < vars > , otherwise returns 0. If the space is available ,
2015-06-19 05:21:56 -04:00
* the size is reserved . The inner pointers may be null when setting
2016-03-10 10:33:04 -05:00
* the outer ones only . The accounting uses either < sess > or < strm >
* depending on the scope . < strm > may be NULL when no stream is known
* and only the session exists ( eg : tcp - request connection ) .
2015-06-06 13:29:07 -04:00
*/
2016-03-10 10:33:04 -05:00
static int var_accounting_add ( struct vars * vars , struct session * sess , struct stream * strm , int size )
2015-06-06 13:29:07 -04:00
{
switch ( vars - > scope ) {
case SCOPE_REQ :
case SCOPE_RES :
2019-06-04 10:27:36 -04:00
if ( var_reqres_limit & & strm & & strm - > vars_reqres . size + size > var_reqres_limit )
2015-06-06 13:29:07 -04:00
return 0 ;
2016-03-10 10:33:04 -05:00
/* fall through */
2015-06-06 13:29:07 -04:00
case SCOPE_TXN :
2019-06-04 10:27:36 -04:00
if ( var_txn_limit & & strm & & strm - > vars_txn . size + size > var_txn_limit )
2015-06-06 13:29:07 -04:00
return 0 ;
2020-02-21 12:13:44 -05:00
goto scope_sess ;
case SCOPE_CHECK : {
2020-04-21 05:53:32 -04:00
struct check * check = objt_check ( sess - > origin ) ;
2020-02-21 12:13:44 -05:00
2020-04-21 05:53:32 -04:00
if ( var_check_limit & & check & & check - > vars . size + size > var_check_limit )
2020-02-21 12:13:44 -05:00
return 0 ;
}
2016-03-10 10:33:04 -05:00
/* fall through */
2020-02-21 12:13:44 -05:00
scope_sess :
2015-06-06 13:29:07 -04:00
case SCOPE_SESS :
2016-03-10 10:33:04 -05:00
if ( var_sess_limit & & sess - > vars . size + size > var_sess_limit )
2015-06-06 13:29:07 -04:00
return 0 ;
2016-11-09 05:36:17 -05:00
/* fall through */
case SCOPE_PROC :
2021-09-08 09:40:58 -04:00
/* note: scope proc collects all others and is currently identical to the
* global limit .
*/
2021-05-08 05:41:28 -04:00
if ( var_proc_limit & & proc_vars . size + size > var_proc_limit )
2016-11-09 05:36:17 -05:00
return 0 ;
2021-09-08 09:40:58 -04:00
if ( var_global_limit & & proc_vars . size + size > var_global_limit )
2015-06-06 13:29:07 -04:00
return 0 ;
}
2016-03-10 10:33:04 -05:00
var_accounting_diff ( vars , sess , strm , size ) ;
2015-06-06 13:29:07 -04:00
return 1 ;
}
2021-09-08 09:03:58 -04:00
/* This function removes a variable from the list and frees the memory it was
* using . If the variable is marked " VF_PERMANENT " , the sample_data is only
* reset to SMP_T_ANY unless < force > is non nul . Returns the freed size .
*/
unsigned int var_clear ( struct var * var , int force )
2016-11-09 10:54:56 -05:00
{
unsigned int size = 0 ;
if ( var - > data . type = = SMP_T_STR | | var - > data . type = = SMP_T_BIN ) {
2021-02-26 15:19:53 -05:00
ha_free ( & var - > data . u . str . area ) ;
2018-07-13 04:54:26 -04:00
size + = var - > data . u . str . data ;
2016-11-09 10:54:56 -05:00
}
2017-07-24 10:24:39 -04:00
else if ( var - > data . type = = SMP_T_METH & & var - > data . u . meth . meth = = HTTP_METH_OTHER ) {
2021-02-26 15:19:53 -05:00
ha_free ( & var - > data . u . meth . str . area ) ;
2018-07-13 04:54:26 -04:00
size + = var - > data . u . meth . str . data ;
2016-11-09 10:54:56 -05:00
}
2021-09-08 09:03:58 -04:00
/* wipe the sample */
var - > data . type = SMP_T_ANY ;
if ( ! ( var - > flags & VF_PERMANENT ) | | force ) {
LIST_DELETE ( & var - > l ) ;
pool_free ( var_pool , var ) ;
size + = sizeof ( struct var ) ;
}
2016-11-09 10:54:56 -05:00
return size ;
}
2018-11-15 12:19:50 -05:00
/* This function free all the memory used by all the variables
2015-06-06 13:29:07 -04:00
* in the list .
*/
2016-03-10 10:33:04 -05:00
void vars_prune ( struct vars * vars , struct session * sess , struct stream * strm )
2015-06-06 13:29:07 -04:00
{
struct var * var , * tmp ;
2015-06-19 05:21:56 -04:00
unsigned int size = 0 ;
2015-06-06 13:29:07 -04:00
2021-09-08 09:19:57 -04:00
vars_wrlock ( vars ) ;
2015-06-06 13:29:07 -04:00
list_for_each_entry_safe ( var , tmp , & vars - > head , l ) {
2021-09-08 09:03:58 -04:00
size + = var_clear ( var , 1 ) ;
2015-06-06 13:29:07 -04:00
}
2021-09-08 09:19:57 -04:00
vars_wrunlock ( vars ) ;
2016-03-10 10:33:04 -05:00
var_accounting_diff ( vars , sess , strm , - size ) ;
2015-06-19 05:59:02 -04:00
}
/* This function frees all the memory used by all the session variables in the
* list starting at < vars > .
*/
void vars_prune_per_sess ( struct vars * vars )
{
struct var * var , * tmp ;
unsigned int size = 0 ;
2021-09-08 09:19:57 -04:00
vars_wrlock ( vars ) ;
2015-06-19 05:59:02 -04:00
list_for_each_entry_safe ( var , tmp , & vars - > head , l ) {
2021-09-08 09:03:58 -04:00
size + = var_clear ( var , 1 ) ;
2015-06-19 05:59:02 -04:00
}
2021-09-08 09:19:57 -04:00
vars_wrunlock ( vars ) ;
2017-07-24 10:30:34 -04:00
2021-09-08 09:51:06 -04:00
if ( var_sess_limit )
_HA_ATOMIC_SUB ( & vars - > size , size ) ;
if ( var_proc_limit | | var_global_limit )
_HA_ATOMIC_SUB ( & proc_vars . size , size ) ;
2015-06-06 13:29:07 -04:00
}
2021-08-31 02:13:25 -04:00
/* This function initializes a variables list head */
void vars_init_head ( struct vars * vars , enum vars_scope scope )
2015-06-06 13:29:07 -04:00
{
LIST_INIT ( & vars - > head ) ;
vars - > scope = scope ;
vars - > size = 0 ;
2017-11-07 04:42:54 -05:00
HA_RWLOCK_INIT ( & vars - > rwlock ) ;
2015-06-06 13:29:07 -04:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This function returns a hash value and a scope for a variable name of a
* specified length . It makes sure that the scope is valid . It returns non - zero
* on success , 0 on failure . Neither hash nor scope may be NULL .
2015-06-06 13:29:07 -04:00
*/
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
static int vars_hash_name ( const char * name , int len , enum vars_scope * scope ,
uint64_t * hash , char * * err )
2015-06-06 13:29:07 -04:00
{
const char * tmp ;
/* Check length. */
if ( len = = 0 ) {
memprintf ( err , " Empty variable name cannot be accepted " ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return 0 ;
2015-06-06 13:29:07 -04:00
}
/* Check scope. */
2016-11-09 05:36:17 -05:00
if ( len > 5 & & strncmp ( name , " proc. " , 5 ) = = 0 ) {
name + = 5 ;
len - = 5 ;
* scope = SCOPE_PROC ;
}
else if ( len > 5 & & strncmp ( name , " sess. " , 5 ) = = 0 ) {
2015-06-06 13:29:07 -04:00
name + = 5 ;
len - = 5 ;
* scope = SCOPE_SESS ;
}
else if ( len > 4 & & strncmp ( name , " txn. " , 4 ) = = 0 ) {
name + = 4 ;
len - = 4 ;
* scope = SCOPE_TXN ;
}
else if ( len > 4 & & strncmp ( name , " req. " , 4 ) = = 0 ) {
name + = 4 ;
len - = 4 ;
* scope = SCOPE_REQ ;
}
else if ( len > 4 & & strncmp ( name , " res. " , 4 ) = = 0 ) {
name + = 4 ;
len - = 4 ;
* scope = SCOPE_RES ;
}
2020-02-21 12:13:44 -05:00
else if ( len > 6 & & strncmp ( name , " check. " , 6 ) = = 0 ) {
name + = 6 ;
len - = 6 ;
* scope = SCOPE_CHECK ;
}
2015-06-06 13:29:07 -04:00
else {
2021-09-03 04:12:55 -04:00
memprintf ( err , " invalid variable name '%.*s'. A variable name must be start by its scope. "
" The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check' " , len , name ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return 0 ;
2015-06-06 13:29:07 -04:00
}
/* Check variable name syntax. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
for ( tmp = name ; tmp < name + len ; tmp + + ) {
2020-02-25 02:16:33 -05:00
if ( ! isalnum ( ( unsigned char ) * tmp ) & & * tmp ! = ' _ ' & & * tmp ! = ' . ' ) {
2015-06-06 13:29:07 -04:00
memprintf ( err , " invalid syntax at char '%s' " , tmp ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return 0 ;
2015-06-06 13:29:07 -04:00
}
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
* hash = XXH3 ( name , len , var_name_hash_seed ) ;
return 1 ;
2015-06-06 13:29:07 -04:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This function returns the variable from the given list that matches
* < name_hash > or returns NULL if not found . It ' s only a linked list since it
* is not expected to have many variables per scope ( a few tens at best ) .
* The caller is responsible for ensuring that < vars > is properly locked .
*/
static struct var * var_get ( struct vars * vars , uint64_t name_hash )
2015-06-06 13:29:07 -04:00
{
struct var * var ;
list_for_each_entry ( var , & vars - > head , l )
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( var - > name_hash = = name_hash )
2015-06-06 13:29:07 -04:00
return var ;
return NULL ;
}
/* Returns 0 if fails, else returns 1. */
static int smp_fetch_var ( const struct arg * args , struct sample * smp , const char * kw , void * private )
{
const struct var_desc * var_desc = & args [ 0 ] . data . var ;
2021-09-03 06:00:13 -04:00
const struct buffer * def = NULL ;
2017-07-24 10:30:34 -04:00
2021-09-03 06:00:13 -04:00
if ( args [ 1 ] . type = = ARGT_STR )
def = & args [ 1 ] . data . str ;
return vars_get_by_desc ( var_desc , smp , def ) ;
2015-06-06 13:29:07 -04:00
}
2021-12-16 11:14:36 -05:00
/*
* Clear the contents of a variable so that it can be reset directly .
* This function is used just before a variable is filled out of a sample ' s
* content .
*/
static inline void var_clear_buffer ( struct sample * smp , struct vars * vars , struct var * var , int var_type )
{
if ( var_type = = SMP_T_STR | | var_type = = SMP_T_BIN ) {
ha_free ( & var - > data . u . str . area ) ;
var_accounting_diff ( vars , smp - > sess , smp - > strm ,
- var - > data . u . str . data ) ;
}
else if ( var_type = = SMP_T_METH & & var - > data . u . meth . meth = = HTTP_METH_OTHER ) {
ha_free ( & var - > data . u . meth . str . area ) ;
var_accounting_diff ( vars , smp - > sess , smp - > strm ,
- var - > data . u . meth . str . data ) ;
}
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This function tries to create a variable whose name hash is <name_hash> in
* scope < scope > and store sample < smp > as its value .
*
* The stream and session are extracted from < smp > , whose stream may be NULL
* when scope is SCOPE_SESS . In case there wouldn ' t be enough memory to store
* the sample while the variable was already created , it would be changed to
* a bool ( which is memory - less ) .
MEDIUM: vars: make the ifexist variant of set-var only apply to the proc scope
When setting variables, there are currently two variants, one which will
always create the variable, and another one, "ifexist", which will only
create or update a variable if a similarly named variable in any scope
already existed before.
The goal was to limit the risk of injecting random names in the proc
scope, but it was achieved by making use of the somewhat limited name
indexing model, which explains the scope-agnostic restriction.
With this change, we're moving the check downwards in the chain, at the
variable level, and only variables under the scope "proc" will be subject
to the restriction. A new set of VF_* flags was added to adjust how
variables are set, and VF_UPDATEONLY is used to mention this restriction.
In this exact state of affairs, this is not completely exact, as if a
similar name was not known in any scope, the variable will continue to
be rejected like before, but this will change soon.
2021-09-07 08:24:07 -04:00
*
* Flags is a bitfield that may contain one of the following flags :
2021-09-08 05:38:25 -04:00
* - VF_CREATEONLY : do nothing if the variable already exists ( success ) .
2021-09-08 05:07:32 -04:00
* - VF_PERMANENT : this flag will be passed to the variable upon creation
MEDIUM: vars: make the ifexist variant of set-var only apply to the proc scope
When setting variables, there are currently two variants, one which will
always create the variable, and another one, "ifexist", which will only
create or update a variable if a similarly named variable in any scope
already existed before.
The goal was to limit the risk of injecting random names in the proc
scope, but it was achieved by making use of the somewhat limited name
indexing model, which explains the scope-agnostic restriction.
With this change, we're moving the check downwards in the chain, at the
variable level, and only variables under the scope "proc" will be subject
to the restriction. A new set of VF_* flags was added to adjust how
variables are set, and VF_UPDATEONLY is used to mention this restriction.
In this exact state of affairs, this is not completely exact, as if a
similar name was not known in any scope, the variable will continue to
be rejected like before, but this will change soon.
2021-09-07 08:24:07 -04:00
*
2021-12-16 11:14:37 -05:00
* - VF_COND_IFEXISTS : only set variable if it already exists
* - VF_COND_IFNOTEXISTS : only set variable if it did not exist yet
* - VF_COND_IFEMPTY : only set variable if sample is empty
* - VF_COND_IFNOTEMPTY : only set variable if sample is not empty
* - VF_COND_IFSET : only set variable if its type is not SMP_TYPE_ANY
* - VF_COND_IFNOTSET : only set variable if its type is ANY
* - VF_COND_IFGT : only set variable if its value is greater than the sample ' s
2021-12-25 01:45:52 -05:00
* - VF_COND_IFLT : only set variable if its value is less than the sample ' s
2021-12-16 11:14:37 -05:00
*
2021-09-07 05:37:37 -04:00
* It returns 0 on failure , non - zero on success .
2015-06-06 13:29:07 -04:00
*/
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
static int var_set ( uint64_t name_hash , enum vars_scope scope , struct sample * smp , uint flags )
2015-06-06 13:29:07 -04:00
{
2021-09-07 05:37:37 -04:00
struct vars * vars ;
2015-06-06 13:29:07 -04:00
struct var * var ;
2021-09-07 05:37:37 -04:00
int ret = 0 ;
2021-12-16 11:14:36 -05:00
int previous_type = SMP_T_ANY ;
2021-09-07 05:37:37 -04:00
vars = get_vars ( smp - > sess , smp - > strm , scope ) ;
if ( ! vars | | vars - > scope ! = scope )
return 0 ;
2021-09-08 09:19:57 -04:00
vars_wrlock ( vars ) ;
2015-06-06 13:29:07 -04:00
/* Look for existing variable name. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
var = var_get ( vars , name_hash ) ;
2015-06-06 13:29:07 -04:00
if ( var ) {
2021-09-08 05:38:25 -04:00
if ( flags & VF_CREATEONLY ) {
ret = 1 ;
goto unlock ;
}
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
if ( flags & VF_COND_IFNOTEXISTS )
goto unlock ;
2015-06-06 13:29:07 -04:00
} else {
2021-12-16 11:14:37 -05:00
if ( flags & VF_COND_IFEXISTS )
MEDIUM: vars: make the ifexist variant of set-var only apply to the proc scope
When setting variables, there are currently two variants, one which will
always create the variable, and another one, "ifexist", which will only
create or update a variable if a similarly named variable in any scope
already existed before.
The goal was to limit the risk of injecting random names in the proc
scope, but it was achieved by making use of the somewhat limited name
indexing model, which explains the scope-agnostic restriction.
With this change, we're moving the check downwards in the chain, at the
variable level, and only variables under the scope "proc" will be subject
to the restriction. A new set of VF_* flags was added to adjust how
variables are set, and VF_UPDATEONLY is used to mention this restriction.
In this exact state of affairs, this is not completely exact, as if a
similar name was not known in any scope, the variable will continue to
be rejected like before, but this will change soon.
2021-09-07 08:24:07 -04:00
goto unlock ;
2018-11-15 12:19:50 -05:00
/* Check memory available. */
2016-03-10 10:33:04 -05:00
if ( ! var_accounting_add ( vars , smp - > sess , smp - > strm , sizeof ( struct var ) ) )
2021-09-07 05:37:37 -04:00
goto unlock ;
2015-06-06 13:29:07 -04:00
/* Create new entry. */
2017-11-24 11:34:44 -05:00
var = pool_alloc ( var_pool ) ;
2015-06-06 13:29:07 -04:00
if ( ! var )
2021-09-07 05:37:37 -04:00
goto unlock ;
2021-04-21 01:32:39 -04:00
LIST_APPEND ( & vars - > head , & var - > l ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
var - > name_hash = name_hash ;
2021-09-08 05:07:32 -04:00
var - > flags = flags & VF_PERMANENT ;
2021-12-16 11:14:35 -05:00
var - > data . type = SMP_T_ANY ;
2015-06-06 13:29:07 -04:00
}
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
/* A variable of type SMP_T_ANY is considered as unset (either created
* and never set or unset - var was called on it ) .
*/
if ( ( flags & VF_COND_IFSET & & var - > data . type = = SMP_T_ANY ) | |
( flags & VF_COND_IFNOTSET & & var - > data . type ! = SMP_T_ANY ) )
goto unlock ;
2015-06-06 13:29:07 -04:00
/* Set type. */
2021-12-16 11:14:36 -05:00
previous_type = var - > data . type ;
2015-08-19 03:00:18 -04:00
var - > data . type = smp - > data . type ;
2015-06-06 13:29:07 -04:00
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
if ( flags & VF_COND_IFEMPTY ) {
switch ( smp - > data . type ) {
case SMP_T_ANY :
case SMP_T_STR :
case SMP_T_BIN :
/* The actual test on the contents of the sample will be
* performed later .
*/
break ;
default :
/* The sample cannot be empty since it has a scalar type. */
var - > data . type = previous_type ;
goto unlock ;
}
}
2015-06-06 13:29:07 -04:00
/* Copy data. If the data needs memory, the function can fail. */
switch ( var - > data . type ) {
case SMP_T_BOOL :
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
var - > data . u . sint = smp - > data . u . sint ;
break ;
2015-06-06 13:29:07 -04:00
case SMP_T_SINT :
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
if ( previous_type = = var - > data . type ) {
if ( ( ( flags & VF_COND_IFGT ) & & ! ( var - > data . u . sint > smp - > data . u . sint ) ) | |
( ( flags & VF_COND_IFLT ) & & ! ( var - > data . u . sint < smp - > data . u . sint ) ) )
goto unlock ;
}
2021-12-16 11:14:36 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
2015-08-19 03:07:19 -04:00
var - > data . u . sint = smp - > data . u . sint ;
2015-06-06 13:29:07 -04:00
break ;
case SMP_T_IPV4 :
2021-12-16 11:14:36 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
2015-08-19 03:07:19 -04:00
var - > data . u . ipv4 = smp - > data . u . ipv4 ;
2015-06-06 13:29:07 -04:00
break ;
case SMP_T_IPV6 :
2021-12-16 11:14:36 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
2015-08-19 03:07:19 -04:00
var - > data . u . ipv6 = smp - > data . u . ipv6 ;
2015-06-06 13:29:07 -04:00
break ;
case SMP_T_STR :
case SMP_T_BIN :
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
if ( ( flags & VF_COND_IFNOTEMPTY & & ! smp - > data . u . str . data ) | |
( flags & VF_COND_IFEMPTY & & smp - > data . u . str . data ) ) {
var - > data . type = previous_type ;
goto unlock ;
}
2021-12-16 11:14:36 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
2018-07-13 04:54:26 -04:00
if ( ! var_accounting_add ( vars , smp - > sess , smp - > strm , smp - > data . u . str . data ) ) {
2015-06-06 13:29:07 -04:00
var - > data . type = SMP_T_BOOL ; /* This type doesn't use additional memory. */
2021-09-07 05:37:37 -04:00
goto unlock ;
2015-06-06 13:29:07 -04:00
}
2021-09-07 05:37:37 -04:00
2018-07-13 04:54:26 -04:00
var - > data . u . str . area = malloc ( smp - > data . u . str . data ) ;
if ( ! var - > data . u . str . area ) {
var_accounting_diff ( vars , smp - > sess , smp - > strm ,
- smp - > data . u . str . data ) ;
2015-06-06 13:29:07 -04:00
var - > data . type = SMP_T_BOOL ; /* This type doesn't use additional memory. */
2021-09-07 05:37:37 -04:00
goto unlock ;
2015-06-06 13:29:07 -04:00
}
2018-07-13 04:54:26 -04:00
var - > data . u . str . data = smp - > data . u . str . data ;
memcpy ( var - > data . u . str . area , smp - > data . u . str . area ,
var - > data . u . str . data ) ;
2015-06-06 13:29:07 -04:00
break ;
case SMP_T_METH :
2021-12-16 11:14:36 -05:00
var_clear_buffer ( smp , vars , var , previous_type ) ;
2017-07-24 10:24:39 -04:00
var - > data . u . meth . meth = smp - > data . u . meth . meth ;
if ( smp - > data . u . meth . meth ! = HTTP_METH_OTHER )
break ;
2018-07-13 04:54:26 -04:00
if ( ! var_accounting_add ( vars , smp - > sess , smp - > strm , smp - > data . u . meth . str . data ) ) {
2015-06-06 13:29:07 -04:00
var - > data . type = SMP_T_BOOL ; /* This type doesn't use additional memory. */
2021-09-07 05:37:37 -04:00
goto unlock ;
2015-06-06 13:29:07 -04:00
}
2021-09-07 05:37:37 -04:00
2018-07-13 04:54:26 -04:00
var - > data . u . meth . str . area = malloc ( smp - > data . u . meth . str . data ) ;
if ( ! var - > data . u . meth . str . area ) {
var_accounting_diff ( vars , smp - > sess , smp - > strm ,
- smp - > data . u . meth . str . data ) ;
2015-06-06 13:29:07 -04:00
var - > data . type = SMP_T_BOOL ; /* This type doesn't use additional memory. */
2021-09-07 05:37:37 -04:00
goto unlock ;
2015-06-06 13:29:07 -04:00
}
2018-07-13 04:54:26 -04:00
var - > data . u . meth . str . data = smp - > data . u . meth . str . data ;
var - > data . u . meth . str . size = smp - > data . u . meth . str . data ;
memcpy ( var - > data . u . meth . str . area , smp - > data . u . meth . str . area ,
var - > data . u . meth . str . data ) ;
2015-06-06 13:29:07 -04:00
break ;
}
2021-09-07 05:37:37 -04:00
/* OK, now done */
ret = 1 ;
unlock :
2021-09-08 09:19:57 -04:00
vars_wrunlock ( vars ) ;
2017-07-24 10:30:34 -04:00
return ret ;
2015-06-06 13:29:07 -04:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* Deletes a variable matching name hash <name_hash> and scope <scope> for the
* session and stream found in < smp > . Note that stream may be null for
* SCOPE_SESS . Returns 0 if the scope was not found otherwise 1.
2021-09-07 05:44:41 -04:00
*/
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
static int var_unset ( uint64_t name_hash , enum vars_scope scope , struct sample * smp )
2016-11-09 10:54:56 -05:00
{
struct vars * vars ;
struct var * var ;
unsigned int size = 0 ;
2019-06-04 10:27:36 -04:00
vars = get_vars ( smp - > sess , smp - > strm , scope ) ;
if ( ! vars | | vars - > scope ! = scope )
2016-11-09 10:54:56 -05:00
return 0 ;
/* Look for existing variable name. */
2021-09-08 09:19:57 -04:00
vars_wrlock ( vars ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
var = var_get ( vars , name_hash ) ;
2016-11-09 10:54:56 -05:00
if ( var ) {
2021-09-08 09:03:58 -04:00
size = var_clear ( var , 0 ) ;
2016-11-09 10:54:56 -05:00
var_accounting_diff ( vars , smp - > sess , smp - > strm , - size ) ;
}
2021-09-08 09:19:57 -04:00
vars_wrunlock ( vars ) ;
2016-11-09 10:54:56 -05:00
return 1 ;
}
2021-12-16 11:14:37 -05:00
/*
* Convert a string set - var condition into its numerical value .
* The corresponding bit is set in the < cond_bitmap > parameter if the
* < cond > is known .
* Returns 1 in case of success .
*/
static int vars_parse_cond_param ( const struct buffer * cond , uint * cond_bitmap , char * * err )
{
struct var_set_condition * cond_elt = & conditions_array [ 0 ] ;
/* The conditions array is NULL terminated. */
while ( cond_elt - > cond_str ) {
if ( chunk_strcmp ( cond , cond_elt - > cond_str ) = = 0 ) {
* cond_bitmap | = cond_elt - > flag ;
break ;
}
+ + cond_elt ;
}
if ( cond_elt - > cond_str = = NULL & & err )
memprintf ( err , " unknown condition \" %.*s \" " , ( int ) cond - > data , cond - > area ) ;
return cond_elt - > cond_str ! = NULL ;
}
2015-06-06 13:29:07 -04:00
/* Returns 0 if fails, else returns 1. */
static int smp_conv_store ( const struct arg * args , struct sample * smp , void * private )
{
2021-12-16 11:14:37 -05:00
uint conditions = 0 ;
int cond_idx = 1 ;
while ( args [ cond_idx ] . type = = ARGT_STR ) {
if ( vars_parse_cond_param ( & args [ cond_idx + + ] . data . str , & conditions , NULL ) = = 0 )
break ;
}
return var_set ( args [ 0 ] . data . var . name_hash , args [ 0 ] . data . var . scope , smp , conditions ) ;
2015-06-06 13:29:07 -04:00
}
2016-11-09 10:54:56 -05:00
/* Returns 0 if fails, else returns 1. */
static int smp_conv_clear ( const struct arg * args , struct sample * smp , void * private )
{
2021-11-26 12:08:39 -05:00
return var_unset ( args [ 0 ] . data . var . name_hash , args [ 0 ] . data . var . scope , smp ) ;
2016-11-09 10:54:56 -05:00
}
2018-11-15 12:19:50 -05:00
/* This functions check an argument entry and fill it with a variable
2015-06-06 13:29:07 -04:00
* type . The argumen must be a string . If the variable lookup fails ,
2018-11-15 12:19:50 -05:00
* the function returns 0 and fill < err > , otherwise it returns 1.
2015-06-06 13:29:07 -04:00
*/
int vars_check_arg ( struct arg * arg , char * * err )
{
enum vars_scope scope ;
2021-09-08 05:07:32 -04:00
struct sample empty_smp = { } ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
uint64_t hash ;
2015-06-06 13:29:07 -04:00
/* Check arg type. */
if ( arg - > type ! = ARGT_STR ) {
memprintf ( err , " unexpected argument type " ) ;
return 0 ;
}
/* Register new variable name. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( arg - > data . str . area , arg - > data . str . data , & scope , & hash , err ) )
2015-06-06 13:29:07 -04:00
return 0 ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( scope = = SCOPE_PROC & & ! var_set ( hash , scope , & empty_smp , VF_CREATEONLY | VF_PERMANENT ) )
2021-09-08 05:07:32 -04:00
return 0 ;
2019-05-13 04:53:29 -04:00
/* properly destroy the chunk */
chunk_destroy ( & arg - > data . str ) ;
2015-06-06 13:29:07 -04:00
/* Use the global variable name pointer. */
arg - > type = ARGT_VAR ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
arg - > data . var . name_hash = hash ;
2015-06-06 13:29:07 -04:00
arg - > data . var . scope = scope ;
return 1 ;
}
2021-12-16 11:14:34 -05:00
/* This function stores a sample in a variable unless it is of type "proc" and
* not defined yet .
2020-05-19 07:49:40 -04:00
* Returns zero on failure and non - zero otherwise . The variable not being
* defined is treated as a failure .
2016-10-31 06:05:37 -04:00
*/
2020-05-19 07:49:40 -04:00
int vars_set_by_name_ifexist ( const char * name , size_t len , struct sample * smp )
2016-10-31 06:05:37 -04:00
{
enum vars_scope scope ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
uint64_t hash ;
2016-10-31 06:05:37 -04:00
/* Resolve name and scope. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( name , len , & scope , & hash , NULL ) )
2020-05-19 07:49:40 -04:00
return 0 ;
2016-10-31 06:05:37 -04:00
2021-12-16 11:14:34 -05:00
/* Variable creation is allowed for all scopes apart from the PROC one. */
2021-12-16 11:14:37 -05:00
return var_set ( hash , scope , smp , ( scope = = SCOPE_PROC ) ? VF_COND_IFEXISTS : 0 ) ;
2016-10-31 06:05:37 -04:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This function stores a sample in a variable.
2020-05-19 07:49:40 -04:00
* Returns zero on failure and non - zero otherwise .
2015-06-09 06:27:17 -04:00
*/
2020-05-19 07:49:40 -04:00
int vars_set_by_name ( const char * name , size_t len , struct sample * smp )
2015-06-09 06:27:17 -04:00
{
enum vars_scope scope ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
uint64_t hash ;
2015-06-09 06:27:17 -04:00
/* Resolve name and scope. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( name , len , & scope , & hash , NULL ) )
2020-05-19 07:49:40 -04:00
return 0 ;
2015-06-09 06:27:17 -04:00
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return var_set ( hash , scope , smp , 0 ) ;
2015-06-09 06:27:17 -04:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This function unsets a variable if it was already defined.
2020-05-19 07:49:40 -04:00
* Returns zero on failure and non - zero otherwise .
2016-11-09 10:54:56 -05:00
*/
2020-05-19 07:49:40 -04:00
int vars_unset_by_name_ifexist ( const char * name , size_t len , struct sample * smp )
2016-11-09 10:54:56 -05:00
{
enum vars_scope scope ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
uint64_t hash ;
2016-11-09 10:54:56 -05:00
/* Resolve name and scope. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( name , len , & scope , & hash , NULL ) )
2020-05-19 07:49:40 -04:00
return 0 ;
2016-11-09 10:54:56 -05:00
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return var_unset ( hash , scope , smp ) ;
2016-11-09 10:54:56 -05:00
}
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
/* This retrieves variable whose hash matches <name_hash> from variables <vars>,
* and if found and not empty , duplicates the result into sample < smp > .
* smp_dup ( ) is used in order to release the variables lock ASAP ( so a pre -
* allocated chunk is obtained via get_trash_shunk ( ) ) . The variables ' lock is
* used for reads .
2021-09-03 05:40:58 -04:00
*
2021-09-03 05:52:38 -04:00
* The function returns 0 if the variable was not found and no default
* value was provided in < def > , otherwise 1 with the sample filled .
* Default values are always returned as strings .
2021-09-03 05:40:58 -04:00
*/
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
static int var_to_smp ( struct vars * vars , uint64_t name_hash , struct sample * smp , const struct buffer * def )
2021-09-03 05:40:58 -04:00
{
struct var * var ;
/* Get the variable entry. */
2021-09-08 09:19:57 -04:00
vars_rdlock ( vars ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
var = var_get ( vars , name_hash ) ;
2021-09-08 07:58:19 -04:00
if ( ! var | | ! var - > data . type ) {
2021-09-03 05:52:38 -04:00
if ( ! def ) {
2021-09-08 09:19:57 -04:00
vars_rdunlock ( vars ) ;
2021-09-03 05:52:38 -04:00
return 0 ;
}
/* not found but we have a default value */
smp - > data . type = SMP_T_STR ;
smp - > data . u . str = * def ;
2021-09-03 05:40:58 -04:00
}
2021-09-03 05:52:38 -04:00
else
smp - > data = var - > data ;
2021-09-03 05:40:58 -04:00
/* Copy sample. */
smp_dup ( smp ) ;
2021-09-08 09:19:57 -04:00
vars_rdunlock ( vars ) ;
2021-09-03 05:40:58 -04:00
return 1 ;
}
2021-02-22 11:20:01 -05:00
/* This function fills a sample with the variable content.
*
* Keep in mind that a sample content is duplicated by using smp_dup ( )
* and it therefore uses a pre - allocated trash chunk as returned by
* get_trash_chunk ( ) .
*
2021-09-03 05:52:38 -04:00
* If the variable is not valid in this scope , 0 is always returned .
* If the variable is valid but not found , either the default value
* < def > is returned if not NULL , or zero is returned .
*
2021-02-22 11:20:01 -05:00
* Returns 1 if the sample is filled , otherwise it returns 0.
2015-06-09 06:27:17 -04:00
*/
2021-09-03 05:52:38 -04:00
int vars_get_by_name ( const char * name , size_t len , struct sample * smp , const struct buffer * def )
2015-06-09 06:27:17 -04:00
{
struct vars * vars ;
enum vars_scope scope ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
uint64_t hash ;
2015-06-09 06:27:17 -04:00
/* Resolve name and scope. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( name , len , & scope , & hash , NULL ) )
2015-06-09 06:27:17 -04:00
return 0 ;
/* Select "vars" pool according with the scope. */
2019-06-04 10:27:36 -04:00
vars = get_vars ( smp - > sess , smp - > strm , scope ) ;
if ( ! vars | | vars - > scope ! = scope )
2015-06-09 06:27:17 -04:00
return 0 ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return var_to_smp ( vars , hash , smp , def ) ;
2015-07-07 15:20:42 -04:00
}
2021-02-22 11:20:01 -05:00
/* This function fills a sample with the content of the variable described
* by < var_desc > .
*
* Keep in mind that a sample content is duplicated by using smp_dup ( )
* and it therefore uses a pre - allocated trash chunk as returned by
* get_trash_chunk ( ) .
*
2021-09-03 05:52:38 -04:00
* If the variable is not valid in this scope , 0 is always returned .
* If the variable is valid but not found , either the default value
* < def > is returned if not NULL , or zero is returned .
*
2021-02-22 11:20:01 -05:00
* Returns 1 if the sample is filled , otherwise it returns 0.
2015-07-07 15:20:42 -04:00
*/
2021-09-03 05:52:38 -04:00
int vars_get_by_desc ( const struct var_desc * var_desc , struct sample * smp , const struct buffer * def )
2015-07-07 15:20:42 -04:00
{
struct vars * vars ;
/* Select "vars" pool according with the scope. */
2019-06-04 10:27:36 -04:00
vars = get_vars ( smp - > sess , smp - > strm , var_desc - > scope ) ;
2015-07-07 15:20:42 -04:00
2018-11-15 12:19:50 -05:00
/* Check if the scope is available a this point of processing. */
2019-06-04 10:27:36 -04:00
if ( ! vars | | vars - > scope ! = var_desc - > scope )
2015-07-07 15:20:42 -04:00
return 0 ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
return var_to_smp ( vars , var_desc - > name_hash , smp , def ) ;
2015-06-09 06:27:17 -04:00
}
2015-08-06 12:25:56 -04:00
/* Always returns ACT_RET_CONT even if an error occurs. */
static enum act_return action_store ( struct act_rule * rule , struct proxy * px ,
2015-09-27 04:00:49 -04:00
struct session * sess , struct stream * s , int flags )
2015-06-06 13:29:07 -04:00
{
2021-09-02 15:00:38 -04:00
struct buffer * fmtstr = NULL ;
2015-06-06 13:29:07 -04:00
struct sample smp ;
2015-08-06 12:25:56 -04:00
int dir ;
switch ( rule - > from ) {
2021-11-02 11:56:05 -04:00
case ACT_F_TCP_REQ_CON : dir = SMP_OPT_DIR_REQ ; break ;
2016-10-21 10:37:51 -04:00
case ACT_F_TCP_REQ_SES : dir = SMP_OPT_DIR_REQ ; break ;
2015-08-06 12:25:56 -04:00
case ACT_F_TCP_REQ_CNT : dir = SMP_OPT_DIR_REQ ; break ;
case ACT_F_TCP_RES_CNT : dir = SMP_OPT_DIR_RES ; break ;
case ACT_F_HTTP_REQ : dir = SMP_OPT_DIR_REQ ; break ;
case ACT_F_HTTP_RES : dir = SMP_OPT_DIR_RES ; break ;
2020-02-24 11:34:11 -05:00
case ACT_F_TCP_CHK : dir = SMP_OPT_DIR_REQ ; break ;
2021-03-26 06:11:34 -04:00
case ACT_F_CFG_PARSER : dir = SMP_OPT_DIR_REQ ; break ; /* not used anyway */
2021-03-26 10:36:44 -04:00
case ACT_F_CLI_PARSER : dir = SMP_OPT_DIR_REQ ; break ; /* not used anyway */
2015-08-06 12:25:56 -04:00
default :
send_log ( px , LOG_ERR , " Vars: internal error while execute action store. " ) ;
if ( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) )
2017-11-24 10:50:31 -05:00
ha_alert ( " Vars: internal error while execute action store. \n " ) ;
2015-08-06 12:25:56 -04:00
return ACT_RET_CONT ;
}
2015-06-06 13:29:07 -04:00
/* Process the expression. */
memset ( & smp , 0 , sizeof ( smp ) ) ;
2021-09-02 15:00:38 -04:00
if ( ! LIST_ISEMPTY ( & rule - > arg . vars . fmt ) ) {
/* a format-string is used */
fmtstr = alloc_trash_chunk ( ) ;
if ( ! fmtstr ) {
send_log ( px , LOG_ERR , " Vars: memory allocation failure while processing store rule. " ) ;
if ( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) )
ha_alert ( " Vars: memory allocation failure while processing store rule. \n " ) ;
return ACT_RET_CONT ;
}
/* execute the log-format expression */
fmtstr - > data = sess_build_logline ( sess , s , fmtstr - > area , fmtstr - > size , & rule - > arg . vars . fmt ) ;
/* convert it to a sample of type string as it's what the vars
* API consumes , and store it .
*/
smp_set_owner ( & smp , px , sess , s , 0 ) ;
smp . data . type = SMP_T_STR ;
smp . data . u . str = * fmtstr ;
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
var_set ( rule - > arg . vars . name_hash , rule - > arg . vars . scope , & smp , rule - > arg . vars . conditions ) ;
2021-09-02 15:00:38 -04:00
}
else {
/* an expression is used */
if ( ! sample_process ( px , sess , s , dir | SMP_OPT_FINAL ,
rule - > arg . vars . expr , & smp ) )
return ACT_RET_CONT ;
}
2015-06-06 13:29:07 -04:00
/* Store the sample, and ignore errors. */
MEDIUM: vars: Enable optional conditions to set-var converter and actions
This patch adds the possibility to add a set of conditions to a set-var
call, be it a converter or an action (http-request or http-response
action for instance). The conditions must all be true for the given
set-var call for the variable to actually be set. If any of the
conditions is false, the variable is left untouched.
The managed conditions are the following : "ifexists", "ifnotexists",
"ifempty", "ifnotempty", "ifset", "ifnotset", "ifgt", "iflt". It is
possible to combine multiple conditions in a single set-var call since
some of them apply to the variable itself, and some others to the input.
This patch does not change the fact that variables of scope proc are
still created during configuration parsing, regardless of the conditions
that might be added to the set-var calls in which they are mentioned.
For instance, such a line :
http-request set-var(proc.foo,ifexists) int(5)
would not prevent the creation of the variable during init, and when
actually reaching this line during runtime, the proc.foo variable would
already exist. This is specific to the proc scope.
These new conditions mean that a set-var could "fail" for other reasons
than memory allocation failures but without clearing the contents of the
variable.
2021-12-16 11:14:39 -05:00
var_set ( rule - > arg . vars . name_hash , rule - > arg . vars . scope , & smp , rule - > arg . vars . conditions ) ;
2021-09-02 15:00:38 -04:00
free_trash_chunk ( fmtstr ) ;
2015-08-06 02:52:53 -04:00
return ACT_RET_CONT ;
2015-06-06 13:29:07 -04:00
}
2016-11-09 10:54:56 -05:00
/* Always returns ACT_RET_CONT even if an error occurs. */
static enum act_return action_clear ( struct act_rule * rule , struct proxy * px ,
struct session * sess , struct stream * s , int flags )
{
struct sample smp ;
memset ( & smp , 0 , sizeof ( smp ) ) ;
smp_set_owner ( & smp , px , sess , s , SMP_OPT_FINAL ) ;
/* Clear the variable using the sample context, and ignore errors. */
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
var_unset ( rule - > arg . vars . name_hash , rule - > arg . vars . scope , & smp ) ;
2016-11-09 10:54:56 -05:00
return ACT_RET_CONT ;
}
2020-06-14 11:27:36 -04:00
static void release_store_rule ( struct act_rule * rule )
{
2021-09-02 15:00:38 -04:00
struct logformat_node * lf , * lfb ;
2021-09-03 04:58:07 -04:00
list_for_each_entry_safe ( lf , lfb , & rule - > arg . vars . fmt , list ) {
2021-09-02 15:00:38 -04:00
LIST_DELETE ( & lf - > list ) ;
release_sample_expr ( lf - > expr ) ;
free ( lf - > arg ) ;
free ( lf ) ;
}
2020-06-14 11:27:36 -04:00
release_sample_expr ( rule - > arg . vars . expr ) ;
}
2015-06-06 13:29:07 -04:00
/* This two function checks the variable name and replace the
* configuration string name by the global string name . its
* the same string , but the global pointer can be easy to
2021-09-03 05:52:38 -04:00
* compare . They return non - zero on success , zero on failure .
2015-06-06 13:29:07 -04:00
*
* The first function checks a sample - fetch and the second
* checks a converter .
*/
static int smp_check_var ( struct arg * args , char * * err )
{
return vars_check_arg ( & args [ 0 ] , err ) ;
}
static int conv_check_var ( struct arg * args , struct sample_conv * conv ,
const char * file , int line , char * * err_msg )
{
2021-12-16 11:14:37 -05:00
int cond_idx = 1 ;
uint conditions = 0 ;
int retval = vars_check_arg ( & args [ 0 ] , err_msg ) ;
while ( retval & & args [ cond_idx ] . type = = ARGT_STR )
retval = vars_parse_cond_param ( & args [ cond_idx + + ] . data . str , & conditions , err_msg ) ;
return retval ;
2015-06-06 13:29:07 -04:00
}
/* This function is a common parser for using variables. It understands
* the format :
*
2021-12-16 11:14:38 -05:00
* set - var - fmt ( < variable - name > [ , < cond > . . . ] ) < format - string >
* set - var ( < variable - name > [ , < cond > . . . ] ) < expression >
2019-06-04 10:43:29 -04:00
* unset - var ( < variable - name > )
2015-06-06 13:29:07 -04:00
*
2015-08-06 12:25:56 -04:00
* It returns ACT_RET_PRS_ERR if fails and < err > is filled with an error
* message . Otherwise , it returns ACT_RET_PRS_OK and the variable < expr >
2021-03-26 06:38:08 -04:00
* is filled with the pointer to the expression to execute . The proxy is
* only used to retrieve the - > conf entries .
2015-06-06 13:29:07 -04:00
*/
2015-08-06 12:25:56 -04:00
static enum act_parse_ret parse_store ( const char * * args , int * arg , struct proxy * px ,
struct act_rule * rule , char * * err )
2015-06-06 13:29:07 -04:00
{
const char * var_name = args [ * arg - 1 ] ;
int var_len ;
2015-07-28 13:00:28 -04:00
const char * kw_name ;
2021-10-13 11:22:17 -04:00
int flags = 0 , set_var = 0 ; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
2021-09-08 05:07:32 -04:00
struct sample empty_smp = { } ;
2021-12-16 11:14:38 -05:00
struct ist condition = IST_NULL ;
struct ist var = IST_NULL ;
struct ist varname_ist = IST_NULL ;
2016-11-09 10:54:56 -05:00
2021-09-02 15:00:38 -04:00
if ( strncmp ( var_name , " set-var-fmt " , 11 ) = = 0 ) {
var_name + = 11 ;
set_var = 2 ;
}
else if ( strncmp ( var_name , " set-var " , 7 ) = = 0 ) {
2016-11-09 10:54:56 -05:00
var_name + = 7 ;
set_var = 1 ;
}
2021-09-02 12:46:22 -04:00
else if ( strncmp ( var_name , " unset-var " , 9 ) = = 0 ) {
2016-11-09 10:54:56 -05:00
var_name + = 9 ;
set_var = 0 ;
}
2015-06-06 13:29:07 -04:00
if ( * var_name ! = ' ( ' ) {
2021-09-02 15:00:38 -04:00
memprintf ( err , " invalid or incomplete action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)' " ,
2016-11-09 10:54:56 -05:00
args [ * arg - 1 ] ) ;
2015-08-06 12:25:56 -04:00
return ACT_RET_PRS_ERR ;
2015-06-06 13:29:07 -04:00
}
var_name + + ; /* jump the '(' */
var_len = strlen ( var_name ) ;
var_len - - ; /* remove the ')' */
if ( var_name [ var_len ] ! = ' ) ' ) {
2021-09-02 15:00:38 -04:00
memprintf ( err , " incomplete argument after action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)' " ,
2016-11-09 10:54:56 -05:00
args [ * arg - 1 ] ) ;
2015-08-06 12:25:56 -04:00
return ACT_RET_PRS_ERR ;
2015-06-06 13:29:07 -04:00
}
2021-12-16 11:14:38 -05:00
/* Parse the optional conditions. */
var = ist2 ( var_name , var_len ) ;
varname_ist = istsplit ( & var , ' , ' ) ;
var_len = istlen ( varname_ist ) ;
condition = istsplit ( & var , ' , ' ) ;
if ( istlen ( condition ) & & set_var = = 0 ) {
memprintf ( err , " unset-var does not expect parameters after the variable name. Only \" set-var \" and \" set-var-fmt \" manage conditions " ) ;
return ACT_RET_PRS_ERR ;
}
while ( istlen ( condition ) ) {
struct buffer cond = { } ;
chunk_initlen ( & cond , istptr ( condition ) , 0 , istlen ( condition ) ) ;
if ( vars_parse_cond_param ( & cond , & rule - > arg . vars . conditions , err ) = = 0 )
return ACT_RET_PRS_ERR ;
condition = istsplit ( & var , ' , ' ) ;
}
2021-09-02 15:00:38 -04:00
LIST_INIT ( & rule - > arg . vars . fmt ) ;
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
if ( ! vars_hash_name ( var_name , var_len , & rule - > arg . vars . scope , & rule - > arg . vars . name_hash , err ) )
2015-08-06 12:25:56 -04:00
return ACT_RET_PRS_ERR ;
2015-06-06 13:29:07 -04:00
2021-09-08 05:07:32 -04:00
if ( rule - > arg . vars . scope = = SCOPE_PROC & &
MEDIUM: vars: replace the global name index with a hash
The global table of known variables names can only grow and was designed
for static names that are registered at boot. Nowadays it's possible to
set dynamic variable names from Lua or from the CLI, which causes a real
problem that was partially addressed in 2.2 with commit 4e172c93f
("MEDIUM: lua: Add `ifexist` parameter to `set_var`"). Please see github
issue #624 for more context.
This patch simplifies all this by removing the need for a central
registry of known names, and storing 64-bit hashes instead. This is
highly sufficient given the low number of variables in each context.
The hash is calculated using XXH64() which is bijective over the 64-bit
space thus is guaranteed collision-free for 1..8 chars. Above that the
risk remains around 1/2^64 per extra 8 chars so in practice this is
highly sufficient for our usage. A random seed is used at boot to seed
the hash so that it's not attackable from Lua for example.
There's one particular nit though. The "ifexist" hack mentioned above
is now limited to variables of scope "proc" only, and will only match
variables that were already created or declared, but will now verify
the scope as well. This may affect some bogus Lua scripts and SPOE
agents which used to accidentally work because a similarly named
variable used to exist in a different scope. These ones may need to be
fixed to comply with the doc.
Now we can sum up the situation as this one:
- ephemeral variables (scopes sess, txn, req, res) will always be
usable, regardless of any prior declaration. This effectively
addresses the most problematic change from the commit above that
in order to work well could have required some script auditing ;
- process-wide variables (scope proc) that are mentioned in the
configuration, referenced in a "register-var-names" SPOE directive,
or created via "set-var" in the global section or the CLI, are
permanent and will always accept to be set, with or without the
"ifexist" restriction (SPOE uses this internally as well).
- process-wide variables (scope proc) that are only created via a
set-var() tcp/http action, via Lua's set_var() calls, or via an
SPOE with the "force-set-var" directive), will not be permanent
but will always accept to be replaced once they are created, even
if "ifexist" is present
- process-wide variables (scope proc) that do not exist will only
support being created via the set-var() tcp/http action, Lua's
set_var() calls without "ifexist", or an SPOE declared with
"force-set-var".
This means that non-proc variables do not care about "ifexist" nor
prior declaration, and that using "ifexist" should most often be
reliable in Lua and that SPOE should most often work without any
prior declaration. It may be doable to turn "ifexist" to 1 by default
in Lua to further ease the transition. Note: regtests were adjusted.
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-08-31 02:51:02 -04:00
! var_set ( rule - > arg . vars . name_hash , rule - > arg . vars . scope , & empty_smp , VF_CREATEONLY | VF_PERMANENT ) )
2021-09-08 05:07:32 -04:00
return 0 ;
2016-11-09 10:54:56 -05:00
/* There is no fetch method when variable is unset. Just set the right
* action and return . */
if ( ! set_var ) {
rule - > action = ACT_CUSTOM ;
rule - > action_ptr = action_clear ;
2020-06-14 11:27:36 -04:00
rule - > release_ptr = release_store_rule ;
2016-11-09 10:54:56 -05:00
return ACT_RET_PRS_OK ;
}
2015-07-28 13:00:28 -04:00
kw_name = args [ * arg - 1 ] ;
2015-08-06 12:25:56 -04:00
switch ( rule - > from ) {
2021-11-02 11:56:05 -04:00
case ACT_F_TCP_REQ_CON :
flags = SMP_VAL_FE_CON_ACC ;
px - > conf . args . ctx = ARGC_TCO ;
break ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
case ACT_F_TCP_REQ_SES :
flags = SMP_VAL_FE_SES_ACC ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_TSE ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_TCP_REQ_CNT :
2021-10-13 11:22:17 -04:00
if ( px - > cap & PR_CAP_FE )
flags | = SMP_VAL_FE_REQ_CNT ;
if ( px - > cap & PR_CAP_BE )
flags | = SMP_VAL_BE_REQ_CNT ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_TRQ ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_TCP_RES_CNT :
2021-10-13 11:22:17 -04:00
if ( px - > cap & PR_CAP_FE )
flags | = SMP_VAL_FE_RES_CNT ;
if ( px - > cap & PR_CAP_BE )
flags | = SMP_VAL_BE_RES_CNT ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_TRS ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_HTTP_REQ :
2021-10-13 11:22:17 -04:00
if ( px - > cap & PR_CAP_FE )
flags | = SMP_VAL_FE_HRQ_HDR ;
if ( px - > cap & PR_CAP_BE )
flags | = SMP_VAL_BE_HRQ_HDR ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_HRQ ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_HTTP_RES :
2021-10-13 11:22:17 -04:00
if ( px - > cap & PR_CAP_FE )
flags | = SMP_VAL_FE_HRS_HDR ;
if ( px - > cap & PR_CAP_BE )
flags | = SMP_VAL_BE_HRS_HDR ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_HRS ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_TCP_CHK :
flags = SMP_VAL_BE_CHK_RUL ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_TCK ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_CFG_PARSER :
flags = SMP_VAL_CFG_PARSER ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_CFG ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
case ACT_F_CLI_PARSER :
flags = SMP_VAL_CLI_PARSER ;
2021-09-02 13:46:08 -04:00
px - > conf . args . ctx = ARGC_CLI ;
BUG/MINOR: vars: improve accuracy of the rules used to check expression validity
The set-var() expression naturally checks whether expressions are valid
in the context of the rule, but it fails to differentiate frontends from
backends. As such for tcp-content and http-request rules, it will only
accept frontend-compatible sample-fetches, excluding those declared with
SMP_UES_BKEND (a few such as be_id, be_name). For the response it accepts
the backend-compatible expressions only, though it seems that there are
no sample-fetch function that are valid only in the frontend's content,
so that should not cause any problem.
Note that while allowing valid configs to be used, the fix might also
uncover some incorrect configurations where some expressions currently
return nothing (e.g. something depending on frontend declared in a
backend), and which could be rejected, but there does not seem to be
any such keyword. Thus while it should be backported, better not backport
it too far (2.4 and possibly 2.3 only).
2021-09-02 13:03:07 -04:00
break ;
2015-08-06 12:25:56 -04:00
default :
memprintf ( err ,
" internal error, unexpected rule->from=%d, please report this bug! " ,
rule - > from ) ;
2015-08-19 03:04:15 -04:00
return ACT_RET_PRS_ERR ;
2015-08-06 12:25:56 -04:00
}
2021-09-02 13:46:08 -04:00
2021-09-02 15:00:38 -04:00
if ( set_var = = 2 ) { /* set-var-fmt */
if ( ! parse_logformat_string ( args [ * arg ] , px , & rule - > arg . vars . fmt , 0 , flags , err ) )
return ACT_RET_PRS_ERR ;
2021-09-02 13:46:08 -04:00
2021-09-02 15:00:38 -04:00
( * arg ) + + ;
/* for late error reporting */
free ( px - > conf . lfs_file ) ;
px - > conf . lfs_file = strdup ( px - > conf . args . file ) ;
px - > conf . lfs_line = px - > conf . args . line ;
} else {
/* set-var */
rule - > arg . vars . expr = sample_parse_expr ( ( char * * ) args , arg , px - > conf . args . file ,
2021-10-13 09:18:36 -04:00
px - > conf . args . line , err , & px - > conf . args , NULL ) ;
2021-09-02 15:00:38 -04:00
if ( ! rule - > arg . vars . expr )
return ACT_RET_PRS_ERR ;
if ( ! ( rule - > arg . vars . expr - > fetch - > val & flags ) ) {
memprintf ( err ,
" fetch method '%s' extracts information from '%s', none of which is available here " ,
kw_name , sample_src_names ( rule - > arg . vars . expr - > fetch - > use ) ) ;
free ( rule - > arg . vars . expr ) ;
return ACT_RET_PRS_ERR ;
}
2015-08-06 12:25:56 -04:00
}
2015-06-06 13:29:07 -04:00
2015-09-02 11:17:33 -04:00
rule - > action = ACT_CUSTOM ;
2015-08-06 12:25:56 -04:00
rule - > action_ptr = action_store ;
2020-06-14 11:27:36 -04:00
rule - > release_ptr = release_store_rule ;
2015-08-19 03:04:15 -04:00
return ACT_RET_PRS_OK ;
2015-06-06 13:29:07 -04:00
}
2021-03-26 06:38:08 -04:00
/* parses a global "set-var" directive. It will create a temporary rule and
* expression that are parsed , processed , and released on the fly so that we
* respect the real set - var syntax . These directives take the following format :
* set - var < name > < expression >
2021-09-03 03:02:47 -04:00
* set - var - fmt < name > < fmt >
2021-03-26 06:38:08 -04:00
* Note that parse_store ( ) expects " set-var(name) <expression> " so we have to
* temporarily replace the keyword here .
*/
static int vars_parse_global_set_var ( char * * args , int section_type , struct proxy * curpx ,
const struct proxy * defpx , const char * file , int line ,
char * * err )
{
struct proxy px = {
2021-09-03 02:19:43 -04:00
. id = " CFG " ,
2022-01-28 03:22:07 -05:00
. conf . args = { . file = file , . line = line , } ,
2021-03-26 06:38:08 -04:00
} ;
struct act_rule rule = {
. arg . vars . scope = SCOPE_PROC ,
. from = ACT_F_CFG_PARSER ,
2022-01-28 03:22:07 -05:00
. conf = { . file = ( char * ) file , . line = line , } ,
2021-03-26 06:38:08 -04:00
} ;
2021-09-03 03:02:47 -04:00
enum obj_type objt = OBJ_TYPE_NONE ;
struct session * sess = NULL ;
2021-03-26 06:38:08 -04:00
enum act_parse_ret p_ret ;
char * old_arg1 ;
char * tmp_arg1 ;
int arg = 2 ; // variable name
int ret = - 1 ;
2021-09-03 03:02:47 -04:00
int use_fmt = 0 ;
2021-03-26 06:38:08 -04:00
LIST_INIT ( & px . conf . args . list ) ;
2021-09-03 03:02:47 -04:00
use_fmt = strcmp ( args [ 0 ] , " set-var-fmt " ) = = 0 ;
2021-03-26 06:38:08 -04:00
if ( ! * args [ 1 ] | | ! * args [ 2 ] ) {
2021-09-03 03:02:47 -04:00
if ( use_fmt )
memprintf ( err , " '%s' requires a process-wide variable name ('proc.<name>') and a format string. " , args [ 0 ] ) ;
else
memprintf ( err , " '%s' requires a process-wide variable name ('proc.<name>') and a sample expression. " , args [ 0 ] ) ;
2021-03-26 06:38:08 -04:00
goto end ;
}
tmp_arg1 = NULL ;
2021-09-03 03:02:47 -04:00
if ( ! memprintf ( & tmp_arg1 , " set-var%s(%s) " , use_fmt ? " -fmt " : " " , args [ 1 ] ) )
2021-03-26 06:38:08 -04:00
goto end ;
/* parse_store() will always return a message in <err> on error */
old_arg1 = args [ 1 ] ; args [ 1 ] = tmp_arg1 ;
p_ret = parse_store ( ( const char * * ) args , & arg , & px , & rule , err ) ;
free ( args [ 1 ] ) ; args [ 1 ] = old_arg1 ;
if ( p_ret ! = ACT_RET_PRS_OK )
goto end ;
if ( rule . arg . vars . scope ! = SCOPE_PROC ) {
memprintf ( err , " '%s': cannot set variable '%s', only scope 'proc' is permitted in the global section. " , args [ 0 ] , args [ 1 ] ) ;
goto end ;
}
if ( smp_resolve_args ( & px , err ) ! = 0 ) {
release_sample_expr ( rule . arg . vars . expr ) ;
indent_msg ( err , 2 ) ;
goto end ;
}
2021-09-03 03:02:47 -04:00
if ( use_fmt & & ! ( sess = session_new ( & px , NULL , & objt ) ) ) {
release_sample_expr ( rule . arg . vars . expr ) ;
memprintf ( err , " '%s': out of memory when trying to set variable '%s' in the global section. " , args [ 0 ] , args [ 1 ] ) ;
goto end ;
}
action_store ( & rule , & px , sess , NULL , 0 ) ;
2021-03-26 06:38:08 -04:00
release_sample_expr ( rule . arg . vars . expr ) ;
2021-09-03 03:02:47 -04:00
if ( sess )
session_free ( sess ) ;
2021-03-26 06:38:08 -04:00
ret = 0 ;
end :
return ret ;
}
2021-03-26 09:51:31 -04:00
/* parse CLI's "get var <name>" */
static int vars_parse_cli_get_var ( char * * args , char * payload , struct appctx * appctx , void * private )
{
struct vars * vars ;
2021-04-01 11:01:43 -04:00
struct sample smp = { } ;
2021-03-26 09:51:31 -04:00
int i ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) )
return 1 ;
if ( ! * args [ 2 ] )
return cli_err ( appctx , " Missing process-wide variable identifier. \n " ) ;
vars = get_vars ( NULL , NULL , SCOPE_PROC ) ;
if ( ! vars | | vars - > scope ! = SCOPE_PROC )
return 0 ;
2021-09-03 05:52:38 -04:00
if ( ! vars_get_by_name ( args [ 2 ] , strlen ( args [ 2 ] ) , & smp , NULL ) )
2021-03-26 09:51:31 -04:00
return cli_err ( appctx , " Variable not found. \n " ) ;
/* the sample returned by vars_get_by_name() is allocated into a trash
* chunk so we have no constraint to manipulate it .
*/
chunk_printf ( & trash , " %s: type=%s value= " , args [ 2 ] , smp_to_type [ smp . data . type ] ) ;
if ( ! sample_casts [ smp . data . type ] [ SMP_T_STR ] | |
! sample_casts [ smp . data . type ] [ SMP_T_STR ] ( & smp ) ) {
chunk_appendf ( & trash , " (undisplayable) " ) ;
} else {
/* Display the displayable chars*. */
b_putchr ( & trash , ' < ' ) ;
for ( i = 0 ; i < smp . data . u . str . data ; i + + ) {
if ( isprint ( ( unsigned char ) smp . data . u . str . area [ i ] ) )
b_putchr ( & trash , smp . data . u . str . area [ i ] ) ;
else
b_putchr ( & trash , ' . ' ) ;
}
b_putchr ( & trash , ' > ' ) ;
b_putchr ( & trash , 0 ) ;
}
return cli_msg ( appctx , LOG_INFO , trash . area ) ;
}
2021-09-03 03:47:37 -04:00
/* parse CLI's "set var <name>". It accepts:
* - set var < name > < expression >
* - set var < name > expr < expression >
* - set var < name > fmt < format >
*/
2021-03-26 10:19:50 -04:00
static int vars_parse_cli_set_var ( char * * args , char * payload , struct appctx * appctx , void * private )
{
struct proxy px = {
. id = " CLI " ,
2022-01-28 03:22:07 -05:00
. conf . args = { . file = " CLI " , . line = 0 , } ,
2021-03-26 10:19:50 -04:00
} ;
struct act_rule rule = {
. arg . vars . scope = SCOPE_PROC ,
. from = ACT_F_CLI_PARSER ,
2022-01-28 03:22:07 -05:00
. conf = { . file = " CLI " , . line = 0 , } ,
2021-03-26 10:19:50 -04:00
} ;
2021-09-03 03:47:37 -04:00
enum obj_type objt = OBJ_TYPE_NONE ;
struct session * sess = NULL ;
2021-03-26 10:19:50 -04:00
enum act_parse_ret p_ret ;
2021-09-03 03:47:37 -04:00
const char * tmp_args [ 3 ] ;
int tmp_arg ;
char * tmp_act ;
2021-03-26 10:19:50 -04:00
char * err = NULL ;
int nberr ;
2021-09-03 03:47:37 -04:00
int use_fmt = 0 ;
2021-03-26 10:19:50 -04:00
LIST_INIT ( & px . conf . args . list ) ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) )
return 1 ;
2021-09-03 03:47:37 -04:00
if ( ! * args [ 2 ] )
return cli_err ( appctx , " Missing process-wide variable identifier. \n " ) ;
if ( ! * args [ 3 ] )
return cli_err ( appctx , " Missing either 'expr', 'fmt' or expression. \n " ) ;
if ( * args [ 4 ] ) {
/* this is the long format */
if ( strcmp ( args [ 3 ] , " fmt " ) = = 0 )
use_fmt = 1 ;
else if ( strcmp ( args [ 3 ] , " expr " ) ! = 0 ) {
memprintf ( & err , " '%s %s': arg type must be either 'expr' or 'fmt' but got '%s'. " , args [ 0 ] , args [ 1 ] , args [ 3 ] ) ;
goto fail ;
}
}
2021-03-26 10:19:50 -04:00
2021-09-03 03:47:37 -04:00
tmp_act = NULL ;
if ( ! memprintf ( & tmp_act , " set-var%s(%s) " , use_fmt ? " -fmt " : " " , args [ 2 ] ) ) {
2021-03-26 10:19:50 -04:00
memprintf ( & err , " memory allocation error. " ) ;
goto fail ;
}
/* parse_store() will always return a message in <err> on error */
2021-09-03 03:47:37 -04:00
tmp_args [ 0 ] = tmp_act ;
tmp_args [ 1 ] = ( * args [ 4 ] ) ? args [ 4 ] : args [ 3 ] ;
tmp_args [ 2 ] = " " ;
tmp_arg = 1 ; // must point to the first arg after the action
p_ret = parse_store ( tmp_args , & tmp_arg , & px , & rule , & err ) ;
free ( tmp_act ) ;
2021-03-26 10:19:50 -04:00
if ( p_ret ! = ACT_RET_PRS_OK )
goto fail ;
if ( rule . arg . vars . scope ! = SCOPE_PROC ) {
2021-09-03 04:23:26 -04:00
memprintf ( & err , " '%s %s': cannot set variable '%s', only scope 'proc' is permitted here. " , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
2021-03-26 10:19:50 -04:00
goto fail ;
}
err = NULL ;
nberr = smp_resolve_args ( & px , & err ) ;
if ( nberr ) {
release_sample_expr ( rule . arg . vars . expr ) ;
indent_msg ( & err , 2 ) ;
goto fail ;
}
2021-09-03 03:47:37 -04:00
if ( use_fmt & & ! ( sess = session_new ( & px , NULL , & objt ) ) ) {
release_sample_expr ( rule . arg . vars . expr ) ;
memprintf ( & err , " memory allocation error. " ) ;
goto fail ;
}
action_store ( & rule , & px , sess , NULL , 0 ) ;
2021-03-26 10:19:50 -04:00
release_sample_expr ( rule . arg . vars . expr ) ;
2021-09-03 03:47:37 -04:00
if ( sess )
session_free ( sess ) ;
2021-03-26 10:19:50 -04:00
appctx - > st0 = CLI_ST_PROMPT ;
return 0 ;
fail :
return cli_dynerr ( appctx , err ) ;
}
2015-06-06 13:29:07 -04:00
static int vars_max_size ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2015-06-06 13:29:07 -04:00
char * * err , unsigned int * limit )
{
char * error ;
* limit = strtol ( args [ 1 ] , & error , 10 ) ;
if ( * error ! = 0 ) {
memprintf ( err , " %s: '%s' is an invalid size " , args [ 0 ] , args [ 1 ] ) ;
return - 1 ;
}
return 0 ;
}
static int vars_max_size_global ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2015-06-06 13:29:07 -04:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_global_limit ) ;
}
2016-11-09 05:36:17 -05:00
static int vars_max_size_proc ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2016-11-09 05:36:17 -05:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_proc_limit ) ;
}
2015-06-06 13:29:07 -04:00
static int vars_max_size_sess ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2015-06-06 13:29:07 -04:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_sess_limit ) ;
}
static int vars_max_size_txn ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2015-06-06 13:29:07 -04:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_txn_limit ) ;
}
static int vars_max_size_reqres ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2015-06-06 13:29:07 -04:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_reqres_limit ) ;
}
2020-02-21 12:13:44 -05:00
static int vars_max_size_check ( char * * args , int section_type , struct proxy * curpx ,
2021-03-09 03:53:46 -05:00
const struct proxy * defpx , const char * file , int line ,
2020-02-21 12:13:44 -05:00
char * * err )
{
return vars_max_size ( args , section_type , curpx , defpx , file , line , err , & var_check_limit ) ;
}
2021-08-31 02:48:55 -04:00
/* early boot initialization */
static void vars_init ( )
{
var_name_hash_seed = ha_random64 ( ) ;
2022-02-17 10:47:03 -05:00
/* Initialize process vars */
vars_init_head ( & proc_vars , SCOPE_PROC ) ;
2021-08-31 02:48:55 -04:00
}
INITCALL0 ( STG_PREPARE , vars_init ) ;
2015-06-06 13:29:07 -04:00
static struct sample_fetch_kw_list sample_fetch_keywords = { ILH , {
MEDIUM: vars: make the var() sample fetch function really return type ANY
A long-standing issue was reported in issue #1215.
In short, var() was initially internally declared as returning a string
because it was not possible by then to return "any type". As such, users
regularly get trapped thinking that when they're storing an integer there,
then the integer matching method automatically applies. Except that this
is not possible since this is related to the config parser and is decided
at boot time where the variable's type is not known yet.
As such, what is done is that the output being declared as type string,
the string match will automatically apply, and any value will first be
converted to a string. This results in several issues like:
http-request set-var(txn.foo) int(-1)
http-request deny if { var(txn.foo) lt 0 }
not working. This is because the string match on the second line will in
fact compare the string representation of the variable against strings
"lt" and "0", none of which matches.
The doc says that the matching method is mandatory, though that's not
the case in the code due to that default string type being permissive.
There's not even a warning when no explicit match is placed, because
this happens very deep in the expression evaluator and making a special
case just for "var" can reveal very complicated.
The set-var() converter already mandates a matching method, as the
following will be rejected:
... if { int(12),set-var(txn.truc) 12 }
while this one will work:
... if { int(12),set-var(txn.truc) -m int 12 }
As such, this patch this modifies var() to match the doc, returning the
type "any", and mandating the matching method, implying that this bogus
config which does not work:
http-request set-var(txn.foo) int(-1)
http-request deny if { var(txn.foo) lt 0 }
will need to be written like this:
http-request set-var(txn.foo) int(-1)
http-request deny if { var(txn.foo) -m int lt 0 }
This *will* break some configs (and even 3 of our regtests relied on
this), but except those which already match string exclusively, all
other ones are already broken and silently fail (and one of the 3
regtests, the one on FIX, was bogus regarding this).
In order to fix existing configs, one can simply append "-m str"
after a "var()" in an ACL or "if" expression:
http-request deny unless { var(txn.jwt_alg) "ES" }
must become:
http-request deny unless { var(txn.jwt_alg) -m str "ES" }
Most commonly, patterns such as "le", "lt", "ge", "gt", "eq", "ne" in
front of a number indicate that the intent was to match an integer,
and in this case "-m int" would be desired:
tcp-response content reject if ! { var(res.size) gt 3800 }
ought to become:
tcp-response content reject if ! { var(res.size) -m int gt 3800 }
This must not be backported, but if a solution is found to at least
detect this exact condition in the generic expression parser and
emit a warning, this could probably help spot configuration bugs.
Link: https://www.mail-archive.com/haproxy@formilux.org/msg41341.html
Cc: Christopher Faulet <cfaulet@haproxy.com>
Cc: Tim Dsterhus <tim@bastelstu.be>
2021-11-02 12:08:15 -04:00
{ " var " , smp_fetch_var , ARG2 ( 1 , STR , STR ) , smp_check_var , SMP_T_ANY , SMP_USE_CONST } ,
2015-06-06 13:29:07 -04:00
{ /* END */ } ,
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , sample_register_fetches , & sample_fetch_keywords ) ;
2015-06-06 13:29:07 -04:00
static struct sample_conv_kw_list sample_conv_kws = { ILH , {
2021-12-16 11:14:37 -05:00
{ " set-var " , smp_conv_store , ARG5 ( 1 , STR , STR , STR , STR , STR ) , conv_check_var , SMP_T_ANY , SMP_T_ANY } ,
2016-11-09 10:54:56 -05:00
{ " unset-var " , smp_conv_clear , ARG1 ( 1 , STR ) , conv_check_var , SMP_T_ANY , SMP_T_ANY } ,
2015-06-06 13:29:07 -04:00
{ /* END */ } ,
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , sample_register_convs , & sample_conv_kws ) ;
2021-11-02 11:56:05 -04:00
static struct action_kw_list tcp_req_conn_kws = { { } , {
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
{ /* END */ }
} } ;
INITCALL1 ( STG_REGISTER , tcp_req_conn_keywords_register , & tcp_req_conn_kws ) ;
2016-10-21 10:37:51 -04:00
static struct action_kw_list tcp_req_sess_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2016-10-21 10:37:51 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , tcp_req_sess_keywords_register , & tcp_req_sess_kws ) ;
2016-10-21 10:37:51 -04:00
static struct action_kw_list tcp_req_cont_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2015-06-06 13:29:07 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , tcp_req_cont_keywords_register , & tcp_req_cont_kws ) ;
2015-08-19 03:01:53 -04:00
static struct action_kw_list tcp_res_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2015-06-06 13:29:07 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , tcp_res_cont_keywords_register , & tcp_res_kws ) ;
2020-02-21 12:14:59 -05:00
static struct action_kw_list tcp_check_kws = { ILH , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2020-02-21 12:14:59 -05:00
{ /* END */ }
} } ;
INITCALL1 ( STG_REGISTER , tcp_check_keywords_register , & tcp_check_kws ) ;
2015-08-19 03:01:53 -04:00
static struct action_kw_list http_req_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2015-06-06 13:29:07 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , http_req_keywords_register , & http_req_kws ) ;
2015-08-19 03:01:53 -04:00
static struct action_kw_list http_res_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2015-06-06 13:29:07 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , http_res_keywords_register , & http_res_kws ) ;
2020-01-22 03:26:35 -05:00
static struct action_kw_list http_after_res_kws = { { } , {
2021-09-02 15:00:38 -04:00
{ " set-var-fmt " , parse_store , KWF_MATCH_PREFIX } ,
2021-05-06 09:33:09 -04:00
{ " set-var " , parse_store , KWF_MATCH_PREFIX } ,
{ " unset-var " , parse_store , KWF_MATCH_PREFIX } ,
2020-01-22 03:26:35 -05:00
{ /* END */ }
} } ;
INITCALL1 ( STG_REGISTER , http_after_res_keywords_register , & http_after_res_kws ) ;
2015-06-06 13:29:07 -04:00
static struct cfg_kw_list cfg_kws = { { } , {
2021-03-26 06:38:08 -04:00
{ CFG_GLOBAL , " set-var " , vars_parse_global_set_var } ,
2021-09-03 03:02:47 -04:00
{ CFG_GLOBAL , " set-var-fmt " , vars_parse_global_set_var } ,
2015-06-06 13:29:07 -04:00
{ CFG_GLOBAL , " tune.vars.global-max-size " , vars_max_size_global } ,
2016-11-09 05:36:17 -05:00
{ CFG_GLOBAL , " tune.vars.proc-max-size " , vars_max_size_proc } ,
2015-06-06 13:29:07 -04:00
{ CFG_GLOBAL , " tune.vars.sess-max-size " , vars_max_size_sess } ,
{ CFG_GLOBAL , " tune.vars.txn-max-size " , vars_max_size_txn } ,
{ CFG_GLOBAL , " tune.vars.reqres-max-size " , vars_max_size_reqres } ,
2020-02-21 12:13:44 -05:00
{ CFG_GLOBAL , " tune.vars.check-max-size " , vars_max_size_check } ,
2015-06-06 13:29:07 -04:00
{ /* END */ }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , cfg_register_keywords , & cfg_kws ) ;
2021-03-26 09:51:31 -04:00
/* register cli keywords */
static struct cli_kw_list cli_kws = { { } , {
2021-05-07 05:38:37 -04:00
{ { " get " , " var " , NULL } , " get var <name> : retrieve contents of a process-wide variable " , vars_parse_cli_get_var , NULL } ,
2021-09-03 03:47:37 -04:00
{ { " set " , " var " , NULL } , " set var <name> [fmt|expr] {<fmt>|<expr>}: set variable from an expression or a format " , vars_parse_cli_set_var , NULL , NULL , NULL , ACCESS_EXPERIMENTAL } ,
2021-03-26 09:51:31 -04:00
{ { NULL } , NULL , NULL , NULL }
} } ;
INITCALL1 ( STG_REGISTER , cli_register_kw , & cli_kws ) ;